continued out the reuse
This commit is contained in:
@@ -6,7 +6,11 @@ export function SelectableBox({
|
|||||||
selected,
|
selected,
|
||||||
children,
|
children,
|
||||||
...props
|
...props
|
||||||
}: { selected: () => boolean; children: JSXElement } & BoxOptions) {
|
}: {
|
||||||
|
selected: () => boolean;
|
||||||
|
|
||||||
|
children: JSXElement;
|
||||||
|
} & BoxOptions) {
|
||||||
const { theme } = useTheme();
|
const { theme } = useTheme();
|
||||||
|
|
||||||
return (
|
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({
|
export function SelectableText({
|
||||||
selected,
|
selected,
|
||||||
children,
|
children,
|
||||||
|
primary,
|
||||||
|
secondary,
|
||||||
|
tertiary,
|
||||||
...props
|
...props
|
||||||
}: {
|
}: {
|
||||||
selected: () => boolean;
|
selected: () => boolean;
|
||||||
|
primary?: boolean;
|
||||||
|
secondary?: boolean;
|
||||||
|
tertiary?: boolean;
|
||||||
children: JSXElement;
|
children: JSXElement;
|
||||||
} & TextOptions) {
|
} & TextOptions) {
|
||||||
const { theme } = useTheme();
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<text fg={selected() ? theme.surface : theme.text} {...props}>
|
<text
|
||||||
|
fg={getTextColor(
|
||||||
|
primary
|
||||||
|
? ColorSet.PRIMARY
|
||||||
|
: secondary
|
||||||
|
? ColorSet.SECONDARY
|
||||||
|
: tertiary
|
||||||
|
? ColorSet.TERTIARY
|
||||||
|
: ColorSet.DEFAULT,
|
||||||
|
selected,
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
{children}
|
{children}
|
||||||
</text>
|
</text>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ import {
|
|||||||
type TerminalColors,
|
type TerminalColors,
|
||||||
} from "@opentui/core";
|
} from "@opentui/core";
|
||||||
|
|
||||||
type ThemeResolved = {
|
export type ThemeResolved = {
|
||||||
primary: RGBA;
|
primary: RGBA;
|
||||||
secondary: RGBA;
|
secondary: RGBA;
|
||||||
accent: RGBA;
|
accent: RGBA;
|
||||||
@@ -32,7 +32,13 @@ type ThemeResolved = {
|
|||||||
info: RGBA;
|
info: RGBA;
|
||||||
text: RGBA;
|
text: RGBA;
|
||||||
textMuted: RGBA;
|
textMuted: RGBA;
|
||||||
selectedListItemText: RGBA;
|
textPrimary: RGBA;
|
||||||
|
textSecondary: RGBA;
|
||||||
|
textTertiary: RGBA;
|
||||||
|
textSelectedPrimary: RGBA;
|
||||||
|
textSelectedSecondary: RGBA;
|
||||||
|
textSelectedTertiary: RGBA;
|
||||||
|
|
||||||
background: RGBA;
|
background: RGBA;
|
||||||
backgroundPanel: RGBA;
|
backgroundPanel: RGBA;
|
||||||
backgroundElement: RGBA;
|
backgroundElement: RGBA;
|
||||||
@@ -77,6 +83,7 @@ type ThemeResolved = {
|
|||||||
syntaxPunctuation: RGBA;
|
syntaxPunctuation: RGBA;
|
||||||
muted?: RGBA;
|
muted?: RGBA;
|
||||||
surface?: RGBA;
|
surface?: RGBA;
|
||||||
|
selectedListItemText?: RGBA;
|
||||||
layerBackgrounds?: {
|
layerBackgrounds?: {
|
||||||
layer0: RGBA;
|
layer0: RGBA;
|
||||||
layer1: RGBA;
|
layer1: RGBA;
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ export function PodcastCard(props: PodcastCardProps) {
|
|||||||
onMouseDown={props.onSelect}
|
onMouseDown={props.onSelect}
|
||||||
>
|
>
|
||||||
<box flexDirection="row" gap={2} alignItems="center">
|
<box flexDirection="row" gap={2} alignItems="center">
|
||||||
<SelectableText selected={() => props.selected}>
|
<SelectableText selected={() => props.selected} primary>
|
||||||
<strong>{props.podcast.title}</strong>
|
<strong>{props.podcast.title}</strong>
|
||||||
</SelectableText>
|
</SelectableText>
|
||||||
|
|
||||||
@@ -42,7 +42,7 @@ export function PodcastCard(props: PodcastCardProps) {
|
|||||||
<Show when={props.podcast.author && !props.compact}>
|
<Show when={props.podcast.author && !props.compact}>
|
||||||
<SelectableText
|
<SelectableText
|
||||||
selected={() => props.selected}
|
selected={() => props.selected}
|
||||||
fg={theme.textMuted}
|
tertiary
|
||||||
>
|
>
|
||||||
by {props.podcast.author}
|
by {props.podcast.author}
|
||||||
</SelectableText>
|
</SelectableText>
|
||||||
@@ -52,7 +52,7 @@ export function PodcastCard(props: PodcastCardProps) {
|
|||||||
<Show when={props.podcast.description && !props.compact}>
|
<Show when={props.podcast.description && !props.compact}>
|
||||||
<SelectableText
|
<SelectableText
|
||||||
selected={() => props.selected}
|
selected={() => props.selected}
|
||||||
fg={theme.text}
|
tertiary
|
||||||
>
|
>
|
||||||
{props.podcast.description!.length > 80
|
{props.podcast.description!.length > 80
|
||||||
? props.podcast.description!.slice(0, 80) + "..."
|
? props.podcast.description!.slice(0, 80) + "..."
|
||||||
|
|||||||
@@ -54,26 +54,26 @@ export function FeedItem(props: FeedItemProps) {
|
|||||||
>
|
>
|
||||||
<SelectableText
|
<SelectableText
|
||||||
selected={() => props.isSelected}
|
selected={() => props.isSelected}
|
||||||
fg={theme.primary}
|
primary
|
||||||
>
|
>
|
||||||
{props.isSelected ? ">" : " "}
|
{props.isSelected ? ">" : " "}
|
||||||
</SelectableText>
|
</SelectableText>
|
||||||
<SelectableText
|
<SelectableText
|
||||||
selected={() => props.isSelected}
|
selected={() => props.isSelected}
|
||||||
fg={visibilityColor()}
|
tertiary
|
||||||
>
|
>
|
||||||
{visibilityIcon()}
|
{visibilityIcon()}
|
||||||
</SelectableText>
|
</SelectableText>
|
||||||
<SelectableText
|
<SelectableText
|
||||||
selected={() => props.isSelected}
|
selected={() => props.isSelected}
|
||||||
fg={theme.text}
|
primary
|
||||||
>
|
>
|
||||||
{props.feed.customName || props.feed.podcast.title}
|
{props.feed.customName || props.feed.podcast.title}
|
||||||
</SelectableText>
|
</SelectableText>
|
||||||
{props.showEpisodeCount && (
|
{props.showEpisodeCount && (
|
||||||
<SelectableText
|
<SelectableText
|
||||||
selected={() => props.isSelected}
|
selected={() => props.isSelected}
|
||||||
fg={theme.textMuted}
|
tertiary
|
||||||
>
|
>
|
||||||
({episodeCount()})
|
({episodeCount()})
|
||||||
</SelectableText>
|
</SelectableText>
|
||||||
@@ -95,25 +95,25 @@ export function FeedItem(props: FeedItemProps) {
|
|||||||
<box flexDirection="row" gap={1}>
|
<box flexDirection="row" gap={1}>
|
||||||
<SelectableText
|
<SelectableText
|
||||||
selected={() => props.isSelected}
|
selected={() => props.isSelected}
|
||||||
fg={theme.primary}
|
primary
|
||||||
>
|
>
|
||||||
{props.isSelected ? ">" : " "}
|
{props.isSelected ? ">" : " "}
|
||||||
</SelectableText>
|
</SelectableText>
|
||||||
<SelectableText
|
<SelectableText
|
||||||
selected={() => props.isSelected}
|
selected={() => props.isSelected}
|
||||||
fg={visibilityColor()}
|
tertiary
|
||||||
>
|
>
|
||||||
{visibilityIcon()}
|
{visibilityIcon()}
|
||||||
</SelectableText>
|
</SelectableText>
|
||||||
<SelectableText
|
<SelectableText
|
||||||
selected={() => props.isSelected}
|
selected={() => props.isSelected}
|
||||||
fg={theme.warning}
|
secondary
|
||||||
>
|
>
|
||||||
{pinnedIndicator()}
|
{pinnedIndicator()}
|
||||||
</SelectableText>
|
</SelectableText>
|
||||||
<SelectableText
|
<SelectableText
|
||||||
selected={() => props.isSelected}
|
selected={() => props.isSelected}
|
||||||
fg={theme.text}
|
primary
|
||||||
>
|
>
|
||||||
<strong>{props.feed.customName || props.feed.podcast.title}</strong>
|
<strong>{props.feed.customName || props.feed.podcast.title}</strong>
|
||||||
</SelectableText>
|
</SelectableText>
|
||||||
@@ -123,7 +123,7 @@ export function FeedItem(props: FeedItemProps) {
|
|||||||
{props.showEpisodeCount && (
|
{props.showEpisodeCount && (
|
||||||
<SelectableText
|
<SelectableText
|
||||||
selected={() => props.isSelected}
|
selected={() => props.isSelected}
|
||||||
fg={theme.textMuted}
|
tertiary
|
||||||
>
|
>
|
||||||
{episodeCount()} episodes ({unplayedCount()} new)
|
{episodeCount()} episodes ({unplayedCount()} new)
|
||||||
</SelectableText>
|
</SelectableText>
|
||||||
@@ -131,7 +131,7 @@ export function FeedItem(props: FeedItemProps) {
|
|||||||
{props.showLastUpdated && (
|
{props.showLastUpdated && (
|
||||||
<SelectableText
|
<SelectableText
|
||||||
selected={() => props.isSelected}
|
selected={() => props.isSelected}
|
||||||
fg={theme.textMuted}
|
tertiary
|
||||||
>
|
>
|
||||||
Updated: {formatDate(props.feed.lastUpdated)}
|
Updated: {formatDate(props.feed.lastUpdated)}
|
||||||
</SelectableText>
|
</SelectableText>
|
||||||
@@ -143,7 +143,7 @@ export function FeedItem(props: FeedItemProps) {
|
|||||||
selected={() => props.isSelected}
|
selected={() => props.isSelected}
|
||||||
paddingLeft={4}
|
paddingLeft={4}
|
||||||
paddingTop={0}
|
paddingTop={0}
|
||||||
fg={theme.textMuted}
|
tertiary
|
||||||
>
|
>
|
||||||
{props.feed.podcast.description.slice(0, 60)}
|
{props.feed.podcast.description.slice(0, 60)}
|
||||||
{props.feed.podcast.description.length > 60 ? "..." : ""}
|
{props.feed.podcast.description.length > 60 ? "..." : ""}
|
||||||
|
|||||||
@@ -80,9 +80,9 @@ export function FeedPage(props: PageProps) {
|
|||||||
<For each={Object.entries(episodesByDate()).sort(([a], [b]) => b.localeCompare(a))}>
|
<For each={Object.entries(episodesByDate()).sort(([a], [b]) => b.localeCompare(a))}>
|
||||||
{([date, episode], groupIndex) => (
|
{([date, episode], groupIndex) => (
|
||||||
<>
|
<>
|
||||||
<box flexDirection="column" gap={0} paddingLeft={1} paddingRight={1} paddingTop={1} paddingBottom={1}>
|
<box flexDirection="column" gap={0} paddingLeft={1} paddingRight={1} paddingTop={1} paddingBottom={1}>
|
||||||
<text fg={theme.primary}>{date}</text>
|
<SelectableText selected={() => false} primary>{date}</SelectableText>
|
||||||
</box>
|
</box>
|
||||||
<SelectableBox
|
<SelectableBox
|
||||||
selected={() => groupIndex() === selectedIndex()}
|
selected={() => groupIndex() === selectedIndex()}
|
||||||
flexDirection="column"
|
flexDirection="column"
|
||||||
@@ -93,23 +93,25 @@ export function FeedPage(props: PageProps) {
|
|||||||
paddingBottom={0}
|
paddingBottom={0}
|
||||||
onMouseDown={() => setSelectedIndex(groupIndex())}
|
onMouseDown={() => setSelectedIndex(groupIndex())}
|
||||||
>
|
>
|
||||||
<SelectableText selected={() => groupIndex() === selectedIndex()}>
|
<SelectableText selected={() => groupIndex() === selectedIndex()} primary>
|
||||||
{groupIndex() === selectedIndex() ? ">" : " "}
|
{groupIndex() === selectedIndex() ? ">" : " "}
|
||||||
</SelectableText>
|
</SelectableText>
|
||||||
<SelectableText
|
<SelectableText
|
||||||
selected={() => groupIndex() === selectedIndex()}
|
selected={() => groupIndex() === selectedIndex()}
|
||||||
fg={theme.text}
|
primary
|
||||||
>
|
>
|
||||||
{episode.episode.title}
|
{episode.episode.title}
|
||||||
</SelectableText>
|
</SelectableText>
|
||||||
<box flexDirection="row" gap={2} paddingLeft={2}>
|
<box flexDirection="row" gap={2} paddingLeft={2}>
|
||||||
<text fg={theme.primary}>{episode.feed.podcast.title}</text>
|
<SelectableText selected={() => groupIndex() === selectedIndex()} primary>
|
||||||
<text fg={theme.textMuted}>
|
{episode.feed.podcast.title}
|
||||||
|
</SelectableText>
|
||||||
|
<SelectableText selected={() => groupIndex() === selectedIndex()} tertiary>
|
||||||
{formatDate(episode.episode.pubDate)}
|
{formatDate(episode.episode.pubDate)}
|
||||||
</text>
|
</SelectableText>
|
||||||
<text fg={theme.textMuted}>
|
<SelectableText selected={() => groupIndex() === selectedIndex()} tertiary>
|
||||||
{formatDuration(episode.episode.duration)}
|
{formatDuration(episode.episode.duration)}
|
||||||
</text>
|
</SelectableText>
|
||||||
</box>
|
</box>
|
||||||
</SelectableBox>
|
</SelectableBox>
|
||||||
</>
|
</>
|
||||||
|
|||||||
@@ -27,19 +27,19 @@ export function ResultCard(props: ResultCardProps) {
|
|||||||
justifyContent="space-between"
|
justifyContent="space-between"
|
||||||
alignItems="center"
|
alignItems="center"
|
||||||
>
|
>
|
||||||
<box flexDirection="row" gap={2} alignItems="center">
|
<box flexDirection="row" gap={2} alignItems="center">
|
||||||
<SelectableText
|
<SelectableText
|
||||||
selected={() => props.selected}
|
selected={() => props.selected}
|
||||||
fg={theme.primary}
|
primary
|
||||||
>
|
>
|
||||||
<strong>{podcast().title}</strong>
|
<strong>{podcast().title}</strong>
|
||||||
</SelectableText>
|
</SelectableText>
|
||||||
<SourceBadge
|
<SourceBadge
|
||||||
sourceId={props.result.sourceId}
|
sourceId={props.result.sourceId}
|
||||||
sourceName={props.result.sourceName}
|
sourceName={props.result.sourceName}
|
||||||
sourceType={props.result.sourceType}
|
sourceType={props.result.sourceType}
|
||||||
/>
|
/>
|
||||||
</box>
|
</box>
|
||||||
<Show when={podcast().isSubscribed}>
|
<Show when={podcast().isSubscribed}>
|
||||||
<text fg={theme.success}>[Subscribed]</text>
|
<text fg={theme.success}>[Subscribed]</text>
|
||||||
</Show>
|
</Show>
|
||||||
|
|||||||
@@ -16,10 +16,18 @@ export const BASE_THEME_COLORS: ThemeColors = {
|
|||||||
secondary: "#a9b1d6",
|
secondary: "#a9b1d6",
|
||||||
accent: "#f6c177",
|
accent: "#f6c177",
|
||||||
text: "#e6edf3",
|
text: "#e6edf3",
|
||||||
|
textPrimary: "#e6edf3",
|
||||||
|
textSecondary: "#a9b1d6",
|
||||||
|
textTertiary: "#7d8590",
|
||||||
|
textSelectedPrimary: "#1b1f27",
|
||||||
|
textSelectedSecondary: "#e6edf3",
|
||||||
|
textSelectedTertiary: "#a9b1d6",
|
||||||
muted: "#7d8590",
|
muted: "#7d8590",
|
||||||
warning: "#f0b429",
|
warning: "#f0b429",
|
||||||
error: "#f47067",
|
error: "#f47067",
|
||||||
success: "#3fb950",
|
success: "#3fb950",
|
||||||
|
_hasSelectedListItemText: true,
|
||||||
|
thinkingOpacity: 0.5,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Base layer backgrounds
|
// Base layer backgrounds
|
||||||
@@ -61,16 +69,22 @@ export const THEMES_DESKTOP: DesktopTheme = {
|
|||||||
secondary: "#cba6f7",
|
secondary: "#cba6f7",
|
||||||
accent: "#f9e2af",
|
accent: "#f9e2af",
|
||||||
text: "#cdd6f4",
|
text: "#cdd6f4",
|
||||||
|
textPrimary: "#cdd6f4",
|
||||||
|
textSecondary: "#cba6f7",
|
||||||
|
textTertiary: "#7f849c",
|
||||||
|
textSelectedPrimary: "#1e1e2e",
|
||||||
|
textSelectedSecondary: "#cdd6f4",
|
||||||
|
textSelectedTertiary: "#cba6f7",
|
||||||
muted: "#7f849c",
|
muted: "#7f849c",
|
||||||
warning: "#fab387",
|
warning: "#fab387",
|
||||||
error: "#f38ba8",
|
error: "#f38ba8",
|
||||||
success: "#a6e3a1",
|
success: "#a6e3a1",
|
||||||
layerBackgrounds: {
|
layerBackgrounds: {
|
||||||
layer0: "transparent",
|
layer0: "transparent",
|
||||||
layer1: "#181825",
|
layer1: "#181825",
|
||||||
layer2: "#11111b",
|
layer2: "#11111b",
|
||||||
layer3: "#0a0a0f",
|
layer3: "#0a0a0f",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -82,6 +96,12 @@ export const THEMES_DESKTOP: DesktopTheme = {
|
|||||||
secondary: "#83a598",
|
secondary: "#83a598",
|
||||||
accent: "#fe8019",
|
accent: "#fe8019",
|
||||||
text: "#ebdbb2",
|
text: "#ebdbb2",
|
||||||
|
textPrimary: "#ebdbb2",
|
||||||
|
textSecondary: "#83a598",
|
||||||
|
textTertiary: "#928374",
|
||||||
|
textSelectedPrimary: "#282828",
|
||||||
|
textSelectedSecondary: "#ebdbb2",
|
||||||
|
textSelectedTertiary: "#83a598",
|
||||||
muted: "#928374",
|
muted: "#928374",
|
||||||
warning: "#fabd2f",
|
warning: "#fabd2f",
|
||||||
error: "#fb4934",
|
error: "#fb4934",
|
||||||
@@ -103,6 +123,12 @@ export const THEMES_DESKTOP: DesktopTheme = {
|
|||||||
secondary: "#bb9af7",
|
secondary: "#bb9af7",
|
||||||
accent: "#e0af68",
|
accent: "#e0af68",
|
||||||
text: "#c0caf5",
|
text: "#c0caf5",
|
||||||
|
textPrimary: "#c0caf5",
|
||||||
|
textSecondary: "#bb9af7",
|
||||||
|
textTertiary: "#565f89",
|
||||||
|
textSelectedPrimary: "#1a1b26",
|
||||||
|
textSelectedSecondary: "#c0caf5",
|
||||||
|
textSelectedTertiary: "#bb9af7",
|
||||||
muted: "#565f89",
|
muted: "#565f89",
|
||||||
warning: "#e0af68",
|
warning: "#e0af68",
|
||||||
error: "#f7768e",
|
error: "#f7768e",
|
||||||
@@ -124,6 +150,12 @@ export const THEMES_DESKTOP: DesktopTheme = {
|
|||||||
secondary: "#81a1c1",
|
secondary: "#81a1c1",
|
||||||
accent: "#ebcb8b",
|
accent: "#ebcb8b",
|
||||||
text: "#eceff4",
|
text: "#eceff4",
|
||||||
|
textPrimary: "#eceff4",
|
||||||
|
textSecondary: "#81a1c1",
|
||||||
|
textTertiary: "#4c566a",
|
||||||
|
textSelectedPrimary: "#2e3440",
|
||||||
|
textSelectedSecondary: "#eceff4",
|
||||||
|
textSelectedTertiary: "#81a1c1",
|
||||||
muted: "#4c566a",
|
muted: "#4c566a",
|
||||||
warning: "#ebcb8b",
|
warning: "#ebcb8b",
|
||||||
error: "#bf616a",
|
error: "#bf616a",
|
||||||
|
|||||||
@@ -23,11 +23,20 @@ export type ThemeColors = {
|
|||||||
secondary: ColorValue;
|
secondary: ColorValue;
|
||||||
accent: ColorValue;
|
accent: ColorValue;
|
||||||
text: ColorValue;
|
text: ColorValue;
|
||||||
|
textPrimary?: ColorValue;
|
||||||
|
textSecondary?: ColorValue;
|
||||||
|
textTertiary?: ColorValue;
|
||||||
|
textSelectedPrimary?: ColorValue;
|
||||||
|
textSelectedSecondary?: ColorValue;
|
||||||
|
textSelectedTertiary?: ColorValue;
|
||||||
muted: ColorValue;
|
muted: ColorValue;
|
||||||
warning: ColorValue;
|
warning: ColorValue;
|
||||||
error: ColorValue;
|
error: ColorValue;
|
||||||
success: ColorValue;
|
success: ColorValue;
|
||||||
layerBackgrounds?: LayerBackgrounds;
|
layerBackgrounds?: LayerBackgrounds;
|
||||||
|
_hasSelectedListItemText?: boolean;
|
||||||
|
thinkingOpacity?: number;
|
||||||
|
selectedListItemText?: ColorValue;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type ThemeVariant = {
|
export type ThemeVariant = {
|
||||||
|
|||||||
@@ -23,4 +23,10 @@ export type ThemeJson = {
|
|||||||
export type ThemeColors = Record<string, RGBA> & {
|
export type ThemeColors = Record<string, RGBA> & {
|
||||||
_hasSelectedListItemText: boolean
|
_hasSelectedListItemText: boolean
|
||||||
thinkingOpacity: number
|
thinkingOpacity: number
|
||||||
|
textPrimary?: ColorValue
|
||||||
|
textSecondary?: ColorValue
|
||||||
|
textTertiary?: ColorValue
|
||||||
|
textSelectedPrimary?: ColorValue
|
||||||
|
textSelectedSecondary?: ColorValue
|
||||||
|
textSelectedTertiary?: ColorValue
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user