65536.md/src/App.tsx
2025-05-14 10:18:30 +09:00

99 lines
3 KiB
TypeScript

import { useState, useEffect, useCallback } from "react";
import { encode, decode } from "base65536";
import debounce from "lodash.debounce";
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";
export default function App() {
const editor = useCreateBlockNote();
const [, setContentForURL] = useState<string>("");
useEffect(() => {
const loadContentFromURL = async () => {
if (!editor) return;
const urlParams = new URLSearchParams(window.location.search);
const encoded = urlParams.get("c");
if (encoded) {
try {
const decodedBytes = decode(encoded);
let decompressed: Uint8Array;
try {
decompressed = pako.ungzip(decodedBytes);
} catch {
// 압축 안 되어 있음
decompressed = decodedBytes;
}
const markdown = new TextDecoder().decode(decompressed);
const blocks = await editor.tryParseMarkdownToBlocks(markdown);
editor.replaceBlocks(editor.topLevelBlocks, blocks);
setContentForURL(markdown);
} catch (e) {
console.error("Failed to decode or decompress content:", e);
setContentForURL("");
}
} else {
setContentForURL("");
}
};
loadContentFromURL();
}, [editor]);
// useCallback을 사용해 debounce 유지
const debouncedHandleChange = useCallback(
debounce(async () => {
if (!editor) return;
const newContentMarkdown = await editor.blocksToMarkdownLossy(
editor.topLevelBlocks
);
setContentForURL(newContentMarkdown);
const encoder = new TextEncoder();
let encodedString: string;
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);
}, 250), [editor] // 500ms 대기
);
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-muted-foreground">
A simple markdown editor
</p>
</div>
<CopyLink />
</div>
<div id="editor-container" className="w-full p-4 h-full max-h-screen">
<BlockNoteView
editor={editor}
onChange={debouncedHandleChange}
className="w-full h-full"
/>
</div>
</main>
);
}