40글자 이상부터 GZip
This commit is contained in:
parent
abe5d9c241
commit
b8723bc952
4 changed files with 70 additions and 42 deletions
11
bun.lock
11
bun.lock
|
|
@ -11,10 +11,12 @@
|
||||||
"@radix-ui/react-label": "^2.1.6",
|
"@radix-ui/react-label": "^2.1.6",
|
||||||
"@radix-ui/react-slot": "^1.2.2",
|
"@radix-ui/react-slot": "^1.2.2",
|
||||||
"@tailwindcss/vite": "^4.1.6",
|
"@tailwindcss/vite": "^4.1.6",
|
||||||
|
"@types/pako": "^2.0.3",
|
||||||
"base65536": "^5.0.0",
|
"base65536": "^5.0.0",
|
||||||
"class-variance-authority": "^0.7.1",
|
"class-variance-authority": "^0.7.1",
|
||||||
"clsx": "^2.1.1",
|
"clsx": "^2.1.1",
|
||||||
"lucide-react": "^0.510.0",
|
"lucide-react": "^0.510.0",
|
||||||
|
"pako": "^2.1.0",
|
||||||
"react": "^19.1.0",
|
"react": "^19.1.0",
|
||||||
"react-dom": "^19.1.0",
|
"react-dom": "^19.1.0",
|
||||||
"tailwind-merge": "^3.3.0",
|
"tailwind-merge": "^3.3.0",
|
||||||
|
|
@ -22,6 +24,7 @@
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@eslint/js": "^9.25.0",
|
"@eslint/js": "^9.25.0",
|
||||||
|
"@types/bun": "^1.2.13",
|
||||||
"@types/node": "^22.15.17",
|
"@types/node": "^22.15.17",
|
||||||
"@types/react": "^19.1.2",
|
"@types/react": "^19.1.2",
|
||||||
"@types/react-dom": "^19.1.2",
|
"@types/react-dom": "^19.1.2",
|
||||||
|
|
@ -340,6 +343,8 @@
|
||||||
|
|
||||||
"@tiptap/react": ["@tiptap/react@2.12.0", "", { "dependencies": { "@tiptap/extension-bubble-menu": "^2.12.0", "@tiptap/extension-floating-menu": "^2.12.0", "@types/use-sync-external-store": "^0.0.6", "fast-deep-equal": "^3", "use-sync-external-store": "^1" }, "peerDependencies": { "@tiptap/core": "^2.7.0", "@tiptap/pm": "^2.7.0", "react": "^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-D+PR+4kJO9h8AB/7XyQ/Anw8tqeS2ecv5QemBOCHi9JlMAjytauUrj6IfFBO9RbsCowlBjW5GnSpFhzpk2Gghg=="],
|
"@tiptap/react": ["@tiptap/react@2.12.0", "", { "dependencies": { "@tiptap/extension-bubble-menu": "^2.12.0", "@tiptap/extension-floating-menu": "^2.12.0", "@types/use-sync-external-store": "^0.0.6", "fast-deep-equal": "^3", "use-sync-external-store": "^1" }, "peerDependencies": { "@tiptap/core": "^2.7.0", "@tiptap/pm": "^2.7.0", "react": "^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-D+PR+4kJO9h8AB/7XyQ/Anw8tqeS2ecv5QemBOCHi9JlMAjytauUrj6IfFBO9RbsCowlBjW5GnSpFhzpk2Gghg=="],
|
||||||
|
|
||||||
|
"@types/bun": ["@types/bun@1.2.13", "", { "dependencies": { "bun-types": "1.2.13" } }, "sha512-u6vXep/i9VBxoJl3GjZsl/BFIsvML8DfVDO0RYLEwtSZSp981kEO1V5NwRcO1CPJ7AmvpbnDCiMKo3JvbDEjAg=="],
|
||||||
|
|
||||||
"@types/debug": ["@types/debug@4.1.12", "", { "dependencies": { "@types/ms": "*" } }, "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ=="],
|
"@types/debug": ["@types/debug@4.1.12", "", { "dependencies": { "@types/ms": "*" } }, "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ=="],
|
||||||
|
|
||||||
"@types/estree": ["@types/estree@1.0.7", "", {}, "sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ=="],
|
"@types/estree": ["@types/estree@1.0.7", "", {}, "sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ=="],
|
||||||
|
|
@ -360,6 +365,8 @@
|
||||||
|
|
||||||
"@types/node": ["@types/node@22.15.17", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-wIX2aSZL5FE+MR0JlvF87BNVrtFWf6AE6rxSE9X7OwnVvoyCQjpzSRJ+M87se/4QCkCiebQAqrJ0y6fwIyi7nw=="],
|
"@types/node": ["@types/node@22.15.17", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-wIX2aSZL5FE+MR0JlvF87BNVrtFWf6AE6rxSE9X7OwnVvoyCQjpzSRJ+M87se/4QCkCiebQAqrJ0y6fwIyi7nw=="],
|
||||||
|
|
||||||
|
"@types/pako": ["@types/pako@2.0.3", "", {}, "sha512-bq0hMV9opAcrmE0Byyo0fY3Ew4tgOevJmQ9grUhpXQhYfyLJ1Kqg3P33JT5fdbT2AjeAjR51zqqVjAL/HMkx7Q=="],
|
||||||
|
|
||||||
"@types/react": ["@types/react@19.1.4", "", { "dependencies": { "csstype": "^3.0.2" } }, "sha512-EB1yiiYdvySuIITtD5lhW4yPyJ31RkJkkDw794LaQYrxCSaQV/47y5o1FMC4zF9ZyjUjzJMZwbovEnT5yHTW6g=="],
|
"@types/react": ["@types/react@19.1.4", "", { "dependencies": { "csstype": "^3.0.2" } }, "sha512-EB1yiiYdvySuIITtD5lhW4yPyJ31RkJkkDw794LaQYrxCSaQV/47y5o1FMC4zF9ZyjUjzJMZwbovEnT5yHTW6g=="],
|
||||||
|
|
||||||
"@types/react-dom": ["@types/react-dom@19.1.5", "", { "peerDependencies": { "@types/react": "^19.0.0" } }, "sha512-CMCjrWucUBZvohgZxkjd6S9h0nZxXjzus6yDfUb+xLxYM7VvjKNH1tQrE9GWLql1XoOP4/Ds3bwFqShHUYraGg=="],
|
"@types/react-dom": ["@types/react-dom@19.1.5", "", { "peerDependencies": { "@types/react": "^19.0.0" } }, "sha512-CMCjrWucUBZvohgZxkjd6S9h0nZxXjzus6yDfUb+xLxYM7VvjKNH1tQrE9GWLql1XoOP4/Ds3bwFqShHUYraGg=="],
|
||||||
|
|
@ -414,6 +421,8 @@
|
||||||
|
|
||||||
"braces": ["braces@3.0.3", "", { "dependencies": { "fill-range": "^7.1.1" } }, "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA=="],
|
"braces": ["braces@3.0.3", "", { "dependencies": { "fill-range": "^7.1.1" } }, "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA=="],
|
||||||
|
|
||||||
|
"bun-types": ["bun-types@1.2.13", "", { "dependencies": { "@types/node": "*" } }, "sha512-rRjA1T6n7wto4gxhAO/ErZEtOXyEZEmnIHQfl0Dt1QQSB4QV0iP6BZ9/YB5fZaHFQ2dwHFrmPaRQ9GGMX01k9Q=="],
|
||||||
|
|
||||||
"bytes": ["bytes@3.1.2", "", {}, "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg=="],
|
"bytes": ["bytes@3.1.2", "", {}, "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg=="],
|
||||||
|
|
||||||
"call-bind-apply-helpers": ["call-bind-apply-helpers@1.0.2", "", { "dependencies": { "es-errors": "^1.3.0", "function-bind": "^1.1.2" } }, "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ=="],
|
"call-bind-apply-helpers": ["call-bind-apply-helpers@1.0.2", "", { "dependencies": { "es-errors": "^1.3.0", "function-bind": "^1.1.2" } }, "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ=="],
|
||||||
|
|
@ -836,6 +845,8 @@
|
||||||
|
|
||||||
"p-locate": ["p-locate@5.0.0", "", { "dependencies": { "p-limit": "^3.0.2" } }, "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw=="],
|
"p-locate": ["p-locate@5.0.0", "", { "dependencies": { "p-limit": "^3.0.2" } }, "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw=="],
|
||||||
|
|
||||||
|
"pako": ["pako@2.1.0", "", {}, "sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug=="],
|
||||||
|
|
||||||
"parent-module": ["parent-module@1.0.1", "", { "dependencies": { "callsites": "^3.0.0" } }, "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g=="],
|
"parent-module": ["parent-module@1.0.1", "", { "dependencies": { "callsites": "^3.0.0" } }, "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g=="],
|
||||||
|
|
||||||
"parse5": ["parse5@7.3.0", "", { "dependencies": { "entities": "^6.0.0" } }, "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw=="],
|
"parse5": ["parse5@7.3.0", "", { "dependencies": { "entities": "^6.0.0" } }, "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw=="],
|
||||||
|
|
|
||||||
|
|
@ -17,10 +17,12 @@
|
||||||
"@radix-ui/react-label": "^2.1.6",
|
"@radix-ui/react-label": "^2.1.6",
|
||||||
"@radix-ui/react-slot": "^1.2.2",
|
"@radix-ui/react-slot": "^1.2.2",
|
||||||
"@tailwindcss/vite": "^4.1.6",
|
"@tailwindcss/vite": "^4.1.6",
|
||||||
|
"@types/pako": "^2.0.3",
|
||||||
"base65536": "^5.0.0",
|
"base65536": "^5.0.0",
|
||||||
"class-variance-authority": "^0.7.1",
|
"class-variance-authority": "^0.7.1",
|
||||||
"clsx": "^2.1.1",
|
"clsx": "^2.1.1",
|
||||||
"lucide-react": "^0.510.0",
|
"lucide-react": "^0.510.0",
|
||||||
|
"pako": "^2.1.0",
|
||||||
"react": "^19.1.0",
|
"react": "^19.1.0",
|
||||||
"react-dom": "^19.1.0",
|
"react-dom": "^19.1.0",
|
||||||
"tailwind-merge": "^3.3.0",
|
"tailwind-merge": "^3.3.0",
|
||||||
|
|
@ -28,6 +30,7 @@
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@eslint/js": "^9.25.0",
|
"@eslint/js": "^9.25.0",
|
||||||
|
"@types/bun": "^1.2.13",
|
||||||
"@types/node": "^22.15.17",
|
"@types/node": "^22.15.17",
|
||||||
"@types/react": "^19.1.2",
|
"@types/react": "^19.1.2",
|
||||||
"@types/react-dom": "^19.1.2",
|
"@types/react-dom": "^19.1.2",
|
||||||
|
|
|
||||||
74
src/App.tsx
74
src/App.tsx
|
|
@ -1,45 +1,46 @@
|
||||||
import { useState, useEffect } from 'react';
|
import { useState, useEffect } from "react";
|
||||||
import { encode, decode } from 'base65536'; // 실제 base65536 라이브러리 사용을 가정합니다.
|
import { encode, decode } from "base65536";
|
||||||
|
import pako from "pako";
|
||||||
|
|
||||||
import "@blocknote/core/fonts/inter.css";
|
import "@blocknote/core/fonts/inter.css";
|
||||||
import { BlockNoteView } from "@blocknote/mantine";
|
import { BlockNoteView } from "@blocknote/mantine";
|
||||||
import "@blocknote/mantine/style.css";
|
import "@blocknote/mantine/style.css";
|
||||||
import { useCreateBlockNote } from "@blocknote/react";
|
import { useCreateBlockNote } from "@blocknote/react";
|
||||||
import CopyLink from './components/CopyLink';
|
import CopyLink from "./components/CopyLink";
|
||||||
|
|
||||||
export default function App() {
|
export default function App() {
|
||||||
const editor = useCreateBlockNote();
|
const editor = useCreateBlockNote();
|
||||||
const [, setContentForURL] = useState<string>('');
|
const [, setContentForURL] = useState<string>("");
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const loadContentFromURL = async () => {
|
const loadContentFromURL = async () => {
|
||||||
if (!editor) {
|
if (!editor) return;
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const urlParams = new URLSearchParams(window.location.search);
|
const urlParams = new URLSearchParams(window.location.search);
|
||||||
const encoded = urlParams.get('c');
|
const encoded = urlParams.get("c");
|
||||||
|
|
||||||
console.log("Encoded from URL:", encoded);
|
|
||||||
|
|
||||||
if (encoded) {
|
if (encoded) {
|
||||||
try {
|
try {
|
||||||
const decodedBytes = decode(encoded);
|
const decodedBytes = decode(encoded);
|
||||||
const decodedString = new TextDecoder().decode(decodedBytes);
|
|
||||||
console.log("Decoded string:", decodedString);
|
|
||||||
|
|
||||||
|
let decompressed: Uint8Array;
|
||||||
|
try {
|
||||||
|
decompressed = pako.ungzip(decodedBytes);
|
||||||
|
} catch {
|
||||||
|
// 압축 안 되어 있음
|
||||||
|
decompressed = decodedBytes;
|
||||||
|
}
|
||||||
|
|
||||||
const blocks = await editor.tryParseMarkdownToBlocks(decodedString);
|
const markdown = new TextDecoder().decode(decompressed);
|
||||||
|
const blocks = await editor.tryParseMarkdownToBlocks(markdown);
|
||||||
editor.replaceBlocks(editor.topLevelBlocks, blocks);
|
editor.replaceBlocks(editor.topLevelBlocks, blocks);
|
||||||
|
setContentForURL(markdown);
|
||||||
setContentForURL(decodedString);
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error("Failed to decode or load content:", e);
|
console.error("Failed to decode or decompress content:", e);
|
||||||
setContentForURL('');
|
setContentForURL("");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
setContentForURL('');
|
setContentForURL("");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -49,31 +50,44 @@ export default function App() {
|
||||||
const handleChange = async () => {
|
const handleChange = async () => {
|
||||||
if (!editor) return;
|
if (!editor) return;
|
||||||
|
|
||||||
const newContentMarkdown = await editor.blocksToMarkdownLossy(editor.topLevelBlocks);
|
const newContentMarkdown = await editor.blocksToMarkdownLossy(
|
||||||
setContentForURL(newContentMarkdown); // URL 업데이트 및 상태 반영
|
editor.topLevelBlocks
|
||||||
|
);
|
||||||
|
setContentForURL(newContentMarkdown);
|
||||||
|
|
||||||
const encoder = new TextEncoder();
|
const encoder = new TextEncoder();
|
||||||
const encodedContent = encoder.encode(newContentMarkdown);
|
let encodedString: string;
|
||||||
const encodedString = encode(encodedContent); // base65536의 encode 함수
|
|
||||||
|
|
||||||
window.history.replaceState({}, '', `?c=${encodedString}`);
|
if (newContentMarkdown.length >= 40) {
|
||||||
|
const encodedBytes = encoder.encode(newContentMarkdown);
|
||||||
|
const compressed = pako.gzip(encodedBytes);
|
||||||
|
encodedString = encode(compressed); // 압축 + 인코딩
|
||||||
|
} else {
|
||||||
|
const plainBytes = encoder.encode(newContentMarkdown);
|
||||||
|
encodedString = encode(plainBytes); // 압축 없이 인코딩
|
||||||
|
}
|
||||||
|
|
||||||
|
const newUrl = `${window.location.pathname}?c=${encodedString}`;
|
||||||
|
window.history.replaceState({}, "", newUrl);
|
||||||
window.dispatchEvent(new Event("url-changed"));
|
window.dispatchEvent(new Event("url-changed"));
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<main className='flex flex-col w-full h-screen'>
|
<main className="flex flex-col w-full h-screen">
|
||||||
<div className='flex flex-row items-center justify-between w-full p-4 bg-background'>
|
<div className="flex flex-row items-center justify-between w-full p-4 bg-background">
|
||||||
<div>
|
<div>
|
||||||
<h1 className='text-2xl font-bold'>65536.md</h1>
|
<h1 className="text-2xl font-bold">65536.md</h1>
|
||||||
<p className='text-sm text-gray-500'>A simple markdown editor</p>
|
<p className="text-sm text-muted-foreground">
|
||||||
|
A simple markdown editor
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<CopyLink />
|
<CopyLink />
|
||||||
</div>
|
</div>
|
||||||
<div id='editor-container' className='w-full p-4 h-full max-h-screen'>
|
<div id="editor-container" className="w-full p-4 h-full max-h-screen">
|
||||||
<BlockNoteView
|
<BlockNoteView
|
||||||
editor={editor}
|
editor={editor}
|
||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
className='w-full h-full'
|
className="w-full h-full"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
|
|
|
||||||
|
|
@ -115,7 +115,7 @@
|
||||||
@apply border-border outline-ring/50;
|
@apply border-border outline-ring/50;
|
||||||
}
|
}
|
||||||
body {
|
body {
|
||||||
@apply bg-[#1f1f1f] text-foreground h-screen;
|
@apply dark:bg-[#1f1f1f] text-foreground h-screen;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue