Add markdown preview feature to App.tsx

This commit is contained in:
2026-01-31 14:19:42 +01:00
parent 700b9d72bb
commit bd6ffe6d41

View File

@@ -7,6 +7,8 @@ import { listen } from "@tauri-apps/api/event";
import { invoke } from "@tauri-apps/api/core"; import { invoke } from "@tauri-apps/api/core";
import historyIcon from "./assets/history.png"; import historyIcon from "./assets/history.png";
import historyIconBright from "./assets/history_b.png"; import historyIconBright from "./assets/history_b.png";
import { markdownToHTML } from "./markdown/markdown";
import "./markdown/markdown-render.css";
import { import {
createText, createText,
deleteText, deleteText,
@@ -77,6 +79,7 @@ export default function App() {
const [selectedHistoryId, setSelectedHistoryId] = useState<string | null>(null); const [selectedHistoryId, setSelectedHistoryId] = useState<string | null>(null);
const [confirmState, setConfirmState] = useState<ConfirmState | null>(null); const [confirmState, setConfirmState] = useState<ConfirmState | null>(null);
const [settingsOpen, setSettingsOpen] = useState(false); const [settingsOpen, setSettingsOpen] = useState(false);
const [markdownPreview, setMarkdownPreview] = useState(false);
const [theme, setTheme] = useState<"default" | "light">(() => { const [theme, setTheme] = useState<"default" | "light">(() => {
const storedTheme = localStorage.getItem("textdb.theme"); const storedTheme = localStorage.getItem("textdb.theme");
return storedTheme === "light" ? "light" : "default"; return storedTheme === "light" ? "light" : "default";
@@ -137,6 +140,7 @@ export default function App() {
const isViewingHistory = viewingVersion !== null; const isViewingHistory = viewingVersion !== null;
const isDirty = !isViewingHistory && body !== lastPersistedBody; const isDirty = !isViewingHistory && body !== lastPersistedBody;
const hasText = body.trim().length > 0; const hasText = body.trim().length > 0;
const showLineNumbersActive = showLineNumbers && !markdownPreview;
const statusKey = useMemo(() => { const statusKey = useMemo(() => {
if (isViewingHistory) return "history"; if (isViewingHistory) return "history";
@@ -166,14 +170,14 @@ export default function App() {
const lineNumbers = useMemo(() => lines.map((_, index) => index + 1), [lines]); const lineNumbers = useMemo(() => lines.map((_, index) => index + 1), [lines]);
const handleTextareaScroll = useCallback((event: React.UIEvent<HTMLTextAreaElement>) => { const handleTextareaScroll = useCallback((event: React.UIEvent<HTMLTextAreaElement>) => {
if (!showLineNumbers) return; if (!showLineNumbersActive) return;
if (lineNumbersRef.current) { if (lineNumbersRef.current) {
lineNumbersRef.current.scrollTop = event.currentTarget.scrollTop; lineNumbersRef.current.scrollTop = event.currentTarget.scrollTop;
} }
}, [showLineNumbers]); }, [showLineNumbersActive]);
useEffect(() => { useEffect(() => {
if (!showLineNumbers) return; if (!showLineNumbersActive) return;
const textarea = textareaRef.current; const textarea = textareaRef.current;
if (!textarea || typeof ResizeObserver === "undefined") return; if (!textarea || typeof ResizeObserver === "undefined") return;
const observer = new ResizeObserver(() => { const observer = new ResizeObserver(() => {
@@ -181,10 +185,10 @@ export default function App() {
}); });
observer.observe(textarea); observer.observe(textarea);
return () => observer.disconnect(); return () => observer.disconnect();
}, [showLineNumbers]); }, [showLineNumbersActive]);
useEffect(() => { useEffect(() => {
if (!showLineNumbers) return; if (!showLineNumbersActive) return;
let raf = 0; let raf = 0;
const handleResize = () => { const handleResize = () => {
if (raf) cancelAnimationFrame(raf); if (raf) cancelAnimationFrame(raf);
@@ -197,10 +201,10 @@ export default function App() {
window.removeEventListener("resize", handleResize); window.removeEventListener("resize", handleResize);
if (raf) cancelAnimationFrame(raf); if (raf) cancelAnimationFrame(raf);
}; };
}, [showLineNumbers]); }, [showLineNumbersActive]);
useLayoutEffect(() => { useLayoutEffect(() => {
if (!showLineNumbers) return; if (!showLineNumbersActive) return;
const textarea = textareaRef.current; const textarea = textareaRef.current;
const measure = measureRef.current; const measure = measureRef.current;
if (!textarea || !measure) return; if (!textarea || !measure) return;
@@ -212,13 +216,13 @@ export default function App() {
if (lineNumbersRef.current) { if (lineNumbersRef.current) {
lineNumbersRef.current.scrollTop = textarea.scrollTop; lineNumbersRef.current.scrollTop = textarea.scrollTop;
} }
}, [lines, showLineNumbers, textSize, measureTick, sidebarCollapsed, historyOpen]); }, [lines, showLineNumbersActive, textSize, measureTick, sidebarCollapsed, historyOpen]);
useEffect(() => { useEffect(() => {
if (showLineNumbers && textareaRef.current && lineNumbersRef.current) { if (showLineNumbersActive && textareaRef.current && lineNumbersRef.current) {
lineNumbersRef.current.scrollTop = textareaRef.current.scrollTop; lineNumbersRef.current.scrollTop = textareaRef.current.scrollTop;
} }
}, [showLineNumbers, body]); }, [showLineNumbersActive, body]);
const refreshTexts = useCallback(async () => { const refreshTexts = useCallback(async () => {