diff --git a/src/components/TabPanel.tsx b/src/components/TabPanel.tsx index a8e26da..eabebdf 100644 --- a/src/components/TabPanel.tsx +++ b/src/components/TabPanel.tsx @@ -19,6 +19,7 @@ import { For } from "solid-js"; import { useTheme } from "@/context/ThemeContext"; import { useNavigation } from "@/context/NavigationContext"; import { useScrollIntoView } from "@/hooks/useScrollIntoView"; +import { useSelectionMarker } from "@/hooks/useSelectionMarker"; import { TABS } from "@/utils/navigation"; import { NF_ICONS, supportsNerdFonts } from "@/utils/nerd-fonts"; @@ -51,6 +52,7 @@ export function TabListPane(props: { muted?: boolean }) { const nerd = supportsNerdFonts(); const { theme } = useTheme(); const nav = useNavigation(); + const marker = useSelectionMarker(); const cursor = () => nav.tabCursor(); const activeTab = () => nav.activeTab(); @@ -104,7 +106,7 @@ export function TabListPane(props: { muted?: boolean }) { }} > {/* ── selection marker (j/k cursor) ─────────────────────────── */} - {isCursor() ? "❯" : " "} + {isCursor() ? marker() : " "} {nerd && ( {TAB_ICON[tab]} diff --git a/src/hooks/useSelectionMarker.ts b/src/hooks/useSelectionMarker.ts new file mode 100644 index 0000000..f59ecbd --- /dev/null +++ b/src/hooks/useSelectionMarker.ts @@ -0,0 +1,16 @@ +/** + * useSelectionMarker — reactive accessor for the row-selection marker glyph. + * + * When the `showSelectionMarker` setting is on, the focused row of every list + * renders `❯`; when off (the default), it renders a space so column alignment + * is preserved. Every list pane in the app reads the marker through this hook + * so the setting applies consistently everywhere. + */ + +import { useAppStore } from "@/stores/app"; + +export function useSelectionMarker(): () => string { + const app = useAppStore(); + return () => + app.state().settings.showSelectionMarker ? "❯" : " "; +} diff --git a/src/pages/Discover/DiscoverPage.tsx b/src/pages/Discover/DiscoverPage.tsx index a090b52..d31bf32 100644 --- a/src/pages/Discover/DiscoverPage.tsx +++ b/src/pages/Discover/DiscoverPage.tsx @@ -33,6 +33,7 @@ import { PaneRow } from "@/components/PaneRow"; import { TabListPane } from "@/components/TabPanel"; import { LoadingIndicator } from "@/components/LoadingIndicator"; import { useScrollIntoView } from "@/hooks/useScrollIntoView"; +import { useSelectionMarker } from "@/hooks/useSelectionMarker"; export const DiscoverPaneCount = 1; @@ -43,6 +44,7 @@ function DiscoverPage() { const { theme } = useTheme(); const muted = () => theme.muted || theme.text; const nav = useNavigation(); + const marker = useSelectionMarker(); const depth = nav.currentDepth; const focus = (d: number = depth()) => nav.depthFocus(d); @@ -182,12 +184,11 @@ function DiscoverPage() { ref={ref} flexDirection="row" gap={1} - paddingLeft={1} paddingRight={1} backgroundColor={focusBg(index(), lf(), false)} > - {index() === nav.depthFocus(0) ? "❯" : " "} + {index() === nav.depthFocus(0) ? marker() : " "} {nerd && ( @@ -219,7 +220,6 @@ function DiscoverPage() { ref={ref} flexDirection="row" gap={1} - paddingLeft={1} paddingRight={1} backgroundColor={focusBg(index(), lf(), isActive())} onMouseDown={() => { @@ -229,7 +229,7 @@ function DiscoverPage() { }} > - {index() === lf() ? "❯" : " "} + {index() === lf() ? marker() : " "} {nerd && ( @@ -268,7 +268,6 @@ function DiscoverPage() { ref={ref} flexDirection="column" gap={0} - paddingLeft={1} paddingRight={1} backgroundColor={focusBg(index(), lf(), isActive())} onMouseDown={() => { @@ -278,7 +277,7 @@ function DiscoverPage() { > - {index() === lf() ? "❯" : " "} + {index() === lf() ? marker() : " "} {podcast.title} diff --git a/src/pages/Feed/FeedPage.tsx b/src/pages/Feed/FeedPage.tsx index a8aaf7b..404ec36 100644 --- a/src/pages/Feed/FeedPage.tsx +++ b/src/pages/Feed/FeedPage.tsx @@ -40,6 +40,7 @@ import { LoadingIndicator } from "@/components/LoadingIndicator"; import { PaneRow } from "@/components/PaneRow"; import { TabListPane } from "@/components/TabPanel"; import { useScrollIntoView } from "@/hooks/useScrollIntoView"; +import { useSelectionMarker } from "@/hooks/useSelectionMarker"; export const FeedPaneCount = 1; @@ -55,6 +56,7 @@ function FeedPage() { const { theme } = useTheme(); const muted = () => theme.muted || theme.text; const nav = useNavigation(); + const marker = useSelectionMarker(); // ── flat episode list (depth 0 — the only depth Feed has) ──────────────── const episodes = createMemo( @@ -257,7 +259,6 @@ function FeedPage() { ref={ref} flexDirection="column" gap={0} - paddingLeft={1} paddingRight={1} backgroundColor={focusBg(index(), fi(), isActive())} onMouseDown={() => { @@ -267,7 +268,7 @@ function FeedPage() { > - {index() === fi() ? "❯" : " "} + {index() === fi() ? marker() : " "} {item.episode.episodeNumber @@ -304,7 +305,6 @@ function FeedPage() { ref={moreRef} flexDirection="row" gap={1} - paddingLeft={1} paddingRight={1} backgroundColor={focusBg(episodes().length, focusedRow(), isActive())} onMouseDown={() => { @@ -313,7 +313,7 @@ function FeedPage() { }} > - {focusedOnMore() ? "❯" : " "} + {focusedOnMore() ? marker() : " "} {nerd && ( diff --git a/src/pages/MyShows/MyShowsPage.tsx b/src/pages/MyShows/MyShowsPage.tsx index 7dd6e1b..a1dac5a 100644 --- a/src/pages/MyShows/MyShowsPage.tsx +++ b/src/pages/MyShows/MyShowsPage.tsx @@ -35,6 +35,7 @@ import { LoadingIndicator } from "@/components/LoadingIndicator"; import { PaneRow } from "@/components/PaneRow"; import { TabListPane } from "@/components/TabPanel"; import { useScrollIntoView } from "@/hooks/useScrollIntoView"; +import { useSelectionMarker } from "@/hooks/useSelectionMarker"; export const MyShowsPaneCount = 1; @@ -47,6 +48,7 @@ export function MyShowsPage() { const { theme } = useTheme(); const muted = () => theme.muted || theme.text; const nav = useNavigation(); + const marker = useSelectionMarker(); const stack = nav.depthStack; const depth = nav.currentDepth; @@ -254,12 +256,11 @@ export function MyShowsPage() { ref={ref} flexDirection="row" gap={1} - paddingLeft={1} paddingRight={1} backgroundColor={focusBg(index(), lf(), false)} > - {index() === lf() ? "❯" : " "} + {index() === lf() ? marker() : " "} {showTitle(feed)} ({feed.episodes.length}) @@ -299,7 +300,6 @@ export function MyShowsPage() { ref={ref} flexDirection="row" gap={1} - paddingLeft={1} paddingRight={1} backgroundColor={focusBg(index(), lf(), isActive())} onMouseDown={() => { @@ -308,7 +308,7 @@ export function MyShowsPage() { }} > - {index() === lf() ? "❯" : " "} + {index() === lf() ? marker() : " "} {showTitle(feed)} @@ -354,7 +354,6 @@ export function MyShowsPage() { ref={ref} flexDirection="column" gap={0} - paddingLeft={1} paddingRight={1} backgroundColor={focusBg(index(), lf(), isActive())} onMouseDown={() => { @@ -364,7 +363,7 @@ export function MyShowsPage() { > - {index() === lf() ? "❯" : " "} + {index() === lf() ? marker() : " "} {ep.episodeNumber ? `#${ep.episodeNumber} ` : ""} diff --git a/src/pages/Search/SearchPage.tsx b/src/pages/Search/SearchPage.tsx index 5b30d40..e6c1842 100644 --- a/src/pages/Search/SearchPage.tsx +++ b/src/pages/Search/SearchPage.tsx @@ -42,6 +42,7 @@ import { PaneRow } from "@/components/PaneRow"; import { TabListPane } from "@/components/TabPanel"; import { LoadingIndicator } from "@/components/LoadingIndicator"; import { useScrollIntoView } from "@/hooks/useScrollIntoView"; +import { useSelectionMarker } from "@/hooks/useSelectionMarker"; export const SearchPaneCount = 1; @@ -52,6 +53,7 @@ function SearchPage() { const { theme } = useTheme(); const muted = () => theme.muted || theme.text; const nav = useNavigation(); + const marker = useSelectionMarker(); const stack = nav.depthStack; const depth = nav.currentDepth; @@ -288,7 +290,6 @@ function SearchPage() { ref={ref} flexDirection="row" gap={1} - paddingLeft={1} paddingRight={1} backgroundColor={ typing() @@ -311,7 +312,7 @@ function SearchPage() { : focusFg(index(), lf(), isActive()) } > - {index() === lf() && !typing() ? "❯" : " "} + {index() === lf() && !typing() ? marker() : " "} { @@ -375,7 +375,7 @@ function SearchPage() { > - {index() === fi() ? "❯" : " "} + {index() === fi() ? marker() : " "} {result.podcast.title} diff --git a/src/pages/Settings/PreferencesPanel.tsx b/src/pages/Settings/PreferencesPanel.tsx index a4e38a2..9528876 100644 --- a/src/pages/Settings/PreferencesPanel.tsx +++ b/src/pages/Settings/PreferencesPanel.tsx @@ -20,6 +20,7 @@ import { useFeedStore } from "@/stores/feed"; import { useTheme } from "@/context/ThemeContext"; import { useInputFocusNav } from "@/hooks/useInputFocusNav"; import { useScrollIntoView } from "@/hooks/useScrollIntoView"; +import { useSelectionMarker } from "@/hooks/useSelectionMarker"; import { NavMode, useNavigation, @@ -88,6 +89,18 @@ export function usePreferencesItems(): SettingItem[] { transparentBackground: !settings().transparentBackground, }), }, + { + id: "showSelectionMarker", + label: "Selection Marker", + kind: "toggle", + display: () => (settings().showSelectionMarker ? "On" : "Off"), + help: () => + `Show the ❯ marker on the focused row of every list (tabs, shows, episodes, results).\nType: toggle\nDefault: off\nCurrent: ${settings().showSelectionMarker ? "On" : "Off"}\nSpace/Enter to toggle.`, + toggle: () => + app.updateSettings({ + showSelectionMarker: !settings().showSelectionMarker, + }), + }, { id: "fontSize", label: "Font Size", @@ -427,6 +440,7 @@ function WhitelistEditor() { const focused = () => !nav.inputFocused() && index() === wlCursorClamped(); const ref = useScrollIntoView(focused); + const marker = useSelectionMarker(); const bg = () => (focused() ? theme.primary : undefined); const fg = () => (focused() ? theme.surface : theme.text); return ( @@ -434,7 +448,6 @@ function WhitelistEditor() { ref={ref} flexDirection="row" gap={1} - paddingLeft={1} paddingRight={1} backgroundColor={bg()} onMouseDown={() => { @@ -445,7 +458,7 @@ function WhitelistEditor() { wlToggle(feed.id); }} > - {focused() ? "❯" : " "} + {focused() ? marker() : " "} {inList(feed.id) ? "●" : "○"} {feed.customName || feed.podcast.title} diff --git a/src/pages/Settings/SettingsPage.tsx b/src/pages/Settings/SettingsPage.tsx index b76b1db..7fd243d 100644 --- a/src/pages/Settings/SettingsPage.tsx +++ b/src/pages/Settings/SettingsPage.tsx @@ -38,6 +38,7 @@ import { useDownloadItems } from "./DownloadManager"; import { PaneRow } from "@/components/PaneRow"; import { TabListPane } from "@/components/TabPanel"; import { useScrollIntoView } from "@/hooks/useScrollIntoView"; +import { useSelectionMarker } from "@/hooks/useSelectionMarker"; export const SettingsPaneCount = 1; @@ -436,17 +437,17 @@ function Row(props: { ? theme.selectedListItemText ?? theme.text : theme.text; const ref = useScrollIntoView(() => props.focused); + const marker = useSelectionMarker(); return ( - {props.focused ? "❯" : " "} + {props.focused ? marker() : " "} {props.icon && nerd && {props.icon}} {props.label} diff --git a/src/stores/app.ts b/src/stores/app.ts index f56a130..32d1223 100644 --- a/src/stores/app.ts +++ b/src/stores/app.ts @@ -30,6 +30,7 @@ const defaultSettings: AppSettings = { playbackSpeed: 1, downloadPath: "", transparentBackground: false, + showSelectionMarker: false, visualizer: defaultVisualizerSettings, }; diff --git a/src/types/settings.ts b/src/types/settings.ts index ceae72b..fced4e2 100644 --- a/src/types/settings.ts +++ b/src/types/settings.ts @@ -81,6 +81,8 @@ export type AppSettings = { downloadPath: string; /** Render the app background transparent (let the terminal's own bg show). */ transparentBackground: boolean; + /** Show the `❯` cursor marker on the focused row of every list (default: off). */ + showSelectionMarker: boolean; visualizer: VisualizerSettings; }; diff --git a/src/utils/app-persistence.ts b/src/utils/app-persistence.ts index a469ea2..bd87ab9 100644 --- a/src/utils/app-persistence.ts +++ b/src/utils/app-persistence.ts @@ -33,6 +33,7 @@ const defaultSettings: AppSettings = { playbackSpeed: 1, downloadPath: "", transparentBackground: false, + showSelectionMarker: false, visualizer: defaultVisualizerSettings, };