From dad0ac86d07ef27e0fa242bf3db05a54e7a27e07 Mon Sep 17 00:00:00 2001 From: PJ Date: Mon, 10 Aug 2026 18:36:39 +0530 Subject: [PATCH] feat(chapters): focus the title field when adding a chapter --- src/components/EditorView.tsx | 18 ++++++++++++++++++ src/components/Sidebar.tsx | 8 +++++++- src/store/useBook.ts | 21 +++++++++++++++------ 3 files changed, 40 insertions(+), 7 deletions(-) diff --git a/src/components/EditorView.tsx b/src/components/EditorView.tsx index b5f5af0..7c1e05c 100644 --- a/src/components/EditorView.tsx +++ b/src/components/EditorView.tsx @@ -37,6 +37,8 @@ export function EditorView() { const setChapterContent = useBook((s) => s.setChapterContent); const setChapterTitle = useBook((s) => s.setChapterTitle); const setChapterNoTitle = useBook((s) => s.setChapterNoTitle); + const pendingTitleFocus = useBook((s) => s.pendingTitleFocus); + const clearTitleFocus = useBook((s) => s.clearTitleFocus); const markSaved = useBook((s) => s.markSaved); const theme = useTheme((s) => s.theme); const toggleTheme = useTheme((s) => s.toggle); @@ -88,6 +90,7 @@ export function EditorView() { }, []); const focusEditorSoon = useCallback(() => { + if (useBook.getState().pendingTitleFocus) return; requestAnimationFrame(() => editorRef.current?.commands.focus(undefined, { scrollIntoView: false })); }, []); @@ -420,6 +423,8 @@ export function EditorView() { chapter.noTitle ? "No title" : kind === "body" ? "Chapter title" : kind === "part" ? "Part title (optional)" : "Page title" } disabled={!!chapter.noTitle} + focusRequest={pendingTitleFocus === chapter.id} + onFocused={clearTitleFocus} onChange={(value) => setChapterTitle(chapter.id, value)} /> {(chapter.noTitle || !chapter.title.trim()) && ( @@ -495,11 +500,15 @@ function ChapterTitleInput({ value, placeholder, disabled, + focusRequest, + onFocused, onChange, }: { value: string; placeholder: string; disabled: boolean; + focusRequest: boolean; + onFocused: () => void; onChange: (value: string) => void; }) { const ref = useRef(null); @@ -511,6 +520,15 @@ function ChapterTitleInput({ el.style.height = `${el.scrollHeight}px`; }, [value, disabled]); + useEffect(() => { + if (!focusRequest || disabled) return; + const el = ref.current; + if (!el) return; + el.focus(); + el.select(); + onFocused(); + }, [focusRequest, disabled, onFocused]); + return (