janitorial work

This commit is contained in:
2026-02-07 15:12:34 -05:00
parent 7eb49ac1c7
commit 73aa211229
17 changed files with 38 additions and 76 deletions

View File

@@ -1,7 +1,7 @@
import { createSignal, createMemo, ErrorBoundary } from "solid-js"; import { createSignal, createMemo, ErrorBoundary } from "solid-js";
import { useSelectionHandler } from "@opentui/solid"; import { useSelectionHandler } from "@opentui/solid";
import { Layout } from "./Layout"; import { Layout } from "./Layout";
import { TabNavigation } from "./components/TabNavigation"; import { TabId, TabNavigation } from "./components/TabNavigation";
import { FeedPage } from "@/tabs/Feed/FeedPage"; import { FeedPage } from "@/tabs/Feed/FeedPage";
import { MyShowsPage } from "@/tabs/MyShows/MyShowsPage"; import { MyShowsPage } from "@/tabs/MyShows/MyShowsPage";
import { LoginScreen } from "@/tabs/Settings/LoginScreen"; import { LoginScreen } from "@/tabs/Settings/LoginScreen";
@@ -21,7 +21,6 @@ import { useAppKeyboard } from "@/hooks/useAppKeyboard";
import { Clipboard } from "@/utils/clipboard"; import { Clipboard } from "@/utils/clipboard";
import { useToast } from "@/ui/toast"; import { useToast } from "@/ui/toast";
import { useRenderer } from "@opentui/solid"; import { useRenderer } from "@opentui/solid";
import type { TabId } from "@/components/Tab";
import type { AuthScreen } from "@/types/auth"; import type { AuthScreen } from "@/types/auth";
import type { Episode } from "@/types/episode"; import type { Episode } from "@/types/episode";

View File

