better themeing

This commit is contained in:
2026-02-10 14:28:06 -05:00
parent ce022dc447
commit a405474f11
3 changed files with 131 additions and 124 deletions

View File

@@ -13,6 +13,7 @@ import { format } from "date-fns";
import type { Episode } from "@/types/episode"; import type { Episode } from "@/types/episode";
import type { Feed } from "@/types/feed"; import type { Feed } from "@/types/feed";
import { PageProps } from "@/App"; import { PageProps } from "@/App";
import { useTheme } from "@/context/ThemeContext";
enum MyShowsPaneType { enum MyShowsPaneType {
SHOWS = 1, SHOWS = 1,
@@ -27,6 +28,8 @@ export function MyShowsPage(props: PageProps) {
const [showIndex, setShowIndex] = createSignal(0); const [showIndex, setShowIndex] = createSignal(0);
const [episodeIndex, setEpisodeIndex] = createSignal(0); const [episodeIndex, setEpisodeIndex] = createSignal(0);
const [isRefreshing, setIsRefreshing] = createSignal(false); const [isRefreshing, setIsRefreshing] = createSignal(false);
const { theme } = useTheme();
const mutedColor = () => theme.muted || theme.text;
/** Threshold: load more when within this many items of the end */ /** Threshold: load more when within this many items of the end */
const LOAD_MORE_THRESHOLD = 5; const LOAD_MORE_THRESHOLD = 5;
@@ -94,23 +97,6 @@ export function MyShowsPage(props: PageProps) {
} }
}; };
/** Get download status color */
const downloadColor = (episodeId: string): string => {
const status = downloadStore.getDownloadStatus(episodeId);
switch (status) {
case DownloadStatus.QUEUED:
return "yellow";
case DownloadStatus.DOWNLOADING:
return "cyan";
case DownloadStatus.COMPLETED:
return "green";
case DownloadStatus.FAILED:
return "red";
default:
return "gray";
}
};
const handleRefresh = async () => { const handleRefresh = async () => {
const show = selectedShow(); const show = selectedShow();
if (!show) return; if (!show) return;
@@ -127,17 +113,34 @@ export function MyShowsPage(props: PageProps) {
setEpisodeIndex(0); setEpisodeIndex(0);
}; };
/** Get download status color */
const downloadColor = (episodeId: string): string => {
const status = downloadStore.getDownloadStatus(episodeId);
switch (status) {
case DownloadStatus.QUEUED:
return theme.warning.toString();
case DownloadStatus.DOWNLOADING:
return theme.primary.toString();
case DownloadStatus.COMPLETED:
return theme.success.toString();
case DownloadStatus.FAILED:
return theme.error.toString();
default:
return mutedColor().toString();
}
};
return ( return (
<box flexDirection="row" flexGrow={1} width="100%"> <box flexDirection="row" flexGrow={1} width="100%">
<box flexDirection="column" height="100%"> <box flexDirection="column" height="100%">
<Show when={isRefreshing()}> <Show when={isRefreshing()}>
<text fg="yellow">Refreshing...</text> <text fg={theme.warning}>Refreshing...</text>
</Show> </Show>
<Show <Show
when={shows().length > 0} when={shows().length > 0}
fallback={ fallback={
<box padding={1}> <box padding={1}>
<text fg="gray"> <text fg={theme.muted}>
No shows yet. Subscribe from Discover or Search. No shows yet. Subscribe from Discover or Search.
</text> </text>
</box> </box>
@@ -154,19 +157,19 @@ export function MyShowsPage(props: PageProps) {
gap={1} gap={1}
paddingLeft={1} paddingLeft={1}
paddingRight={1} paddingRight={1}
backgroundColor={index() === showIndex() ? "#333" : undefined} backgroundColor={index() === showIndex() ? theme.primary : undefined}
onMouseDown={() => { onMouseDown={() => {
setShowIndex(index()); setShowIndex(index());
setEpisodeIndex(0); setEpisodeIndex(0);
}} }}
> >
<text fg={index() === showIndex() ? "cyan" : "gray"}> <text fg={index() === showIndex() ? theme.primary : theme.muted}>
{index() === showIndex() ? ">" : " "} {index() === showIndex() ? ">" : " "}
</text> </text>
<text fg={index() === showIndex() ? "white" : undefined}> <text fg={index() === showIndex() ? theme.text : undefined}>
{feed.customName || feed.podcast.title} {feed.customName || feed.podcast.title}
</text> </text>
<text fg="gray">({feed.episodes.length})</text> <text fg={theme.muted}>({feed.episodes.length})</text>
</box> </box>
)} )}
</For> </For>
@@ -178,7 +181,7 @@ export function MyShowsPage(props: PageProps) {
when={selectedShow()} when={selectedShow()}
fallback={ fallback={
<box padding={1}> <box padding={1}>
<text fg="gray">Select a show</text> <text fg={theme.muted}>Select a show</text>
</box> </box>
} }
> >
@@ -186,7 +189,7 @@ export function MyShowsPage(props: PageProps) {
when={episodes().length > 0} when={episodes().length > 0}
fallback={ fallback={
<box padding={1}> <box padding={1}>
<text fg="gray">No episodes. Press [r] to refresh.</text> <text fg={theme.muted}>No episodes. Press [r] to refresh.</text>
</box> </box>
} }
> >
@@ -202,16 +205,16 @@ export function MyShowsPage(props: PageProps) {
paddingLeft={1} paddingLeft={1}
paddingRight={1} paddingRight={1}
backgroundColor={ backgroundColor={
index() === episodeIndex() ? "#333" : undefined index() === episodeIndex() ? theme.primary : undefined
} }
onMouseDown={() => setEpisodeIndex(index())} onMouseDown={() => setEpisodeIndex(index())}
> >
<box flexDirection="row" gap={1}> <box flexDirection="row" gap={1}>
<text fg={index() === episodeIndex() ? "cyan" : "gray"}> <text fg={index() === episodeIndex() ? theme.primary : theme.muted}>
{index() === episodeIndex() ? ">" : " "} {index() === episodeIndex() ? ">" : " "}
</text> </text>
<text <text
fg={index() === episodeIndex() ? "white" : undefined} fg={index() === episodeIndex() ? theme.text : undefined}
> >
{episode.episodeNumber {episode.episodeNumber
? `#${episode.episodeNumber} ` ? `#${episode.episodeNumber} `
@@ -220,8 +223,8 @@ export function MyShowsPage(props: PageProps) {
</text> </text>
</box> </box>
<box flexDirection="row" gap={2} paddingLeft={2}> <box flexDirection="row" gap={2} paddingLeft={2}>
<text fg="gray">{formatDate(episode.pubDate)}</text> <text fg={theme.muted}>{formatDate(episode.pubDate)}</text>
<text fg="gray">{formatDuration(episode.duration)}</text> <text fg={theme.muted}>{formatDuration(episode.duration)}</text>
<Show when={downloadLabel(episode.id)}> <Show when={downloadLabel(episode.id)}>
<text fg={downloadColor(episode.id)}> <text fg={downloadColor(episode.id)}>
{downloadLabel(episode.id)} {downloadLabel(episode.id)}
@@ -233,7 +236,7 @@ export function MyShowsPage(props: PageProps) {
</For> </For>
<Show when={feedStore.isLoadingMore()}> <Show when={feedStore.isLoadingMore()}>
<box paddingLeft={2} paddingTop={1}> <box paddingLeft={2} paddingTop={1}>
<text fg="yellow">Loading more episodes...</text> <text fg={theme.warning}>Loading more episodes...</text>
</box> </box>
</Show> </Show>
<Show <Show
@@ -244,7 +247,7 @@ export function MyShowsPage(props: PageProps) {
} }
> >
<box paddingLeft={2} paddingTop={1}> <box paddingLeft={2} paddingTop={1}>
<text fg="gray">Scroll down for more episodes</text> <text fg={theme.muted}>Scroll down for more episodes</text>
</box> </box>
</Show> </Show>
</scrollbox> </scrollbox>

View File

@@ -3,6 +3,7 @@ import { PlaybackControls } from "./PlaybackControls";
import { RealtimeWaveform } from "./RealtimeWaveform"; import { RealtimeWaveform } from "./RealtimeWaveform";
import { useAudio } from "@/hooks/useAudio"; import { useAudio } from "@/hooks/useAudio";
import { useAppStore } from "@/stores/app"; import { useAppStore } from "@/stores/app";
import { useTheme } from "@/context/ThemeContext";
enum PlayerPaneType { enum PlayerPaneType {
PLAYER = 1, PLAYER = 1,
@@ -11,6 +12,7 @@ export const PlayerPaneCount = 1;
export function PlayerPage(props: PageProps) { export function PlayerPage(props: PageProps) {
const audio = useAudio(); const audio = useAudio();
const { theme } = useTheme();
const progressPercent = () => { const progressPercent = () => {
const d = audio.duration(); const d = audio.duration();
@@ -30,19 +32,19 @@ export function PlayerPage(props: PageProps) {
<text> <text>
<strong>Now Playing</strong> <strong>Now Playing</strong>
</text> </text>
<text fg="gray"> <text fg={theme.muted}>
{formatTime(audio.position())} / {formatTime(audio.duration())} ( {formatTime(audio.position())} / {formatTime(audio.duration())} (
{progressPercent()}%) {progressPercent()}%)
</text> </text>
</box> </box>
{audio.error() && <text fg="red">{audio.error()}</text>} {audio.error() && <text fg={theme.error}>{audio.error()}</text>}
<box border padding={1} flexDirection="column" gap={1}> <box border padding={1} flexDirection="column" gap={1}>
<text fg="white"> <text fg={theme.text}>
<strong>{audio.currentEpisode()?.title}</strong> <strong>{audio.currentEpisode()?.title}</strong>
</text> </text>
<text fg="gray">{audio.currentEpisode()?.description}</text> <text fg={theme.muted}>{audio.currentEpisode()?.description}</text>
<RealtimeWaveform <RealtimeWaveform
visualizerConfig={(() => { visualizerConfig={(() => {

View File

@@ -10,6 +10,7 @@ import { SearchHistory } from "./SearchHistory";
import type { SearchResult } from "@/types/source"; import type { SearchResult } from "@/types/source";
import { PageProps } from "@/App"; import { PageProps } from "@/App";
import { MyShowsPage } from "../MyShows/MyShowsPage"; import { MyShowsPage } from "../MyShows/MyShowsPage";
import { useTheme } from "@/context/ThemeContext";
enum SearchPaneType { enum SearchPaneType {
INPUT = 1, INPUT = 1,
@@ -23,6 +24,7 @@ export function SearchPage(props: PageProps) {
const [inputValue, setInputValue] = createSignal(""); const [inputValue, setInputValue] = createSignal("");
const [resultIndex, setResultIndex] = createSignal(0); const [resultIndex, setResultIndex] = createSignal(0);
const [historyIndex, setHistoryIndex] = createSignal(0); const [historyIndex, setHistoryIndex] = createSignal(0);
const { theme } = useTheme();
// Keep parent informed about input focus state // Keep parent informed about input focus state
// TODO: have a global input focused prop in useKeyboard hook // TODO: have a global input focused prop in useKeyboard hook
@@ -83,16 +85,16 @@ export function SearchPage(props: PageProps) {
paddingRight={1} paddingRight={1}
onMouseDown={handleSearch} onMouseDown={handleSearch}
> >
<text fg="cyan">[Enter] Search</text> <text fg={theme.primary}>[Enter] Search</text>
</box> </box>
</box> </box>
{/* Status */} {/* Status */}
<Show when={searchStore.isSearching()}> <Show when={searchStore.isSearching()}>
<text fg="yellow">Searching...</text> <text fg={theme.warning}>Searching...</text>
</Show> </Show>
<Show when={searchStore.error()}> <Show when={searchStore.error()}>
<text fg="red">{searchStore.error()}</text> <text fg={theme.error}>{searchStore.error()}</text>
</Show> </Show>
</box> </box>
@@ -102,7 +104,7 @@ export function SearchPage(props: PageProps) {
<box flexDirection="column" flexGrow={1} border> <box flexDirection="column" flexGrow={1} border>
<box padding={1}> <box padding={1}>
<text <text
fg={props.depth() === SearchPaneType.RESULTS ? "cyan" : "gray"} fg={props.depth() === SearchPaneType.RESULTS ? theme.primary : theme.muted}
> >
Results ({searchStore.results().length}) Results ({searchStore.results().length})
</text> </text>
@@ -111,7 +113,7 @@ export function SearchPage(props: PageProps) {
when={searchStore.results().length > 0} when={searchStore.results().length > 0}
fallback={ fallback={
<box padding={2}> <box padding={2}>
<text fg="gray"> <text fg={theme.muted}>
{searchStore.query() {searchStore.query()
? "No results found" ? "No results found"
: "Enter a search term to find podcasts"} : "Enter a search term to find podcasts"}
@@ -136,7 +138,7 @@ export function SearchPage(props: PageProps) {
<box padding={1} flexDirection="column"> <box padding={1} flexDirection="column">
<box paddingBottom={1}> <box paddingBottom={1}>
<text <text
fg={props.depth() === SearchPaneType.HISTORY ? "cyan" : "gray"} fg={props.depth() === SearchPaneType.HISTORY ? theme.primary : theme.muted}
> >
History History
</text> </text>