From 276732d2a9c7502d1562340f20d4d0a88498399c Mon Sep 17 00:00:00 2001 From: Michael Freno Date: Thu, 12 Feb 2026 00:11:56 -0500 Subject: [PATCH] continued out the reuse --- src/components/Selectable.tsx | 48 +++++++++++++++++++++++++++--- src/context/ThemeContext.tsx | 11 +++++-- src/pages/Discover/PodcastCard.tsx | 6 ++-- src/pages/Feed/FeedItem.tsx | 22 +++++++------- src/pages/Feed/FeedPage.tsx | 22 +++++++------- src/pages/Search/ResultCard.tsx | 26 ++++++++-------- src/types/desktop-theme.ts | 44 +++++++++++++++++++++++---- src/types/settings.ts | 9 ++++++ src/types/theme-schema.ts | 6 ++++ 9 files changed, 145 insertions(+), 49 deletions(-) diff --git a/src/components/Selectable.tsx b/src/components/Selectable.tsx index 3a590e9..690ca89 100644 --- a/src/components/Selectable.tsx +++ b/src/components/Selectable.tsx @@ -6,7 +6,11 @@ export function SelectableBox({ selected, children, ...props -}: { selected: () => boolean; children: JSXElement } & BoxOptions) { +}: { + selected: () => boolean; + + children: JSXElement; +} & BoxOptions) { const { theme } = useTheme(); return ( @@ -21,18 +25,54 @@ export function SelectableBox({ ); } +enum ColorSet { + PRIMARY, + SECONDARY, + TERTIARY, + DEFAULT, +} +function getTextColor(set: ColorSet, selected: () => boolean) { + const { theme } = useTheme(); + switch (set) { + case ColorSet.PRIMARY: + return selected() ? theme.textSelectedPrimary : theme.textPrimary; + case ColorSet.SECONDARY: + return selected() ? theme.textSelectedSecondary : theme.textSecondary; + case ColorSet.TERTIARY: + return selected() ? theme.textSelectedTertiary : theme.textTertiary; + default: + return theme.textPrimary; + } +} + export function SelectableText({ selected, children, + primary, + secondary, + tertiary, ...props }: { selected: () => boolean; + primary?: boolean; + secondary?: boolean; + tertiary?: boolean; children: JSXElement; } & TextOptions) { - const { theme } = useTheme(); - return ( - + {children} ); diff --git a/src/context/ThemeContext.tsx b/src/context/ThemeContext.tsx index 866ca48..28805a9 100644 --- a/src/context/ThemeContext.tsx +++ b/src/context/ThemeContext.tsx @@ -22,7 +22,7 @@ import { type TerminalColors, } from "@opentui/core"; -type ThemeResolved = { +export type ThemeResolved = { primary: RGBA; secondary: RGBA; accent: RGBA; @@ -32,7 +32,13 @@ type ThemeResolved = { info: RGBA; text: RGBA; textMuted: RGBA; - selectedListItemText: RGBA; + textPrimary: RGBA; + textSecondary: RGBA; + textTertiary: RGBA; + textSelectedPrimary: RGBA; + textSelectedSecondary: RGBA; + textSelectedTertiary: RGBA; + background: RGBA; backgroundPanel: RGBA; backgroundElement: RGBA; @@ -77,6 +83,7 @@ type ThemeResolved = { syntaxPunctuation: RGBA; muted?: RGBA; surface?: RGBA; + selectedListItemText?: RGBA; layerBackgrounds?: { layer0: RGBA; layer1: RGBA; diff --git a/src/pages/Discover/PodcastCard.tsx b/src/pages/Discover/PodcastCard.tsx index 3061018..6150806 100644 --- a/src/pages/Discover/PodcastCard.tsx +++ b/src/pages/Discover/PodcastCard.tsx @@ -29,7 +29,7 @@ export function PodcastCard(props: PodcastCardProps) { onMouseDown={props.onSelect} > - props.selected}> + props.selected} primary> {props.podcast.title} @@ -42,7 +42,7 @@ export function PodcastCard(props: PodcastCardProps) { props.selected} - fg={theme.textMuted} + tertiary > by {props.podcast.author} @@ -52,7 +52,7 @@ export function PodcastCard(props: PodcastCardProps) { props.selected} - fg={theme.text} + tertiary > {props.podcast.description!.length > 80 ? props.podcast.description!.slice(0, 80) + "..." diff --git a/src/pages/Feed/FeedItem.tsx b/src/pages/Feed/FeedItem.tsx index d4bced8..c9880ac 100644 --- a/src/pages/Feed/FeedItem.tsx +++ b/src/pages/Feed/FeedItem.tsx @@ -54,26 +54,26 @@ export function FeedItem(props: FeedItemProps) { > props.isSelected} - fg={theme.primary} + primary > {props.isSelected ? ">" : " "} props.isSelected} - fg={visibilityColor()} + tertiary > {visibilityIcon()} props.isSelected} - fg={theme.text} + primary > {props.feed.customName || props.feed.podcast.title} {props.showEpisodeCount && ( props.isSelected} - fg={theme.textMuted} + tertiary > ({episodeCount()}) @@ -95,25 +95,25 @@ export function FeedItem(props: FeedItemProps) { props.isSelected} - fg={theme.primary} + primary > {props.isSelected ? ">" : " "} props.isSelected} - fg={visibilityColor()} + tertiary > {visibilityIcon()} props.isSelected} - fg={theme.warning} + secondary > {pinnedIndicator()} props.isSelected} - fg={theme.text} + primary > {props.feed.customName || props.feed.podcast.title} @@ -123,7 +123,7 @@ export function FeedItem(props: FeedItemProps) { {props.showEpisodeCount && ( props.isSelected} - fg={theme.textMuted} + tertiary > {episodeCount()} episodes ({unplayedCount()} new) @@ -131,7 +131,7 @@ export function FeedItem(props: FeedItemProps) { {props.showLastUpdated && ( props.isSelected} - fg={theme.textMuted} + tertiary > Updated: {formatDate(props.feed.lastUpdated)} @@ -143,7 +143,7 @@ export function FeedItem(props: FeedItemProps) { selected={() => props.isSelected} paddingLeft={4} paddingTop={0} - fg={theme.textMuted} + tertiary > {props.feed.podcast.description.slice(0, 60)} {props.feed.podcast.description.length > 60 ? "..." : ""} diff --git a/src/pages/Feed/FeedPage.tsx b/src/pages/Feed/FeedPage.tsx index 1b1aee4..0d7173e 100644 --- a/src/pages/Feed/FeedPage.tsx +++ b/src/pages/Feed/FeedPage.tsx @@ -80,9 +80,9 @@ export function FeedPage(props: PageProps) { b.localeCompare(a))}> {([date, episode], groupIndex) => ( <> - - {date} - + + false} primary>{date} + groupIndex() === selectedIndex()} flexDirection="column" @@ -93,23 +93,25 @@ export function FeedPage(props: PageProps) { paddingBottom={0} onMouseDown={() => setSelectedIndex(groupIndex())} > - groupIndex() === selectedIndex()}> + groupIndex() === selectedIndex()} primary> {groupIndex() === selectedIndex() ? ">" : " "} groupIndex() === selectedIndex()} - fg={theme.text} + primary > {episode.episode.title} - {episode.feed.podcast.title} - + groupIndex() === selectedIndex()} primary> + {episode.feed.podcast.title} + + groupIndex() === selectedIndex()} tertiary> {formatDate(episode.episode.pubDate)} - - + + groupIndex() === selectedIndex()} tertiary> {formatDuration(episode.episode.duration)} - + diff --git a/src/pages/Search/ResultCard.tsx b/src/pages/Search/ResultCard.tsx index 15dec65..c04f168 100644 --- a/src/pages/Search/ResultCard.tsx +++ b/src/pages/Search/ResultCard.tsx @@ -27,19 +27,19 @@ export function ResultCard(props: ResultCardProps) { justifyContent="space-between" alignItems="center" > - - props.selected} - fg={theme.primary} - > - {podcast().title} - - - + + props.selected} + primary + > + {podcast().title} + + + [Subscribed] diff --git a/src/types/desktop-theme.ts b/src/types/desktop-theme.ts index 71f992d..c18053f 100644 --- a/src/types/desktop-theme.ts +++ b/src/types/desktop-theme.ts @@ -16,10 +16,18 @@ export const BASE_THEME_COLORS: ThemeColors = { secondary: "#a9b1d6", accent: "#f6c177", text: "#e6edf3", + textPrimary: "#e6edf3", + textSecondary: "#a9b1d6", + textTertiary: "#7d8590", + textSelectedPrimary: "#1b1f27", + textSelectedSecondary: "#e6edf3", + textSelectedTertiary: "#a9b1d6", muted: "#7d8590", warning: "#f0b429", error: "#f47067", success: "#3fb950", + _hasSelectedListItemText: true, + thinkingOpacity: 0.5, } // Base layer backgrounds @@ -61,16 +69,22 @@ export const THEMES_DESKTOP: DesktopTheme = { secondary: "#cba6f7", accent: "#f9e2af", text: "#cdd6f4", + textPrimary: "#cdd6f4", + textSecondary: "#cba6f7", + textTertiary: "#7f849c", + textSelectedPrimary: "#1e1e2e", + textSelectedSecondary: "#cdd6f4", + textSelectedTertiary: "#cba6f7", muted: "#7f849c", warning: "#fab387", error: "#f38ba8", success: "#a6e3a1", - layerBackgrounds: { - layer0: "transparent", - layer1: "#181825", - layer2: "#11111b", - layer3: "#0a0a0f", - }, + layerBackgrounds: { + layer0: "transparent", + layer1: "#181825", + layer2: "#11111b", + layer3: "#0a0a0f", + }, }, }, { @@ -82,6 +96,12 @@ export const THEMES_DESKTOP: DesktopTheme = { secondary: "#83a598", accent: "#fe8019", text: "#ebdbb2", + textPrimary: "#ebdbb2", + textSecondary: "#83a598", + textTertiary: "#928374", + textSelectedPrimary: "#282828", + textSelectedSecondary: "#ebdbb2", + textSelectedTertiary: "#83a598", muted: "#928374", warning: "#fabd2f", error: "#fb4934", @@ -103,6 +123,12 @@ export const THEMES_DESKTOP: DesktopTheme = { secondary: "#bb9af7", accent: "#e0af68", text: "#c0caf5", + textPrimary: "#c0caf5", + textSecondary: "#bb9af7", + textTertiary: "#565f89", + textSelectedPrimary: "#1a1b26", + textSelectedSecondary: "#c0caf5", + textSelectedTertiary: "#bb9af7", muted: "#565f89", warning: "#e0af68", error: "#f7768e", @@ -124,6 +150,12 @@ export const THEMES_DESKTOP: DesktopTheme = { secondary: "#81a1c1", accent: "#ebcb8b", text: "#eceff4", + textPrimary: "#eceff4", + textSecondary: "#81a1c1", + textTertiary: "#4c566a", + textSelectedPrimary: "#2e3440", + textSelectedSecondary: "#eceff4", + textSelectedTertiary: "#81a1c1", muted: "#4c566a", warning: "#ebcb8b", error: "#bf616a", diff --git a/src/types/settings.ts b/src/types/settings.ts index 996250d..2abf560 100644 --- a/src/types/settings.ts +++ b/src/types/settings.ts @@ -23,11 +23,20 @@ export type ThemeColors = { secondary: ColorValue; accent: ColorValue; text: ColorValue; + textPrimary?: ColorValue; + textSecondary?: ColorValue; + textTertiary?: ColorValue; + textSelectedPrimary?: ColorValue; + textSelectedSecondary?: ColorValue; + textSelectedTertiary?: ColorValue; muted: ColorValue; warning: ColorValue; error: ColorValue; success: ColorValue; layerBackgrounds?: LayerBackgrounds; + _hasSelectedListItemText?: boolean; + thinkingOpacity?: number; + selectedListItemText?: ColorValue; }; export type ThemeVariant = { diff --git a/src/types/theme-schema.ts b/src/types/theme-schema.ts index 65fb6ae..a5004fa 100644 --- a/src/types/theme-schema.ts +++ b/src/types/theme-schema.ts @@ -23,4 +23,10 @@ export type ThemeJson = { export type ThemeColors = Record & { _hasSelectedListItemText: boolean thinkingOpacity: number + textPrimary?: ColorValue + textSecondary?: ColorValue + textTertiary?: ColorValue + textSelectedPrimary?: ColorValue + textSelectedSecondary?: ColorValue + textSelectedTertiary?: ColorValue }