@@ -47,26 +47,13 @@ export function Layout(props: LayoutProps) {
return ( return (
<box <box
flexDirection="column" flexDirection="row"
width="100%" width="100%"
height="100%" height="100%"
backgroundColor={theme.surface} backgroundColor={theme.surface}
> >
{/* Header - tab bar */}
<Show when={props.header}>
<box
style={{
height: 3,
backgroundColor: theme.surface ?? theme.backgroundPanel,
}}
>
<box style={{ paddingLeft: 1, paddingTop: 0, paddingBottom: 0 }}>
{props.header} {props.header}
</box>
</box>
</Show>
{/* Main content: side-by-side panels */}
<box flexDirection="row" style={{ flexGrow: 1 }}> <box flexDirection="row" style={{ flexGrow: 1 }}>
<For each={props.panels}> <For each={props.panels}>
{(panel, index) => ( {(panel, index) => (

View File

@@ -72,7 +72,7 @@ export function CodeValidation(props: CodeValidationProps) {
? (currentIndex - 1 + fields.length) % fields.length ? (currentIndex - 1 + fields.length) % fields.length
: (currentIndex + 1) % fields.length; : (currentIndex + 1) % fields.length;
setFocusField(fields[nextIndex]); setFocusField(fields[nextIndex]);
} else if (key.name === "return" || key.name === "enter") { } else if (key.name === "return" || key.name === "tab") {
if (focusField() === "submit") { if (focusField() === "submit") {
handleSubmit(); handleSubmit();
} else if (focusField() === "back" && props.onBack) { } else if (focusField() === "back" && props.onBack) {

View File

@@ -18,7 +18,14 @@ export const tabs: TabDefinition[] = [
export function TabNavigation(props: TabNavigationProps) { export function TabNavigation(props: TabNavigationProps) {
const { theme } = useTheme(); const { theme } = useTheme();
return ( return (
<box style={{ flexDirection: "column", gap: 1, width: 20 }}> <box
backgroundColor={theme.surface}
style={{
flexDirection: "column",
width: 10,
flexGrow: 1,
}}
>
<For each={tabs}> <For each={tabs}>
{(tab) => ( {(tab) => (
<box <box
@@ -26,16 +33,17 @@ export function TabNavigation(props: TabNavigationProps) {
borderColor={theme.border} borderColor={theme.border}
onMouseDown={() => props.onTabSelect(tab.id)} onMouseDown={() => props.onTabSelect(tab.id)}
style={{ style={{
padding: 1,
paddingLeft: 2,
backgroundColor: backgroundColor:
tab.id == props.activeTab ? theme.primary : "transparent", tab.id == props.activeTab ? theme.primary : "transparent",
}} }}
> >
<text style={{ fg: theme.text }}> <text
{tab.id == props.activeTab ? "[" : " "} style={{
fg: tab.id == props.activeTab ? "white" : theme.text,
alignSelf: "center",
}}
>
{tab.label} {tab.label}
{tab.id == props.activeTab ? "]" : " "}
</text> </text>
</box> </box>
)} )}

View File

@@ -4,11 +4,18 @@
*/ */
import { useKeyboard, useRenderer } from "@opentui/solid" import { useKeyboard, useRenderer } from "@opentui/solid"
import type { TabId } from "../components/Tab"
import type { Accessor } from "solid-js" import type { Accessor } from "solid-js"
const TAB_ORDER: TabId[] = ["feed", "shows", "discover", "search", "player", "settings"] const TAB_ORDER: TabId[] = ["feed", "shows", "discover", "search", "player", "settings"]
type TabId =
| "feed"
| "shows"
| "discover"
| "search"
| "player"
| "settings"
type ShortcutOptions = { type ShortcutOptions = {
activeTab: TabId activeTab: TabId
onTabChange: (tab: TabId) => void onTabChange: (tab: TabId) => void
@@ -53,8 +60,9 @@ export function useAppKeyboard(options: ShortcutOptions) {
return return
} }
// Return key cycles tabs (equivalent to Tab)
if (key.name === "return") { if (key.name === "return") {
options.onAction?.("enter") options.onTabChange(getNextTab(options.activeTab))
return return
} }

View File

@@ -1,37 +0,0 @@
import { useKeyboard, useRenderer } from "@opentui/solid"
type ShortcutOptions = {
onSave?: () => void
onQuit?: () => void
onTabNext?: () => void
onTabPrev?: () => void
}
export function useKeyboardShortcuts(options: ShortcutOptions) {
const renderer = useRenderer()
useKeyboard((key) => {
if (key.ctrl && key.name === "q") {
if (options.onQuit) {
options.onQuit()
} else {
renderer.destroy()
}
return
}
if (key.ctrl && key.name === "s") {
options.onSave?.()
return
}
if (key.name === "right") {
options.onTabNext?.()
return
}
if (key.name === "left") {
options.onTabPrev?.()
}
})
}

View File

@@ -38,7 +38,7 @@ export function DiscoverPage(props: DiscoverPageProps) {
} }
if ( if (
(key.name === "return" || key.name === "enter") && key.name === "return" &&
area === "categories" area === "categories"
) { ) {
setFocusArea("shows"); setFocusArea("shows");

View File

@@ -57,7 +57,7 @@ export function FeedDetail(props: FeedDetailProps) {
setSelectedIndex((i) => Math.max(0, i - 1)); setSelectedIndex((i) => Math.max(0, i - 1));
} else if (key.name === "down" || key.name === "j") { } else if (key.name === "down" || key.name === "j") {
setSelectedIndex((i) => Math.min(eps.length - 1, i + 1)); setSelectedIndex((i) => Math.min(eps.length - 1, i + 1));
} else if (key.name === "return" || key.name === "enter") { } else if (key.name === "return") {
const episode = eps[selectedIndex()]; const episode = eps[selectedIndex()];
if (episode && props.onPlayEpisode) { if (episode && props.onPlayEpisode) {
props.onPlayEpisode(episode); props.onPlayEpisode(episode);

View File

@@ -30,7 +30,7 @@ export function FeedFilterComponent(props: FeedFilterProps) {
? (currentIndex - 1 + fields.length) % fields.length ? (currentIndex - 1 + fields.length) % fields.length
: (currentIndex + 1) % fields.length; : (currentIndex + 1) % fields.length;
setFocusField(fields[nextIndex]); setFocusField(fields[nextIndex]);
} else if (key.name === "return" || key.name === "enter") { } else if (key.name === "return") {
if (focusField() === "visibility") { if (focusField() === "visibility") {
cycleVisibility(); cycleVisibility();
} else if (focusField() === "sort") { } else if (focusField() === "sort") {

View File

@@ -37,7 +37,7 @@ export function FeedList(props: FeedListProps) {
setSelectedIndex((i) => Math.max(0, i - 1)); setSelectedIndex((i) => Math.max(0, i - 1));
} else if (key.name === "down" || key.name === "j") { } else if (key.name === "down" || key.name === "j") {
setSelectedIndex((i) => Math.min(feeds.length - 1, i + 1)); setSelectedIndex((i) => Math.min(feeds.length - 1, i + 1));
} else if (key.name === "return" || key.name === "enter") { } else if (key.name === "return") {
const feed = feeds[selectedIndex()]; const feed = feeds[selectedIndex()];
if (feed && props.onOpenFeed) { if (feed && props.onOpenFeed) {
props.onOpenFeed(feed); props.onOpenFeed(feed);

View File

@@ -50,7 +50,7 @@ export function FeedPage(props: FeedPageProps) {
setSelectedIndex((i) => Math.min(episodes.length - 1, i + 1)); setSelectedIndex((i) => Math.min(episodes.length - 1, i + 1));
} else if (key.name === "up" || key.name === "k") { } else if (key.name === "up" || key.name === "k") {
setSelectedIndex((i) => Math.max(0, i - 1)); setSelectedIndex((i) => Math.max(0, i - 1));
} else if (key.name === "return" || key.name === "enter") { } else if (key.name === "return") {
const item = episodes[selectedIndex()]; const item = episodes[selectedIndex()];
if (item) props.onPlayEpisode?.(item.episode, item.feed); if (item) props.onPlayEpisode?.(item.episode, item.feed);
} else if (key.name === "home" || key.name === "g") { } else if (key.name === "home" || key.name === "g") {

View File

@@ -63,7 +63,7 @@ export function SearchPage(props: SearchPageProps) {
const area = focusArea(); const area = focusArea();
// Enter to search from input // Enter to search from input
if ((key.name === "return" || key.name === "enter") && area === "input") { if (key.name === "return" && area === "input") {
handleSearch(); handleSearch();
return; return;
} }

View File

@@ -71,7 +71,7 @@ export function LoginScreen(props: LoginScreenProps) {
? (currentIndex - 1 + fields.length) % fields.length ? (currentIndex - 1 + fields.length) % fields.length
: (currentIndex + 1) % fields.length; : (currentIndex + 1) % fields.length;
setFocusField(fields[nextIndex]); setFocusField(fields[nextIndex]);
} else if (key.name === "return" || key.name === "enter") { } else if (key.name === "return") {
if (focusField() === "submit") { if (focusField() === "submit") {
handleSubmit(); handleSubmit();
} else if (focusField() === "code" && props.onNavigateToCode) { } else if (focusField() === "code" && props.onNavigateToCode) {

View File

@@ -26,7 +26,7 @@ export function OAuthPlaceholder(props: OAuthPlaceholderProps) {
? (currentIndex - 1 + fields.length) % fields.length ? (currentIndex - 1 + fields.length) % fields.length
: (currentIndex + 1) % fields.length; : (currentIndex + 1) % fields.length;
setFocusField(fields[nextIndex]); setFocusField(fields[nextIndex]);
} else if (key.name === "return" || key.name === "enter") { } else if (key.name === "return") {
if (focusField() === "code" && props.onNavigateToCode) { if (focusField() === "code" && props.onNavigateToCode) {
props.onNavigateToCode(); props.onNavigateToCode();
} else if (focusField() === "back" && props.onBack) { } else if (focusField() === "back" && props.onBack) {

View File

@@ -46,7 +46,7 @@ export function PreferencesPanel() {
if (key.name === "right" || key.name === "l") { if (key.name === "right" || key.name === "l") {
stepValue(1); stepValue(1);
} }
if (key.name === "space" || key.name === "return" || key.name === "enter") { if (key.name === "space" || key.name === "return") {
toggleValue(); toggleValue();
} }
}; };

View File

@@ -62,7 +62,6 @@ export function SourceManager(props: SourceManagerProps) {
setSelectedIndex((i) => Math.min(sources().length - 1, i + 1)); setSelectedIndex((i) => Math.min(sources().length - 1, i + 1));
} else if ( } else if (
key.name === "return" || key.name === "return" ||
key.name === "enter" ||
key.name === "space" key.name === "space"
) { ) {
const source = sources()[selectedIndex()]; const source = sources()[selectedIndex()];
@@ -98,7 +97,6 @@ export function SourceManager(props: SourceManagerProps) {
if (focusArea() === "explicit") { if (focusArea() === "explicit") {
if ( if (
key.name === "enter" ||
key.name === "return" || key.name === "return" ||
key.name === "space" key.name === "space"
) { ) {
@@ -113,7 +111,6 @@ export function SourceManager(props: SourceManagerProps) {
if (focusArea() === "language") { if (focusArea() === "language") {
if ( if (
key.name === "enter" ||
key.name === "return" || key.name === "return" ||
key.name === "space" key.name === "space"
) { ) {

View File

@@ -29,7 +29,7 @@ export function SyncProfile(props: SyncProfileProps) {
? (currentIndex - 1 + fields.length) % fields.length ? (currentIndex - 1 + fields.length) % fields.length
: (currentIndex + 1) % fields.length; : (currentIndex + 1) % fields.length;
setFocusField(fields[nextIndex]); setFocusField(fields[nextIndex]);
} else if (key.name === "return" || key.name === "enter") { } else if (key.name === "return") {
if (focusField() === "sync" && props.onManageSync) { if (focusField() === "sync" && props.onManageSync) {
props.onManageSync(); props.onManageSync();
} else if (focusField() === "logout" && props.onLogout) { } else if (focusField() === "logout" && props.onLogout) {