40글자 이상부터 GZip

This commit is contained in:
imnyang 2025-05-14 00:55:04 +09:00
commit b8723bc952
4 changed files with 70 additions and 42 deletions

View file

@ -11,10 +11,12 @@
"@radix-ui/react-label": "^2.1.6",
"@radix-ui/react-slot": "^1.2.2",
"@tailwindcss/vite": "^4.1.6",
"@types/pako": "^2.0.3",
"base65536": "^5.0.0",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"lucide-react": "^0.510.0",
"pako": "^2.1.0",
"react": "^19.1.0",
"react-dom": "^19.1.0",
"tailwind-merge": "^3.3.0",
@ -22,6 +24,7 @@
},
"devDependencies": {
"@eslint/js": "^9.25.0",
"@types/bun": "^1.2.13",
"@types/node": "^22.15.17",
"@types/react": "^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=="],
"@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/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/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-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=="],
"bun-types": ["bun-types@1.2.13", "", { "dependencies": { "@types/node": "*" } }, "sha512-rRjA1T6n7wto4gxhAO/ErZEtOXyEZEmnIHQfl0Dt1QQSB4QV0iP6BZ9/YB5fZaHFQ2dwHFrmPaRQ9GGMX01k9Q=="],
"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=="],
@ -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=="],
"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=="],
"parse5": ["parse5@7.3.0", "", { "dependencies": { "entities": "^6.0.0" } }, "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw=="],

View file

@ -17,10 +17,12 @@
"@radix-ui/react-label": "^2.1.6",
"@radix-ui/react-slot": "^1.2.2",
"@tailwindcss/vite": "^4.1.6",
"@types/pako": "^2.0.3",
"base65536": "^5.0.0",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"lucide-react": "^0.510.0",
"pako": "^2.1.0",
"react": "^19.1.0",
"react-dom": "^19.1.0",
"tailwind-merge": "^3.3.0",
@ -28,6 +30,7 @@
},
"devDependencies": {
"@eslint/js": "^9.25.0",
"@types/bun": "^1.2.13",
"@types/node": "^22.15.17",
"@types/react": "^19.1.2",
"@types/react-dom": "^19.1.2",

View file

@ -1,45 +1,46 @@
import { useState, useEffect } from 'react';
import { encode, decode } from 'base65536'; // 실제 base65536 라이브러리 사용을 가정합니다.
import { useState, useEffect } from "react";
import { encode, decode } from "base65536";
import pako from "pako";
import "@blocknote/core/fonts/inter.css";
import { BlockNoteView } from "@blocknote/mantine";
import "@blocknote/mantine/style.css";
import { useCreateBlockNote } from "@blocknote/react";
import CopyLink from './components/CopyLink';
import CopyLink from "./components/CopyLink";
export default function App() {
const editor = useCreateBlockNote();
const [, setContentForURL] = useState<string>('');
const [, setContentForURL] = useState<string>("");
useEffect(() => {
const loadContentFromURL = async () => {
if (!editor) {
return;
}
if (!editor) return;
const urlParams = new URLSearchParams(window.location.search);
const encoded = urlParams.get('c');
console.log("Encoded from URL:", encoded);
const encoded = urlParams.get("c");
if (encoded) {
try {
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);
setContentForURL(decodedString);
setContentForURL(markdown);
} catch (e) {
console.error("Failed to decode or load content:", e);
setContentForURL('');
console.error("Failed to decode or decompress content:", e);
setContentForURL("");
}
} else {
setContentForURL('');
setContentForURL("");
}
};
@ -49,33 +50,46 @@ export default function App() {
const handleChange = async () => {
if (!editor) return;
const newContentMarkdown = await editor.blocksToMarkdownLossy(editor.topLevelBlocks);
setContentForURL(newContentMarkdown); // URL 업데이트 및 상태 반영
const newContentMarkdown = await editor.blocksToMarkdownLossy(
editor.topLevelBlocks
);
setContentForURL(newContentMarkdown);
const encoder = new TextEncoder();
const encodedContent = encoder.encode(newContentMarkdown);
const encodedString = encode(encodedContent); // base65536의 encode 함수
let encodedString: string;
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"));
};
return (
<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>
<h1 className='text-2xl font-bold'>65536.md</h1>
<p className='text-sm text-gray-500'>A simple markdown editor</p>
</div>
<CopyLink />
<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>
<h1 className="text-2xl font-bold">65536.md</h1>
<p className="text-sm text-muted-foreground">
A simple markdown editor
</p>
</div>
<div id='editor-container' className='w-full p-4 h-full max-h-screen'>
<BlockNoteView
editor={editor}
onChange={handleChange}
className='w-full h-full'
/>
</div>
</main>
<CopyLink />
</div>
<div id="editor-container" className="w-full p-4 h-full max-h-screen">
<BlockNoteView
editor={editor}
onChange={handleChange}
className="w-full h-full"
/>
</div>
</main>
);
}

View file

@ -115,7 +115,7 @@
@apply border-border outline-ring/50;
}
body {
@apply bg-[#1f1f1f] text-foreground h-screen;
@apply dark:bg-[#1f1f1f] text-foreground h-screen;
}
}