Add markdown preview feature to App.tsx
This commit is contained in:
24
src/App.tsx
24
src/App.tsx
@@ -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 () => {
|
||||||
|
|||||||
Reference in New Issue
Block a user