diff --git a/src/pages/Discover/DiscoverPage.tsx b/src/pages/Discover/DiscoverPage.tsx index a73b475..a34543c 100644 --- a/src/pages/Discover/DiscoverPage.tsx +++ b/src/pages/Discover/DiscoverPage.tsx @@ -159,34 +159,42 @@ function DiscoverPage() { : `${focusedCategory()?.name ?? "Discover"} · ${podcasts().length}`; // ── parent pane: previous-depth list (muted/blank at depth 0) ───────────── - // Stable gate (not a ternary root swap) so the parent list - // mounts/unmounts cleanly on depth change. + // Sibling blocks per depth (the known-good opentui disposal + // pattern, mirrors Settings): a STABLE fragment root whose inner + // children toggle on depth change, so the old subtree is disposed instead + // of left orphaned next to the new one (single and + // ternary root swaps both leak the previous root). const parentContent = () => ( - = 1} fallback={}> - - {(cat, index) => { - const lf = () => nav.depthFocus(0); - const ref = useScrollIntoView(() => index() === lf()); - return ( - - - {index() === nav.depthFocus(0) ? "❯" : " "} - - - {cat.name} - - - ); - }} - - + <> + + + + = 1}> + + {(cat, index) => { + const lf = () => nav.depthFocus(0); + const ref = useScrollIntoView(() => index() === lf()); + return ( + + + {index() === nav.depthFocus(0) ? "❯" : " "} + + + {cat.name} + + + ); + }} + + + ); // ── current pane ─────────────────────────────────────────────────────────── diff --git a/src/pages/Search/SearchPage.tsx b/src/pages/Search/SearchPage.tsx index aa51ff2..0203d68 100644 --- a/src/pages/Search/SearchPage.tsx +++ b/src/pages/Search/SearchPage.tsx @@ -74,12 +74,16 @@ function SearchPage() { // j/k (yielding to a non-existent input) and only the scrollbox's native // scroll responds. // - // The effect only re-runs on a depth transition, so Escape (defocus) and - // `s` (refocus) at the same depth are not clobbered. + // The depth STACK signal is also written by focus moves (setDepthFocus), + // so gate the sync on the depth VALUE via a memo: the effect must re-run + // only on an actual depth transition. Without the memo every j/k at the + // query depth re-focuses the input (undoing Escape), which keeps the + // recents list unreachable by keyboard. onMount(() => nav.setInputFocused(depth() === 0)); onCleanup(() => nav.setInputFocused(false)); + const isQueryDepth = createMemo(() => depth() === 0); createEffect(() => { - nav.setInputFocused(depth() === 0); + nav.setInputFocused(isQueryDepth()); }); // ── results (depth 1) ───────────────────────────────────────────────────── @@ -213,15 +217,25 @@ function SearchPage() { : theme.text; // ── parent pane: previous-depth content (tab list at depth 0) ────────────── + // Sibling blocks per depth (the known-good opentui disposal + // pattern, mirrors Settings): a STABLE fragment root whose inner + // children toggle on depth change, so the old subtree is disposed instead + // of left orphaned next to the new one (single and + // ternary root swaps both leak the previous root). const parentContent = () => ( - = 1} fallback={}> - - Query - {submittedQuery() || "(empty)"} - - h: back to query - - + <> + + + + = 1}> + + Query + {submittedQuery() || "(empty)"} + + h: back to query + + + ); // ── current pane ──────────────────────────────────────────────────────────── @@ -239,6 +253,9 @@ function SearchPage() { placeholder="Enter podcast name..." focused={inputActive()} width={28} + textColor={theme.text} + focusedTextColor={theme.accent} + cursorColor={theme.accent} /> @@ -274,6 +291,10 @@ function SearchPage() { onMouseDown={() => { nav.setActivePane(DEPTH_CENTER_PANE); nav.setDepthFocus(index(), 0); + // A recent is an action, not an item: clicking + // it re-runs that search (focus-only would be + // invisible — the input still owns the keys). + selectRecent(query); }} > diff --git a/src/pages/Settings/ExportDialog.tsx b/src/pages/Settings/ExportDialog.tsx index 6d28cf4..eb64bc3 100644 --- a/src/pages/Settings/ExportDialog.tsx +++ b/src/pages/Settings/ExportDialog.tsx @@ -32,6 +32,9 @@ export function ExportDialog() { value={filename[0]()} onInput={filename[1]} style={{ width: 30 }} + textColor={theme.text} + focusedTextColor={theme.accent} + cursorColor={theme.accent} /> diff --git a/src/pages/Settings/FilePicker.tsx b/src/pages/Settings/FilePicker.tsx index b126a5c..4e2f9b4 100644 --- a/src/pages/Settings/FilePicker.tsx +++ b/src/pages/Settings/FilePicker.tsx @@ -21,6 +21,9 @@ export function FilePicker(props: FilePickerProps) { onInput={props.onChange} placeholder="/path/to/sync-file.json" style={{ width: 40 }} + textColor={theme.text} + focusedTextColor={theme.accent} + cursorColor={theme.accent} /> Format: {format} diff --git a/src/pages/Settings/SourceManager.tsx b/src/pages/Settings/SourceManager.tsx index 1cc91be..cc57010 100644 --- a/src/pages/Settings/SourceManager.tsx +++ b/src/pages/Settings/SourceManager.tsx @@ -103,6 +103,8 @@ function AddSourceForm() { onInput={setName} placeholder="My Custom Feed" width={25} + textColor={theme.text} + focusedTextColor={theme.accent} /> @@ -116,6 +118,8 @@ function AddSourceForm() { }} placeholder="https://example.com/feed.rss" width={35} + textColor={theme.text} + focusedTextColor={theme.accent} />