import { createMemo, ErrorBoundary, Accessor } from "solid-js"; import { useKeyboard, useSelectionHandler } from "@opentui/solid"; import { TabNavigation } from "./components/TabNavigation"; import { CodeValidation } from "@/components/CodeValidation"; import { LoadingIndicator } from "@/components/LoadingIndicator"; import { useAuthStore } from "@/stores/auth"; import { useFeedStore } from "@/stores/feed"; import { useAudio } from "@/hooks/useAudio"; import { useMultimediaKeys } from "@/hooks/useMultimediaKeys"; import { FeedVisibility } from "@/types/feed"; import { Clipboard } from "@/utils/clipboard"; import { useToast } from "@/ui/toast"; import { useRenderer } from "@opentui/solid"; import type { AuthScreen } from "@/types/auth"; import type { Episode } from "@/types/episode"; import { DIRECTION, LayerGraph, TABS, LayerDepths } from "./utils/navigation"; import { useTheme, ThemeProvider } from "./context/ThemeContext"; import { KeybindProvider, useKeybinds } from "./context/KeybindContext"; import { NavigationProvider, useNavigation } from "./context/NavigationContext"; import { useAudioNavStore, AudioSource } from "./stores/audio-nav"; const DEBUG = import.meta.env.DEBUG; export function App() { const nav = useNavigation(); const auth = useAuthStore(); const feedStore = useFeedStore(); const audio = useAudio(); const toast = useToast(); const renderer = useRenderer(); const themeContext = useTheme(); const theme = themeContext.theme; // Create a reactive expression for background color const backgroundColor = () => { return themeContext.selected === "system" ? "transparent" : themeContext.theme.surface; }; const keybind = useKeybinds(); const audioNav = useAudioNavStore(); useMultimediaKeys({ playerFocused: () => nav.activeTab === TABS.PLAYER && nav.activeDepth > 0, inputFocused: () => nav.inputFocused, hasEpisode: () => !!audio.currentEpisode(), }); const handlePlayEpisode = (episode: Episode) => { audio.play(episode); nav.setActiveTab(TABS.PLAYER); nav.setActiveDepth(1); audioNav.setSource(AudioSource.FEED); }; useSelectionHandler((selection: any) => { if (!selection) return; const text = selection.getSelectedText?.(); if (!text || text.trim().length === 0) return; Clipboard.copy(text) .then(() => { toast.show({ message: "Copied to Clipboard!", variant: "info" }); }) .catch(toast.error) .finally(() => { renderer.clearSelection(); }); }); useKeyboard( (keyEvent) => { const isCycle = keybind.match("cycle", keyEvent); const isUp = keybind.match("up", keyEvent); const isDown = keybind.match("down", keyEvent); const isLeft = keybind.match("left", keyEvent); const isRight = keybind.match("right", keyEvent); const isDive = keybind.match("dive", keyEvent); const isOut = keybind.match("out", keyEvent); const isToggle = keybind.match("audio-toggle", keyEvent); const isNext = keybind.match("audio-next", keyEvent); const isPrev = keybind.match("audio-prev", keyEvent); const isSeekForward = keybind.match("audio-seek-forward", keyEvent); const isSeekBackward = keybind.match("audio-seek-backward", keyEvent); const isQuit = keybind.match("quit", keyEvent); const isInverting = keybind.isInverting(keyEvent); // only handling top navigation here, cycle through tabs, just to high priority(player) all else to be handled in each tab if (nav.activeDepth() == 0) { if ( (isCycle && !isInverting) || (isDown && !isInverting) || (isUp && isInverting) ) { nav.nextTab(); return; } if ( (isCycle && isInverting) || (isDown && isInverting) || (isUp && !isInverting) ) { nav.prevTab(); return; } if ( (isDive && !isInverting) || (isOut && isInverting) || (isRight && !isInverting) || (isLeft && isInverting) ) { nav.setActiveDepth(1); } } if (nav.activeDepth() == 1) { if ( (isDive && isInverting) || (isOut && !isInverting) || (isRight && isInverting) || (isLeft && !isInverting) ) { nav.setActiveDepth(0); } } }, { release: false }, ); return ( ( Error: {err?.message ?? String(err)} {"\n"} Press a number key (1-6) to switch tabs. )} > {DEBUG && ( )} {LayerGraph[nav.activeTab()]()} ); }