will be reworking in just a moment

This commit is contained in:
2026-02-04 23:58:53 -05:00
parent c26150221a
commit 4579659784
4 changed files with 140 additions and 38 deletions

View File

@@ -1,6 +1,7 @@
import { createSignal } from "solid-js"
import { useKeyboard } from "@opentui/solid"
import { useAppStore } from "../stores/app"
import { createColorResolver } from "../utils/color"
import type { ThemeName } from "../types/settings"
type FocusField = "theme" | "font" | "speed" | "explicit" | "auto"
@@ -21,6 +22,8 @@ export function PreferencesPanel() {
const settings = () => appStore.state().settings
const preferences = () => appStore.state().preferences
const resolveColor = createColorResolver(appStore.resolveTheme())
const handleKey = (key: { name: string; shift?: boolean }) => {
if (key.name === "tab") {
const fields: FocusField[] = ["theme", "font", "speed", "explicit", "auto"]
@@ -76,55 +79,55 @@ export function PreferencesPanel() {
return (
<box flexDirection="column" gap={1}>
<text fg="var(--color-muted)">Preferences</text>
<text fg={resolveColor("--color-muted")}>Preferences</text>
<box flexDirection="column" gap={1}>
<box flexDirection="row" gap={1} alignItems="center">
<text fg={focusField() === "theme" ? "var(--color-primary)" : "var(--color-muted)"}>Theme:</text>
<text fg={focusField() === "theme" ? resolveColor("--color-primary") : resolveColor("--color-muted")}>Theme:</text>
<box border padding={0}>
<text fg="var(--color-text)">{THEME_LABELS.find((t) => t.value === settings().theme)?.label}</text>
<text fg={resolveColor("--color-text")}>{THEME_LABELS.find((t) => t.value === settings().theme)?.label}</text>
</box>
<text fg="var(--color-muted)">[Left/Right]</text>
<text fg={resolveColor("--color-muted")}>[Left/Right]</text>
</box>
<box flexDirection="row" gap={1} alignItems="center">
<text fg={focusField() === "font" ? "var(--color-primary)" : "var(--color-muted)"}>Font Size:</text>
<text fg={focusField() === "font" ? resolveColor("--color-primary") : resolveColor("--color-muted")}>Font Size:</text>
<box border padding={0}>
<text fg="var(--color-text)">{settings().fontSize}px</text>
<text fg={resolveColor("--color-text")}>{settings().fontSize}px</text>
</box>
<text fg="var(--color-muted)">[Left/Right]</text>
<text fg={resolveColor("--color-muted")}>[Left/Right]</text>
</box>
<box flexDirection="row" gap={1} alignItems="center">
<text fg={focusField() === "speed" ? "var(--color-primary)" : "var(--color-muted)"}>Playback:</text>
<text fg={focusField() === "speed" ? resolveColor("--color-primary") : resolveColor("--color-muted")}>Playback:</text>
<box border padding={0}>
<text fg="var(--color-text)">{settings().playbackSpeed}x</text>
<text fg={resolveColor("--color-text")}>{settings().playbackSpeed}x</text>
</box>
<text fg="var(--color-muted)">[Left/Right]</text>
<text fg={resolveColor("--color-muted")}>[Left/Right]</text>
</box>
<box flexDirection="row" gap={1} alignItems="center">
<text fg={focusField() === "explicit" ? "var(--color-primary)" : "var(--color-muted)"}>Show Explicit:</text>
<text fg={focusField() === "explicit" ? resolveColor("--color-primary") : resolveColor("--color-muted")}>Show Explicit:</text>
<box border padding={0}>
<text fg={preferences().showExplicit ? "var(--color-success)" : "var(--color-muted)"}>
<text fg={preferences().showExplicit ? resolveColor("--color-success") : resolveColor("--color-muted")}>
{preferences().showExplicit ? "On" : "Off"}
</text>
</box>
<text fg="var(--color-muted)">[Space]</text>
<text fg={resolveColor("--color-muted")}>[Space]</text>
</box>
<box flexDirection="row" gap={1} alignItems="center">
<text fg={focusField() === "auto" ? "var(--color-primary)" : "var(--color-muted)"}>Auto Download:</text>
<text fg={focusField() === "auto" ? resolveColor("--color-primary") : resolveColor("--color-muted")}>Auto Download:</text>
<box border padding={0}>
<text fg={preferences().autoDownload ? "var(--color-success)" : "var(--color-muted)"}>
<text fg={preferences().autoDownload ? resolveColor("--color-success") : resolveColor("--color-muted")}>
{preferences().autoDownload ? "On" : "Off"}
</text>
</box>
<text fg="var(--color-muted)">[Space]</text>
<text fg={resolveColor("--color-muted")}>[Space]</text>
</box>
</box>
<text fg="var(--color-muted)">Tab to move focus, Left/Right to adjust</text>
<text fg={resolveColor("--color-muted")}>Tab to move focus, Left/Right to adjust</text>
</box>
)
}

View File

@@ -6,6 +6,7 @@
import { createSignal, For } from "solid-js"
import { useFeedStore } from "../stores/feed"
import { SourceType } from "../types/source"
import { createColorResolver } from "../utils/color"
import type { PodcastSource } from "../types/source"
interface SourceManagerProps {
@@ -148,6 +149,8 @@ export function SourceManager(props: SourceManagerProps) {
const sourceExplicit = () => selectedSource()?.allowExplicit !== false
const sourceLanguage = () => selectedSource()?.language || "en_us"
const resolveColor = createColorResolver({})
return (
<box flexDirection="column" border padding={1} gap={1}>
<box flexDirection="row" justifyContent="space-between">
@@ -155,15 +158,15 @@ export function SourceManager(props: SourceManagerProps) {
<strong>Podcast Sources</strong>
</text>
<box border padding={0} onMouseDown={props.onClose}>
<text fg="var(--color-primary)">[Esc] Close</text>
<text fg={resolveColor("--color-primary")}>[Esc] Close</text>
</box>
</box>
<text fg="var(--color-muted)">Manage where to search for podcasts</text>
<text fg={resolveColor("--color-muted")}>Manage where to search for podcasts</text>
{/* Source list */}
<box border padding={1} flexDirection="column" gap={1}>
<text fg={focusArea() === "list" ? "var(--color-primary)" : "var(--color-muted)"}>Sources:</text>
<text fg={focusArea() === "list" ? resolveColor("--color-primary") : resolveColor("--color-muted")}>Sources:</text>
<scrollbox height={6}>
<For each={sources()}>
{(source, index) => (
@@ -173,7 +176,7 @@ export function SourceManager(props: SourceManagerProps) {
padding={0}
backgroundColor={
focusArea() === "list" && index() === selectedIndex()
? "var(--color-primary)"
? resolveColor("--color-primary")
: undefined
}
onMouseDown={() => {
@@ -184,21 +187,21 @@ export function SourceManager(props: SourceManagerProps) {
>
<text fg={
focusArea() === "list" && index() === selectedIndex()
? "var(--color-primary)"
: "var(--color-muted)"
? resolveColor("--color-primary")
: resolveColor("--color-muted")
}>
{focusArea() === "list" && index() === selectedIndex()
? ">"
: " "}
</text>
<text fg={source.enabled ? "var(--color-success)" : "var(--color-error)"}>
<text fg={source.enabled ? resolveColor("--color-success") : resolveColor("--color-error")}>
{source.enabled ? "[x]" : "[ ]"}
</text>
<text fg="var(--color-accent)">{getSourceIcon(source)}</text>
<text fg={resolveColor("--color-accent")}>{getSourceIcon(source)}</text>
<text
fg={
focusArea() === "list" && index() === selectedIndex()
? "var(--color-text)"
? resolveColor("--color-text")
: undefined
}
>
@@ -208,7 +211,7 @@ export function SourceManager(props: SourceManagerProps) {
)}
</For>
</scrollbox>
<text fg="var(--color-muted)">Space/Enter to toggle, d to delete, a to add</text>
<text fg={resolveColor("--color-muted")}>Space/Enter to toggle, d to delete, a to add</text>
{/* API settings */}
<box flexDirection="column" gap={1}>

View File

@@ -42,6 +42,8 @@ export function ThemeProvider({ children }: { children: any }) {
// Handle system theme changes
createEffect(() => {
if (isSystemTheme()) {
// Check if window and matchMedia are available
if (typeof window !== "undefined" && typeof window.matchMedia === "function") {
const mediaQuery = window.matchMedia("(prefers-color-scheme: dark)")
const handler = () => {
const newMode = getSystemThemeMode()
@@ -55,6 +57,7 @@ export function ThemeProvider({ children }: { children: any }) {
mediaQuery.removeEventListener("change", handler)
})
}
}
})
return (

93
src/utils/color.ts Normal file
View File

@@ -0,0 +1,93 @@
/**
* Color Resolution Utility
* Converts CSS variable references to actual color values
*/
/**
* Map of CSS variable names to their default color values
* Used as fallback when CSS variables aren't resolved
*/
const CSS_VARIABLE_MAP: Record<string, string> = {
"--color-background": "transparent",
"--color-surface": "#1b1f27",
"--color-primary": "#6fa8ff",
"--color-secondary": "#a9b1d6",
"--color-accent": "#f6c177",
"--color-text": "#e6edf3",
"--color-muted": "#7d8590",
"--color-warning": "#f0b429",
"--color-error": "#f47067",
"--color-success": "#3fb950",
"--color-layer0": "transparent",
"--color-layer1": "#1e222e",
"--color-layer2": "#161b22",
"--color-layer3": "#0d1117",
}
/**
* Resolves a CSS variable reference to an actual color value
* @param variable The CSS variable string (e.g., "var(--color-primary)")
* @returns The resolved color value or a default fallback
*/
export function resolveCSSVariable(variable: string): string {
if (!variable || typeof variable !== "string") {
return "#ff00ff" // Default magenta fallback
}
// Extract the variable name from var(--name)
const match = variable.match(/var\(([^)]+)\)/)
if (match && match[1]) {
const varName = match[1].trim()
// Get the computed style value from the document
if (typeof document !== "undefined") {
const root = document.documentElement
const computedValue = getComputedStyle(root).getPropertyValue(varName)
if (computedValue) {
return computedValue.trim()
}
}
// Fall back to the map
return CSS_VARIABLE_MAP[varName] || "#ff00ff"
}
// Return the original value if it's not a CSS variable
return variable
}
/**
* Creates a function that resolves CSS variables from a theme object
* @param theme The theme object with color properties
* @returns A function that resolves CSS variable strings to actual colors
*/
export function createColorResolver(theme: {
background?: string
surface?: string
primary?: string
secondary?: string
accent?: string
text?: string
muted?: string
warning?: string
error?: string
success?: string
layerBackgrounds?: {
layer0?: string
layer1?: string
layer2?: string
layer3?: string
}
}) {
return (color: string | undefined): string => {
if (!color) {
return "#ff00ff" // Default magenta fallback
}
// If it's already a valid hex or named color, return it
if (/^#([0-9A-Fa-f]{3}|[0-9A-Fa-f]{6})$/.test(color) || /^[a-z]+$/i.test(color)) {
return color
}
// Otherwise, try to resolve it as a CSS variable
return resolveCSSVariable(color)
}
}