Compare commits
2 Commits
19a1f1a43b
...
73aa211229
| Author | SHA1 | Date | |
|---|---|---|---|
| 73aa211229 | |||
| 7eb49ac1c7 |
@@ -1,7 +1,7 @@
|
||||
import { createSignal, createMemo, ErrorBoundary } from "solid-js";
|
||||
import { useSelectionHandler } from "@opentui/solid";
|
||||
import { Layout } from "./Layout";
|
||||
import { TabNavigation } from "./components/TabNavigation";
|
||||
import { TabId, TabNavigation } from "./components/TabNavigation";
|
||||
import { FeedPage } from "@/tabs/Feed/FeedPage";
|
||||
import { MyShowsPage } from "@/tabs/MyShows/MyShowsPage";
|
||||
import { LoginScreen } from "@/tabs/Settings/LoginScreen";
|
||||
@@ -21,7 +21,6 @@ import { useAppKeyboard } from "@/hooks/useAppKeyboard";
|
||||
import { Clipboard } from "@/utils/clipboard";
|
||||
import { useToast } from "@/ui/toast";
|
||||
import { useRenderer } from "@opentui/solid";
|
||||
import type { TabId } from "@/components/Tab";
|
||||
import type { AuthScreen } from "@/types/auth";
|
||||
import type { Episode } from "@/types/episode";
|
||||
|
||||
|
||||
@@ -47,26 +47,13 @@ export function Layout(props: LayoutProps) {
|
||||
|
||||
return (
|
||||
<box
|
||||
flexDirection="column"
|
||||
flexDirection="row"
|
||||
width="100%"
|
||||
height="100%"
|
||||
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}
|
||||
</box>
|
||||
</box>
|
||||
</Show>
|
||||
|
||||
{/* Main content: side-by-side panels */}
|
||||
<box flexDirection="row" style={{ flexGrow: 1 }}>
|
||||
<For each={props.panels}>
|
||||
{(panel, index) => (
|
||||
|
||||
@@ -72,7 +72,7 @@ export function CodeValidation(props: CodeValidationProps) {
|
||||
? (currentIndex - 1 + fields.length) % fields.length
|
||||
: (currentIndex + 1) % fields.length;
|
||||
setFocusField(fields[nextIndex]);
|
||||
} else if (key.name === "return" || key.name === "enter") {
|
||||
} else if (key.name === "return" || key.name === "tab") {
|
||||
if (focusField() === "submit") {
|
||||
handleSubmit();
|
||||
} else if (focusField() === "back" && props.onBack) {
|
||||
|
||||
@@ -18,7 +18,14 @@ export const tabs: TabDefinition[] = [
|
||||
export function TabNavigation(props: TabNavigationProps) {
|
||||
const { theme } = useTheme();
|
||||
return (
|
||||
<box style={{ flexDirection: "row", gap: 1 }}>
|
||||
<box
|
||||
backgroundColor={theme.surface}
|
||||
style={{
|
||||
flexDirection: "column",
|
||||
width: 10,
|
||||
flexGrow: 1,
|
||||
}}
|
||||
>
|
||||
<For each={tabs}>
|
||||
{(tab) => (
|
||||
<box
|
||||
@@ -26,15 +33,17 @@ export function TabNavigation(props: TabNavigationProps) {
|
||||
borderColor={theme.border}
|
||||
onMouseDown={() => props.onTabSelect(tab.id)}
|
||||
style={{
|
||||
padding: 1,
|
||||
backgroundColor:
|
||||
tab.id == props.activeTab ? theme.primary : "transparent",
|
||||
}}
|
||||
>
|
||||
<text style={{ fg: theme.text }}>
|
||||
{tab.id == props.activeTab ? "[" : " "}
|
||||
<text
|
||||
style={{
|
||||
fg: tab.id == props.activeTab ? "white" : theme.text,
|
||||
alignSelf: "center",
|
||||
}}
|
||||
>
|
||||
{tab.label}
|
||||
{tab.id == props.activeTab ? "]" : " "}
|
||||
</text>
|
||||
</box>
|
||||
)}
|
||||
|
||||
@@ -4,11 +4,18 @@
|
||||
*/
|
||||
|
||||
import { useKeyboard, useRenderer } from "@opentui/solid"
|
||||
import type { TabId } from "../components/Tab"
|
||||
import type { Accessor } from "solid-js"
|
||||
|
||||
const TAB_ORDER: TabId[] = ["feed", "shows", "discover", "search", "player", "settings"]
|
||||
|
||||
type TabId =
|
||||
| "feed"
|
||||
| "shows"
|
||||
| "discover"
|
||||
| "search"
|
||||
| "player"
|
||||
| "settings"
|
||||
|
||||
type ShortcutOptions = {
|
||||
activeTab: TabId
|
||||
onTabChange: (tab: TabId) => void
|
||||
@@ -53,8 +60,9 @@ export function useAppKeyboard(options: ShortcutOptions) {
|
||||
return
|
||||
}
|
||||
|
||||
// Return key cycles tabs (equivalent to Tab)
|
||||
if (key.name === "return") {
|
||||
options.onAction?.("enter")
|
||||
options.onTabChange(getNextTab(options.activeTab))
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@@ -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?.()
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -38,7 +38,7 @@ export function DiscoverPage(props: DiscoverPageProps) {
|
||||
}
|
||||
|
||||
if (
|
||||
(key.name === "return" || key.name === "enter") &&
|
||||
key.name === "return" &&
|
||||
area === "categories"
|
||||
) {
|
||||
setFocusArea("shows");
|
||||
|
||||
@@ -57,7 +57,7 @@ export function FeedDetail(props: FeedDetailProps) {
|
||||
setSelectedIndex((i) => Math.max(0, i - 1));
|
||||
} else if (key.name === "down" || key.name === "j") {
|
||||
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()];
|
||||
if (episode && props.onPlayEpisode) {
|
||||
props.onPlayEpisode(episode);
|
||||
|
||||
@@ -30,7 +30,7 @@ export function FeedFilterComponent(props: FeedFilterProps) {
|
||||
? (currentIndex - 1 + fields.length) % fields.length
|
||||
: (currentIndex + 1) % fields.length;
|
||||
setFocusField(fields[nextIndex]);
|
||||
} else if (key.name === "return" || key.name === "enter") {
|
||||
} else if (key.name === "return") {
|
||||
if (focusField() === "visibility") {
|
||||
cycleVisibility();
|
||||
} else if (focusField() === "sort") {
|
||||
|
||||
@@ -37,7 +37,7 @@ export function FeedList(props: FeedListProps) {
|
||||
setSelectedIndex((i) => Math.max(0, i - 1));
|
||||
} else if (key.name === "down" || key.name === "j") {
|
||||
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()];
|
||||
if (feed && props.onOpenFeed) {
|
||||
props.onOpenFeed(feed);
|
||||
|
||||
@@ -50,7 +50,7 @@ export function FeedPage(props: FeedPageProps) {
|
||||
setSelectedIndex((i) => Math.min(episodes.length - 1, i + 1));
|
||||
} else if (key.name === "up" || key.name === "k") {
|
||||
setSelectedIndex((i) => Math.max(0, i - 1));
|
||||
} else if (key.name === "return" || key.name === "enter") {
|
||||
} else if (key.name === "return") {
|
||||
const item = episodes[selectedIndex()];
|
||||
if (item) props.onPlayEpisode?.(item.episode, item.feed);
|
||||
} else if (key.name === "home" || key.name === "g") {
|
||||
|
||||
@@ -63,7 +63,7 @@ export function SearchPage(props: SearchPageProps) {
|
||||
const area = focusArea();
|
||||
|
||||
// Enter to search from input
|
||||
if ((key.name === "return" || key.name === "enter") && area === "input") {
|
||||
if (key.name === "return" && area === "input") {
|
||||
handleSearch();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -71,7 +71,7 @@ export function LoginScreen(props: LoginScreenProps) {
|
||||
? (currentIndex - 1 + fields.length) % fields.length
|
||||
: (currentIndex + 1) % fields.length;
|
||||
setFocusField(fields[nextIndex]);
|
||||
} else if (key.name === "return" || key.name === "enter") {
|
||||
} else if (key.name === "return") {
|
||||
if (focusField() === "submit") {
|
||||
handleSubmit();
|
||||
} else if (focusField() === "code" && props.onNavigateToCode) {
|
||||
|
||||
@@ -26,7 +26,7 @@ export function OAuthPlaceholder(props: OAuthPlaceholderProps) {
|
||||
? (currentIndex - 1 + fields.length) % fields.length
|
||||
: (currentIndex + 1) % fields.length;
|
||||
setFocusField(fields[nextIndex]);
|
||||
} else if (key.name === "return" || key.name === "enter") {
|
||||
} else if (key.name === "return") {
|
||||
if (focusField() === "code" && props.onNavigateToCode) {
|
||||
props.onNavigateToCode();
|
||||
} else if (focusField() === "back" && props.onBack) {
|
||||
|
||||
@@ -46,7 +46,7 @@ export function PreferencesPanel() {
|
||||
if (key.name === "right" || key.name === "l") {
|
||||
stepValue(1);
|
||||
}
|
||||
if (key.name === "space" || key.name === "return" || key.name === "enter") {
|
||||
if (key.name === "space" || key.name === "return") {
|
||||
toggleValue();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -62,7 +62,6 @@ export function SourceManager(props: SourceManagerProps) {
|
||||
setSelectedIndex((i) => Math.min(sources().length - 1, i + 1));
|
||||
} else if (
|
||||
key.name === "return" ||
|
||||
key.name === "enter" ||
|
||||
key.name === "space"
|
||||
) {
|
||||
const source = sources()[selectedIndex()];
|
||||
@@ -98,7 +97,6 @@ export function SourceManager(props: SourceManagerProps) {
|
||||
|
||||
if (focusArea() === "explicit") {
|
||||
if (
|
||||
key.name === "enter" ||
|
||||
key.name === "return" ||
|
||||
key.name === "space"
|
||||
) {
|
||||
@@ -113,7 +111,6 @@ export function SourceManager(props: SourceManagerProps) {
|
||||
|
||||
if (focusArea() === "language") {
|
||||
if (
|
||||
key.name === "enter" ||
|
||||
key.name === "return" ||
|
||||
key.name === "space"
|
||||
) {
|
||||
|
||||
@@ -29,7 +29,7 @@ export function SyncProfile(props: SyncProfileProps) {
|
||||
? (currentIndex - 1 + fields.length) % fields.length
|
||||
: (currentIndex + 1) % fields.length;
|
||||
setFocusField(fields[nextIndex]);
|
||||
} else if (key.name === "return" || key.name === "enter") {
|
||||
} else if (key.name === "return") {
|
||||
if (focusField() === "sync" && props.onManageSync) {
|
||||
props.onManageSync();
|
||||
} else if (focusField() === "logout" && props.onLogout) {
|
||||
|
||||
Reference in New Issue
Block a user