codeblog-app 1.5.0 → 1.5.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://json.schemastore.org/package.json",
3
3
  "name": "codeblog-app",
4
- "version": "1.5.0",
4
+ "version": "1.5.2",
5
5
  "description": "CLI client for CodeBlog — the forum where AI writes the posts",
6
6
  "type": "module",
7
7
  "license": "MIT",
@@ -56,11 +56,11 @@
56
56
  "typescript": "5.8.2"
57
57
  },
58
58
  "optionalDependencies": {
59
- "codeblog-app-darwin-arm64": "1.5.0",
60
- "codeblog-app-darwin-x64": "1.5.0",
61
- "codeblog-app-linux-arm64": "1.5.0",
62
- "codeblog-app-linux-x64": "1.5.0",
63
- "codeblog-app-windows-x64": "1.5.0"
59
+ "codeblog-app-darwin-arm64": "1.5.2",
60
+ "codeblog-app-darwin-x64": "1.5.2",
61
+ "codeblog-app-linux-arm64": "1.5.2",
62
+ "codeblog-app-linux-x64": "1.5.2",
63
+ "codeblog-app-windows-x64": "1.5.2"
64
64
  },
65
65
  "dependencies": {
66
66
  "@ai-sdk/amazon-bedrock": "^4.0.60",
@@ -67,6 +67,9 @@ export const UpdateCommand: CommandModule = {
67
67
  if (os !== "windows") {
68
68
  await fs.chmod(bin, 0o755)
69
69
  }
70
+ if (os === "darwin") {
71
+ Bun.spawn(["codesign", "--sign", "-", "--force", bin], { stdout: "ignore", stderr: "ignore" })
72
+ }
70
73
 
71
74
  await fs.rm(tmp, { recursive: true, force: true })
72
75
 
package/src/tui/app.tsx CHANGED
@@ -5,6 +5,7 @@ import { ExitProvider, useExit } from "./context/exit"
5
5
  import { ThemeProvider, useTheme } from "./context/theme"
6
6
  import { Home } from "./routes/home"
7
7
  import { Chat } from "./routes/chat"
8
+ import { ThemeSetup, ThemePicker } from "./routes/setup"
8
9
 
9
10
  import pkg from "../../package.json"
10
11
  const VERSION = pkg.version
@@ -98,6 +99,9 @@ function App() {
98
99
  return (
99
100
  <box flexDirection="column" width={dimensions().width} height={dimensions().height}>
100
101
  <Switch>
102
+ <Match when={theme.needsSetup}>
103
+ <ThemeSetup />
104
+ </Match>
101
105
  <Match when={route.data.type === "home"}>
102
106
  <Home
103
107
  loggedIn={loggedIn()}
@@ -119,6 +123,9 @@ function App() {
119
123
  <Match when={route.data.type === "chat"}>
120
124
  <Chat />
121
125
  </Match>
126
+ <Match when={route.data.type === "theme"}>
127
+ <ThemePicker onDone={() => route.navigate({ type: "home" })} />
128
+ </Match>
122
129
  </Switch>
123
130
 
124
131
  {/* Status bar */}
@@ -8,7 +8,9 @@ export type SearchRoute = { type: "search"; query: string }
8
8
  export type TrendingRoute = { type: "trending" }
9
9
  export type NotificationsRoute = { type: "notifications" }
10
10
 
11
- export type Route = HomeRoute | ChatRoute | PostRoute | SearchRoute | TrendingRoute | NotificationsRoute
11
+ export type ThemeRoute = { type: "theme" }
12
+
13
+ export type Route = HomeRoute | ChatRoute | PostRoute | SearchRoute | TrendingRoute | NotificationsRoute | ThemeRoute
12
14
 
13
15
  export const { use: useRoute, provider: RouteProvider } = createSimpleContext({
14
16
  name: "Route",
@@ -1,5 +1,8 @@
1
+ import path from "path"
2
+ import fs from "fs"
1
3
  import { createStore } from "solid-js/store"
2
4
  import { createSimpleContext } from "./helper"
5
+ import { Global } from "../../global"
3
6
 
4
7
  export type ThemeColors = {
5
8
  text: string
@@ -223,6 +226,180 @@ const solarized: ThemeDef = {
223
226
  },
224
227
  }
225
228
 
229
+ const catppuccin: ThemeDef = {
230
+ dark: {
231
+ text: "#cdd6f4",
232
+ textMuted: "#6c7086",
233
+ primary: "#89b4fa",
234
+ accent: "#cba6f7",
235
+ success: "#a6e3a1",
236
+ error: "#f38ba8",
237
+ warning: "#f9e2af",
238
+ input: "#cdd6f4",
239
+ cursor: "#6c7086",
240
+ logo1: "#cba6f7",
241
+ logo2: "#89b4fa",
242
+ },
243
+ light: {
244
+ text: "#4c4f69",
245
+ textMuted: "#8c8fa1",
246
+ primary: "#1e66f5",
247
+ accent: "#8839ef",
248
+ success: "#40a02b",
249
+ error: "#d20f39",
250
+ warning: "#df8e1d",
251
+ input: "#4c4f69",
252
+ cursor: "#8c8fa1",
253
+ logo1: "#8839ef",
254
+ logo2: "#1e66f5",
255
+ },
256
+ }
257
+
258
+ const rosepine: ThemeDef = {
259
+ dark: {
260
+ text: "#e0def4",
261
+ textMuted: "#6e6a86",
262
+ primary: "#c4a7e7",
263
+ accent: "#ebbcba",
264
+ success: "#31748f",
265
+ error: "#eb6f92",
266
+ warning: "#f6c177",
267
+ input: "#e0def4",
268
+ cursor: "#6e6a86",
269
+ logo1: "#ebbcba",
270
+ logo2: "#c4a7e7",
271
+ },
272
+ light: {
273
+ text: "#575279",
274
+ textMuted: "#9893a5",
275
+ primary: "#907aa9",
276
+ accent: "#d7827e",
277
+ success: "#286983",
278
+ error: "#b4637a",
279
+ warning: "#ea9d34",
280
+ input: "#575279",
281
+ cursor: "#9893a5",
282
+ logo1: "#d7827e",
283
+ logo2: "#907aa9",
284
+ },
285
+ }
286
+
287
+ const gruvbox: ThemeDef = {
288
+ dark: {
289
+ text: "#ebdbb2",
290
+ textMuted: "#928374",
291
+ primary: "#83a598",
292
+ accent: "#d3869b",
293
+ success: "#b8bb26",
294
+ error: "#fb4934",
295
+ warning: "#fabd2f",
296
+ input: "#ebdbb2",
297
+ cursor: "#928374",
298
+ logo1: "#fe8019",
299
+ logo2: "#83a598",
300
+ },
301
+ light: {
302
+ text: "#3c3836",
303
+ textMuted: "#928374",
304
+ primary: "#076678",
305
+ accent: "#8f3f71",
306
+ success: "#79740e",
307
+ error: "#9d0006",
308
+ warning: "#b57614",
309
+ input: "#3c3836",
310
+ cursor: "#928374",
311
+ logo1: "#af3a03",
312
+ logo2: "#076678",
313
+ },
314
+ }
315
+
316
+ const onedark: ThemeDef = {
317
+ dark: {
318
+ text: "#abb2bf",
319
+ textMuted: "#5c6370",
320
+ primary: "#61afef",
321
+ accent: "#c678dd",
322
+ success: "#98c379",
323
+ error: "#e06c75",
324
+ warning: "#e5c07b",
325
+ input: "#abb2bf",
326
+ cursor: "#5c6370",
327
+ logo1: "#e06c75",
328
+ logo2: "#61afef",
329
+ },
330
+ light: {
331
+ text: "#383a42",
332
+ textMuted: "#a0a1a7",
333
+ primary: "#4078f2",
334
+ accent: "#a626a4",
335
+ success: "#50a14f",
336
+ error: "#e45649",
337
+ warning: "#c18401",
338
+ input: "#383a42",
339
+ cursor: "#a0a1a7",
340
+ logo1: "#e45649",
341
+ logo2: "#4078f2",
342
+ },
343
+ }
344
+
345
+ const kanagawa: ThemeDef = {
346
+ dark: {
347
+ text: "#dcd7ba",
348
+ textMuted: "#727169",
349
+ primary: "#7e9cd8",
350
+ accent: "#957fb8",
351
+ success: "#76946a",
352
+ error: "#c34043",
353
+ warning: "#dca561",
354
+ input: "#dcd7ba",
355
+ cursor: "#727169",
356
+ logo1: "#ff5d62",
357
+ logo2: "#7e9cd8",
358
+ },
359
+ light: {
360
+ text: "#1f1f28",
361
+ textMuted: "#8a8980",
362
+ primary: "#4e8ca2",
363
+ accent: "#624c83",
364
+ success: "#6f894e",
365
+ error: "#c84053",
366
+ warning: "#cc6d00",
367
+ input: "#1f1f28",
368
+ cursor: "#8a8980",
369
+ logo1: "#d7474b",
370
+ logo2: "#4e8ca2",
371
+ },
372
+ }
373
+
374
+ const everforest: ThemeDef = {
375
+ dark: {
376
+ text: "#d3c6aa",
377
+ textMuted: "#859289",
378
+ primary: "#7fbbb3",
379
+ accent: "#d699b6",
380
+ success: "#a7c080",
381
+ error: "#e67e80",
382
+ warning: "#dbbc7f",
383
+ input: "#d3c6aa",
384
+ cursor: "#859289",
385
+ logo1: "#e69875",
386
+ logo2: "#7fbbb3",
387
+ },
388
+ light: {
389
+ text: "#5c6a72",
390
+ textMuted: "#939f91",
391
+ primary: "#3a94c5",
392
+ accent: "#df69ba",
393
+ success: "#8da101",
394
+ error: "#f85552",
395
+ warning: "#dfa000",
396
+ input: "#5c6a72",
397
+ cursor: "#939f91",
398
+ logo1: "#f57d26",
399
+ logo2: "#3a94c5",
400
+ },
401
+ }
402
+
226
403
  export const THEMES: Record<string, ThemeDef> = {
227
404
  codeblog,
228
405
  dracula,
@@ -231,26 +408,40 @@ export const THEMES: Record<string, ThemeDef> = {
231
408
  monokai,
232
409
  github,
233
410
  solarized,
411
+ catppuccin,
412
+ rosepine,
413
+ gruvbox,
414
+ onedark,
415
+ kanagawa,
416
+ everforest,
234
417
  }
235
418
 
236
419
  export const THEME_NAMES = Object.keys(THEMES)
237
420
 
238
- function detect(): "dark" | "light" {
239
- const env = process.env.COLORFGBG
240
- if (env) {
241
- const parts = env.split(";")
242
- const bg = parseInt(parts[parts.length - 1] || "0", 10)
243
- if (bg > 6 && bg !== 8) return "light"
244
- }
245
- return "dark"
421
+ const configPath = path.join(Global.Path.config, "theme.json")
422
+
423
+ type SavedTheme = { name: string; mode: "dark" | "light" }
424
+
425
+ function load(): SavedTheme | null {
426
+ try {
427
+ const data = JSON.parse(fs.readFileSync(configPath, "utf-8"))
428
+ if (data.name && data.mode) return data as SavedTheme
429
+ } catch {}
430
+ return null
431
+ }
432
+
433
+ function save(cfg: SavedTheme) {
434
+ Bun.write(configPath, JSON.stringify(cfg, null, 2)).catch(() => {})
246
435
  }
247
436
 
248
437
  export const { use: useTheme, provider: ThemeProvider } = createSimpleContext({
249
438
  name: "Theme",
250
439
  init: () => {
440
+ const saved = load()
251
441
  const [store, setStore] = createStore({
252
- name: "codeblog" as string,
253
- mode: detect() as "dark" | "light",
442
+ name: saved?.name || "codeblog",
443
+ mode: (saved?.mode || "dark") as "dark" | "light",
444
+ needsSetup: !saved,
254
445
  })
255
446
 
256
447
  return {
@@ -260,14 +451,19 @@ export const { use: useTheme, provider: ThemeProvider } = createSimpleContext({
260
451
  },
261
452
  get name() { return store.name },
262
453
  get mode() { return store.mode },
454
+ get needsSetup() { return store.needsSetup },
263
455
  set(name: string) {
264
- if (THEMES[name]) setStore("name", name)
265
- },
266
- toggle() {
267
- setStore("mode", store.mode === "dark" ? "light" : "dark")
456
+ if (!THEMES[name]) return
457
+ setStore("name", name)
458
+ save({ name, mode: store.mode })
268
459
  },
269
460
  setMode(mode: "dark" | "light") {
270
461
  setStore("mode", mode)
462
+ save({ name: store.name, mode })
463
+ },
464
+ finishSetup() {
465
+ setStore("needsSetup", false)
466
+ save({ name: store.name, mode: store.mode })
271
467
  },
272
468
  }
273
469
  },
@@ -2,7 +2,7 @@ import { createSignal, Show } from "solid-js"
2
2
  import { useKeyboard } from "@opentui/solid"
3
3
  import { useRoute } from "../context/route"
4
4
  import { useExit } from "../context/exit"
5
- import { useTheme, THEME_NAMES } from "../context/theme"
5
+ import { useTheme } from "../context/theme"
6
6
 
7
7
  const LOGO = [
8
8
  " ██████╗ ██████╗ ██████╗ ███████╗██████╗ ██╗ ██████╗ ██████╗ ",
@@ -27,7 +27,7 @@ const HELP_TEXT = [
27
27
  " /notifications View notifications",
28
28
  " /dashboard Your stats",
29
29
  " /models List available AI models",
30
- " /theme [name] Switch theme (codeblog, dracula, nord, tokyonight, monokai, github, solarized)",
30
+ " /theme [name] Switch theme (codeblog, dracula, nord, tokyonight, monokai, github, solarized, catppuccin, rosepine, gruvbox, onedark, kanagawa, everforest)",
31
31
  " /dark | /light Toggle dark/light mode",
32
32
  " /help Show this help",
33
33
  " /exit Exit",
@@ -82,30 +82,10 @@ export function Home(props: {
82
82
  return
83
83
  }
84
84
 
85
- if (cmd === "/theme") {
86
- const name = parts[1]
87
- if (!name) {
88
- showMsg(`Theme: ${theme.name} (${theme.mode}) | Available: ${THEME_NAMES.join(", ")}`, theme.colors.text)
89
- return
90
- }
91
- if (THEME_NAMES.includes(name)) {
92
- theme.set(name)
93
- showMsg(`Theme set to ${name}`, theme.colors.success)
94
- } else {
95
- showMsg(`Unknown theme: ${name}. Available: ${THEME_NAMES.join(", ")}`, theme.colors.error)
96
- }
97
- return
98
- }
99
-
100
- if (cmd === "/dark") {
101
- theme.setMode("dark")
102
- showMsg("Switched to dark mode", theme.colors.success)
103
- return
104
- }
105
-
106
- if (cmd === "/light") {
107
- theme.setMode("light")
108
- showMsg("Switched to light mode", theme.colors.success)
85
+ if (cmd === "/theme" || cmd === "/dark" || cmd === "/light") {
86
+ if (cmd === "/dark") theme.setMode("dark")
87
+ if (cmd === "/light") theme.setMode("light")
88
+ route.navigate({ type: "theme" })
109
89
  return
110
90
  }
111
91
 
@@ -0,0 +1,255 @@
1
+ import { createSignal } from "solid-js"
2
+ import { useKeyboard } from "@opentui/solid"
3
+ import { useTheme, THEME_NAMES, THEMES } from "../context/theme"
4
+
5
+ // High-contrast colors that are visible on ANY terminal background
6
+ const HC = {
7
+ title: "#ff6600",
8
+ text: "#888888",
9
+ selected: "#00cc00",
10
+ dim: "#999999",
11
+ }
12
+
13
+ export function ThemeSetup() {
14
+ const theme = useTheme()
15
+ const modes = ["dark", "light"] as const
16
+ const [step, setStep] = createSignal<"mode" | "theme">("mode")
17
+ const [modeIdx, setModeIdx] = createSignal(0)
18
+ const [themeIdx, setThemeIdx] = createSignal(0)
19
+
20
+ useKeyboard((evt) => {
21
+ if (step() === "mode") {
22
+ if (evt.name === "up" || evt.name === "k") {
23
+ setModeIdx((i) => (i - 1 + modes.length) % modes.length)
24
+ evt.preventDefault()
25
+ return
26
+ }
27
+ if (evt.name === "down" || evt.name === "j") {
28
+ setModeIdx((i) => (i + 1) % modes.length)
29
+ evt.preventDefault()
30
+ return
31
+ }
32
+ if (evt.name === "return") {
33
+ theme.setMode(modes[modeIdx()])
34
+ setStep("theme")
35
+ evt.preventDefault()
36
+ return
37
+ }
38
+ }
39
+
40
+ if (step() === "theme") {
41
+ if (evt.name === "up" || evt.name === "k") {
42
+ const next = (themeIdx() - 1 + THEME_NAMES.length) % THEME_NAMES.length
43
+ setThemeIdx(next)
44
+ theme.set(THEME_NAMES[next])
45
+ evt.preventDefault()
46
+ return
47
+ }
48
+ if (evt.name === "down" || evt.name === "j") {
49
+ const next = (themeIdx() + 1) % THEME_NAMES.length
50
+ setThemeIdx(next)
51
+ theme.set(THEME_NAMES[next])
52
+ evt.preventDefault()
53
+ return
54
+ }
55
+ if (evt.name === "return") {
56
+ theme.finishSetup()
57
+ evt.preventDefault()
58
+ return
59
+ }
60
+ if (evt.name === "escape") {
61
+ setStep("mode")
62
+ evt.preventDefault()
63
+ return
64
+ }
65
+ }
66
+ })
67
+
68
+ return (
69
+ <box flexDirection="column" flexGrow={1} alignItems="center" paddingLeft={2} paddingRight={2}>
70
+ <box flexGrow={1} minHeight={0} />
71
+
72
+ <box flexShrink={0} flexDirection="column" alignItems="center">
73
+ <text fg={HC.title}>
74
+ <span style={{ bold: true }}>{"★ Welcome to CodeBlog ★"}</span>
75
+ </text>
76
+ <box height={1} />
77
+ </box>
78
+
79
+ {step() === "mode" ? (
80
+ <box flexShrink={0} flexDirection="column" width="100%" maxWidth={50}>
81
+ <text fg={HC.title}>
82
+ <span style={{ bold: true }}>{"What is your terminal background color?"}</span>
83
+ </text>
84
+ <box height={1} />
85
+ {modes.map((m, i) => (
86
+ <box flexDirection="row" paddingLeft={2}>
87
+ <text fg={modeIdx() === i ? HC.selected : HC.dim}>
88
+ {modeIdx() === i ? "❯ " : " "}
89
+ </text>
90
+ <text fg={modeIdx() === i ? HC.selected : HC.dim}>
91
+ <span style={{ bold: modeIdx() === i }}>
92
+ {m === "dark" ? "Dark background (black/dark terminal)" : "Light background (white/light terminal)"}
93
+ </span>
94
+ </text>
95
+ </box>
96
+ ))}
97
+ <box height={1} />
98
+ <text fg={HC.text}>{"↑↓ select · Enter confirm"}</text>
99
+ </box>
100
+ ) : (
101
+ <box flexShrink={0} flexDirection="column" width="100%" maxWidth={60}>
102
+ <text fg={theme.colors.text}>
103
+ <span style={{ bold: true }}>{"Choose a color theme:"}</span>
104
+ </text>
105
+ <box height={1} />
106
+ {THEME_NAMES.map((name, i) => {
107
+ const c = THEMES[name][theme.mode]
108
+ return (
109
+ <box flexDirection="row" paddingLeft={2}>
110
+ <text fg={themeIdx() === i ? c.primary : theme.colors.textMuted}>
111
+ {themeIdx() === i ? "❯ " : " "}
112
+ </text>
113
+ <text fg={themeIdx() === i ? c.text : theme.colors.textMuted}>
114
+ <span style={{ bold: themeIdx() === i }}>
115
+ {name.padEnd(14)}
116
+ </span>
117
+ </text>
118
+ <text fg={c.logo1}>{" ●"}</text>
119
+ <text fg={c.logo2}>{"●"}</text>
120
+ <text fg={c.primary}>{"●"}</text>
121
+ <text fg={c.accent}>{"●"}</text>
122
+ <text fg={c.success}>{"●"}</text>
123
+ <text fg={c.error}>{"●"}</text>
124
+ </box>
125
+ )
126
+ })}
127
+ <box height={1} />
128
+ <text fg={theme.colors.textMuted}>{"↑↓ select · Enter confirm · Esc back"}</text>
129
+ </box>
130
+ )}
131
+
132
+ <box flexGrow={1} minHeight={0} />
133
+ </box>
134
+ )
135
+ }
136
+
137
+ export function ThemePicker(props: { onDone: () => void }) {
138
+ const theme = useTheme()
139
+ const [idx, setIdx] = createSignal(THEME_NAMES.indexOf(theme.name))
140
+ const [tab, setTab] = createSignal<"theme" | "mode">("theme")
141
+
142
+ useKeyboard((evt) => {
143
+ if (tab() === "theme") {
144
+ if (evt.name === "up" || evt.name === "k") {
145
+ const next = (idx() - 1 + THEME_NAMES.length) % THEME_NAMES.length
146
+ setIdx(next)
147
+ theme.set(THEME_NAMES[next])
148
+ evt.preventDefault()
149
+ return
150
+ }
151
+ if (evt.name === "down" || evt.name === "j") {
152
+ const next = (idx() + 1) % THEME_NAMES.length
153
+ setIdx(next)
154
+ theme.set(THEME_NAMES[next])
155
+ evt.preventDefault()
156
+ return
157
+ }
158
+ if (evt.name === "tab") {
159
+ setTab("mode")
160
+ evt.preventDefault()
161
+ return
162
+ }
163
+ if (evt.name === "return" || evt.name === "escape") {
164
+ props.onDone()
165
+ evt.preventDefault()
166
+ return
167
+ }
168
+ }
169
+
170
+ if (tab() === "mode") {
171
+ if (evt.name === "up" || evt.name === "down" || evt.name === "k" || evt.name === "j") {
172
+ theme.setMode(theme.mode === "dark" ? "light" : "dark")
173
+ evt.preventDefault()
174
+ return
175
+ }
176
+ if (evt.name === "tab") {
177
+ setTab("theme")
178
+ evt.preventDefault()
179
+ return
180
+ }
181
+ if (evt.name === "return" || evt.name === "escape") {
182
+ props.onDone()
183
+ evt.preventDefault()
184
+ return
185
+ }
186
+ }
187
+ })
188
+
189
+ return (
190
+ <box flexDirection="column" flexGrow={1} paddingLeft={2} paddingRight={2} paddingTop={1}>
191
+ <box flexDirection="row" gap={4} flexShrink={0}>
192
+ <text fg={theme.colors.primary}>
193
+ <span style={{ bold: true }}>Theme Settings</span>
194
+ </text>
195
+ <box flexGrow={1} />
196
+ <text fg={theme.colors.textMuted}>{"Tab: switch section · Enter/Esc: done"}</text>
197
+ </box>
198
+
199
+ <box flexDirection="row" flexGrow={1} paddingTop={1} gap={4}>
200
+ {/* Theme list */}
201
+ <box flexDirection="column" width={40}>
202
+ <text fg={tab() === "theme" ? theme.colors.text : theme.colors.textMuted}>
203
+ <span style={{ bold: true }}>{"Color Theme"}</span>
204
+ </text>
205
+ <box height={1} />
206
+ {THEME_NAMES.map((name, i) => {
207
+ const c = THEMES[name][theme.mode]
208
+ return (
209
+ <box flexDirection="row">
210
+ <text fg={idx() === i ? c.primary : theme.colors.textMuted}>
211
+ {idx() === i && tab() === "theme" ? "❯ " : " "}
212
+ </text>
213
+ <text fg={idx() === i ? c.text : theme.colors.textMuted}>
214
+ <span style={{ bold: idx() === i }}>
215
+ {name.padEnd(14)}
216
+ </span>
217
+ </text>
218
+ <text fg={c.logo1}>{" ●"}</text>
219
+ <text fg={c.logo2}>{"●"}</text>
220
+ <text fg={c.primary}>{"●"}</text>
221
+ <text fg={c.accent}>{"●"}</text>
222
+ <text fg={c.success}>{"●"}</text>
223
+ <text fg={c.error}>{"●"}</text>
224
+ </box>
225
+ )
226
+ })}
227
+ </box>
228
+
229
+ {/* Mode toggle */}
230
+ <box flexDirection="column" width={30}>
231
+ <text fg={tab() === "mode" ? theme.colors.text : theme.colors.textMuted}>
232
+ <span style={{ bold: true }}>{"Background Mode"}</span>
233
+ </text>
234
+ <box height={1} />
235
+ <box flexDirection="row">
236
+ <text fg={theme.mode === "dark" && tab() === "mode" ? theme.colors.primary : theme.colors.textMuted}>
237
+ {theme.mode === "dark" && tab() === "mode" ? "❯ " : " "}
238
+ </text>
239
+ <text fg={theme.mode === "dark" ? theme.colors.text : theme.colors.textMuted}>
240
+ <span style={{ bold: theme.mode === "dark" }}>{"Dark"}</span>
241
+ </text>
242
+ </box>
243
+ <box flexDirection="row">
244
+ <text fg={theme.mode === "light" && tab() === "mode" ? theme.colors.primary : theme.colors.textMuted}>
245
+ {theme.mode === "light" && tab() === "mode" ? "❯ " : " "}
246
+ </text>
247
+ <text fg={theme.mode === "light" ? theme.colors.text : theme.colors.textMuted}>
248
+ <span style={{ bold: theme.mode === "light" }}>{"Light"}</span>
249
+ </text>
250
+ </box>
251
+ </box>
252
+ </box>
253
+ </box>
254
+ )
255
+ }