diff --git a/src/components/PreferencesPanel.tsx b/src/components/PreferencesPanel.tsx
index 509a2ea..bff2914 100644
--- a/src/components/PreferencesPanel.tsx
+++ b/src/components/PreferencesPanel.tsx
@@ -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 (
- Preferences
+ Preferences
- Theme:
+ Theme:
- {THEME_LABELS.find((t) => t.value === settings().theme)?.label}
+ {THEME_LABELS.find((t) => t.value === settings().theme)?.label}
- [Left/Right]
+ [Left/Right]
- Font Size:
+ Font Size:
- {settings().fontSize}px
+ {settings().fontSize}px
- [Left/Right]
+ [Left/Right]
- Playback:
+ Playback:
- {settings().playbackSpeed}x
+ {settings().playbackSpeed}x
- [Left/Right]
+ [Left/Right]
- Show Explicit:
+ Show Explicit:
-
+
{preferences().showExplicit ? "On" : "Off"}
- [Space]
+ [Space]
- Auto Download:
+ Auto Download:
-
+
{preferences().autoDownload ? "On" : "Off"}
- [Space]
+ [Space]
- Tab to move focus, Left/Right to adjust
+ Tab to move focus, Left/Right to adjust
)
}
diff --git a/src/components/SourceManager.tsx b/src/components/SourceManager.tsx
index 903c75a..7b1831a 100644
--- a/src/components/SourceManager.tsx
+++ b/src/components/SourceManager.tsx
@@ -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 (
@@ -155,15 +158,15 @@ export function SourceManager(props: SourceManagerProps) {
Podcast Sources
- [Esc] Close
+ [Esc] Close
- Manage where to search for podcasts
+ Manage where to search for podcasts
{/* Source list */}
- Sources:
+ 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) {
>
{focusArea() === "list" && index() === selectedIndex()
? ">"
: " "}
-
+
{source.enabled ? "[x]" : "[ ]"}
- {getSourceIcon(source)}
+ {getSourceIcon(source)}
@@ -208,7 +211,7 @@ export function SourceManager(props: SourceManagerProps) {
)}
- Space/Enter to toggle, d to delete, a to add
+ Space/Enter to toggle, d to delete, a to add
{/* API settings */}
diff --git a/src/context/ThemeContext.tsx b/src/context/ThemeContext.tsx
index 5b2484a..42b73e6 100644
--- a/src/context/ThemeContext.tsx
+++ b/src/context/ThemeContext.tsx
@@ -42,18 +42,21 @@ export function ThemeProvider({ children }: { children: any }) {
// Handle system theme changes
createEffect(() => {
if (isSystemTheme()) {
- const mediaQuery = window.matchMedia("(prefers-color-scheme: dark)")
- const handler = () => {
- const newMode = getSystemThemeMode()
- setCurrentMode(newMode)
- setResolvedTheme(appStore.resolveTheme())
+ // 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()
+ setCurrentMode(newMode)
+ setResolvedTheme(appStore.resolveTheme())
+ }
+
+ mediaQuery.addEventListener("change", handler)
+
+ onCleanup(() => {
+ mediaQuery.removeEventListener("change", handler)
+ })
}
-
- mediaQuery.addEventListener("change", handler)
-
- onCleanup(() => {
- mediaQuery.removeEventListener("change", handler)
- })
}
})
diff --git a/src/utils/color.ts b/src/utils/color.ts
new file mode 100644
index 0000000..91e921c
--- /dev/null
+++ b/src/utils/color.ts
@@ -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 = {
+ "--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)
+ }
+}