From b7a680d7bbad4f8ea2b804ec7eb2ee9bef97ed55 Mon Sep 17 00:00:00 2001 From: tototomate123 Date: Sat, 7 Feb 2026 22:39:06 +0100 Subject: [PATCH] Improve text input performance with apply/cancel --- CHANGELOG.md | 1 + src/App.tsx | 35 ++++++++++++++++++++-- src/components/TextInputOverlay.tsx | 46 +++++++++++++++++++++++++++-- src/lib/reading.ts | 2 +- src/styles.ts | 42 ++++++++++++++++++++++++++ 5 files changed, 119 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2b6693a..1df37af 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ - Split the big app file into smaller TypeScript modules and add `tsc` typechecking - Add PDF upload support (extract all text client-side for speed reading) +- Improve text editor performance by applying changes explicitly instead of reprocessing on each keystroke ### 2026-02-02: 1.0.5 diff --git a/src/App.tsx b/src/App.tsx index 70cbf3c..099976d 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -42,6 +42,7 @@ export default function App() { const positionsRef = useRef(savedSettings?.positions || {}); const [text, setText] = useState(() => savedSettings?.text || DEFAULT_TEXT); + const [draftText, setDraftText] = useState(() => savedSettings?.text || DEFAULT_TEXT); const [words, setWords] = useState(() => splitWords(savedSettings?.text || DEFAULT_TEXT), ); @@ -100,6 +101,24 @@ export default function App() { const prevTextRef = useRef(text); const fileInputRef = useRef(null); + const toggleTextInput = useCallback(() => { + setShowTextInput((prev) => { + const next = !prev; + if (next) setDraftText(text); + return next; + }); + }, [text]); + + const applyDraftText = useCallback(() => { + setText(draftText); + setShowTextInput(false); + }, [draftText]); + + const cancelDraftText = useCallback(() => { + setDraftText(text); + setShowTextInput(false); + }, [text]); + const togglePlay = useCallback(() => { if (currentIndex >= words.length - 1) { setCurrentIndex(0); @@ -281,6 +300,7 @@ export default function App() { case "Escape": setShowInfo(false); setShowShortcuts(false); + setDraftText(text); setShowTextInput(false); setShowSettings(false); break; @@ -389,7 +409,7 @@ export default function App() {
setShowTextInput((prev) => !prev)} + onToggleTextInput={toggleTextInput} fileInputRef={fileInputRef} onFileUpload={handleFileUpload} fileAccept=".epub,.pdf,.txt" @@ -406,8 +426,17 @@ export default function App() { {showTextInput && ( setText(e.target.value)} + text={draftText} + onChangeText={(e) => setDraftText(e.target.value)} + onKeyDown={(e) => { + if (e.key === "Enter" && (e.ctrlKey || e.metaKey)) { + e.preventDefault(); + applyDraftText(); + } + }} + isDirty={draftText !== text} + onApply={applyDraftText} + onCancel={cancelDraftText} /> )} diff --git a/src/components/TextInputOverlay.tsx b/src/components/TextInputOverlay.tsx index 2686829..6e8480f 100644 --- a/src/components/TextInputOverlay.tsx +++ b/src/components/TextInputOverlay.tsx @@ -1,25 +1,65 @@ -import type { ChangeEventHandler } from "react"; +import type { ChangeEventHandler, KeyboardEventHandler } from "react"; import { styles } from "../styles"; type Props = { text: string; onChangeText: ChangeEventHandler; + onKeyDown?: KeyboardEventHandler; + isDirty: boolean; + onApply: () => void; + onCancel: () => void; }; -export function TextInputOverlay({ text, onChangeText }: Props) { +export function TextInputOverlay({ + text, + onChangeText, + onKeyDown, + isDirty, + onApply, + onCancel, +}: Props) { return (