codeblog-app 1.5.0 → 1.5.1
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 +6 -6
- package/src/cli/cmd/update.ts +3 -0
- package/src/tui/app.tsx +4 -0
- package/src/tui/context/theme.tsx +210 -14
- package/src/tui/routes/home.tsx +1 -1
- package/src/tui/routes/setup.tsx +127 -0
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.
|
|
4
|
+
"version": "1.5.1",
|
|
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.
|
|
60
|
-
"codeblog-app-darwin-x64": "1.5.
|
|
61
|
-
"codeblog-app-linux-arm64": "1.5.
|
|
62
|
-
"codeblog-app-linux-x64": "1.5.
|
|
63
|
-
"codeblog-app-windows-x64": "1.5.
|
|
59
|
+
"codeblog-app-darwin-arm64": "1.5.1",
|
|
60
|
+
"codeblog-app-darwin-x64": "1.5.1",
|
|
61
|
+
"codeblog-app-linux-arm64": "1.5.1",
|
|
62
|
+
"codeblog-app-linux-x64": "1.5.1",
|
|
63
|
+
"codeblog-app-windows-x64": "1.5.1"
|
|
64
64
|
},
|
|
65
65
|
"dependencies": {
|
|
66
66
|
"@ai-sdk/amazon-bedrock": "^4.0.60",
|
package/src/cli/cmd/update.ts
CHANGED
|
@@ -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 } 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()}
|
|
@@ -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
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
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"
|
|
253
|
-
mode:
|
|
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])
|
|
265
|
-
|
|
266
|
-
|
|
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
|
},
|
package/src/tui/routes/home.tsx
CHANGED
|
@@ -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",
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import { createSignal } from "solid-js"
|
|
2
|
+
import { useKeyboard } from "@opentui/solid"
|
|
3
|
+
import { useTheme, THEME_NAMES, THEMES } from "../context/theme"
|
|
4
|
+
|
|
5
|
+
export function ThemeSetup() {
|
|
6
|
+
const theme = useTheme()
|
|
7
|
+
const modes = ["dark", "light"] as const
|
|
8
|
+
const [step, setStep] = createSignal<"mode" | "theme">("mode")
|
|
9
|
+
const [modeIdx, setModeIdx] = createSignal(0)
|
|
10
|
+
const [themeIdx, setThemeIdx] = createSignal(0)
|
|
11
|
+
|
|
12
|
+
useKeyboard((evt) => {
|
|
13
|
+
if (step() === "mode") {
|
|
14
|
+
if (evt.name === "up" || evt.name === "k") {
|
|
15
|
+
setModeIdx((i) => (i - 1 + modes.length) % modes.length)
|
|
16
|
+
theme.setMode(modes[(modeIdx() - 1 + modes.length) % modes.length])
|
|
17
|
+
evt.preventDefault()
|
|
18
|
+
return
|
|
19
|
+
}
|
|
20
|
+
if (evt.name === "down" || evt.name === "j") {
|
|
21
|
+
setModeIdx((i) => (i + 1) % modes.length)
|
|
22
|
+
theme.setMode(modes[(modeIdx() + 1) % modes.length])
|
|
23
|
+
evt.preventDefault()
|
|
24
|
+
return
|
|
25
|
+
}
|
|
26
|
+
if (evt.name === "return") {
|
|
27
|
+
theme.setMode(modes[modeIdx()])
|
|
28
|
+
setStep("theme")
|
|
29
|
+
evt.preventDefault()
|
|
30
|
+
return
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
if (step() === "theme") {
|
|
35
|
+
if (evt.name === "up" || evt.name === "k") {
|
|
36
|
+
setThemeIdx((i) => (i - 1 + THEME_NAMES.length) % THEME_NAMES.length)
|
|
37
|
+
theme.set(THEME_NAMES[(themeIdx() - 1 + THEME_NAMES.length) % THEME_NAMES.length])
|
|
38
|
+
evt.preventDefault()
|
|
39
|
+
return
|
|
40
|
+
}
|
|
41
|
+
if (evt.name === "down" || evt.name === "j") {
|
|
42
|
+
setThemeIdx((i) => (i + 1) % THEME_NAMES.length)
|
|
43
|
+
theme.set(THEME_NAMES[(themeIdx() + 1) % THEME_NAMES.length])
|
|
44
|
+
evt.preventDefault()
|
|
45
|
+
return
|
|
46
|
+
}
|
|
47
|
+
if (evt.name === "return") {
|
|
48
|
+
theme.finishSetup()
|
|
49
|
+
evt.preventDefault()
|
|
50
|
+
return
|
|
51
|
+
}
|
|
52
|
+
if (evt.name === "escape") {
|
|
53
|
+
setStep("mode")
|
|
54
|
+
evt.preventDefault()
|
|
55
|
+
return
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
return (
|
|
61
|
+
<box flexDirection="column" flexGrow={1} alignItems="center" paddingLeft={2} paddingRight={2}>
|
|
62
|
+
<box flexGrow={1} minHeight={0} />
|
|
63
|
+
|
|
64
|
+
<box flexShrink={0} flexDirection="column" alignItems="center">
|
|
65
|
+
<text fg={theme.colors.primary}>
|
|
66
|
+
<span style={{ bold: true }}>Welcome to CodeBlog</span>
|
|
67
|
+
</text>
|
|
68
|
+
<box height={1} />
|
|
69
|
+
<text fg={theme.colors.textMuted}>Let's set up your terminal theme</text>
|
|
70
|
+
<box height={1} />
|
|
71
|
+
</box>
|
|
72
|
+
|
|
73
|
+
{step() === "mode" ? (
|
|
74
|
+
<box flexShrink={0} flexDirection="column" width="100%" maxWidth={50}>
|
|
75
|
+
<text fg={theme.colors.text}>
|
|
76
|
+
<span style={{ bold: true }}>Step 1: Is your terminal background dark or light?</span>
|
|
77
|
+
</text>
|
|
78
|
+
<box height={1} />
|
|
79
|
+
{modes.map((m, i) => (
|
|
80
|
+
<box flexDirection="row" paddingLeft={2}>
|
|
81
|
+
<text fg={modeIdx() === i ? theme.colors.primary : theme.colors.textMuted}>
|
|
82
|
+
{modeIdx() === i ? "❯ " : " "}
|
|
83
|
+
</text>
|
|
84
|
+
<text fg={modeIdx() === i ? theme.colors.text : theme.colors.textMuted}>
|
|
85
|
+
<span style={{ bold: modeIdx() === i }}>
|
|
86
|
+
{m === "dark" ? "🌙 Dark background" : "☀️ Light background"}
|
|
87
|
+
</span>
|
|
88
|
+
</text>
|
|
89
|
+
</box>
|
|
90
|
+
))}
|
|
91
|
+
<box height={1} />
|
|
92
|
+
<text fg={theme.colors.textMuted}>↑↓ to select · Enter to confirm</text>
|
|
93
|
+
</box>
|
|
94
|
+
) : (
|
|
95
|
+
<box flexShrink={0} flexDirection="column" width="100%" maxWidth={50}>
|
|
96
|
+
<text fg={theme.colors.text}>
|
|
97
|
+
<span style={{ bold: true }}>Step 2: Choose a color theme</span>
|
|
98
|
+
</text>
|
|
99
|
+
<box height={1} />
|
|
100
|
+
{THEME_NAMES.map((name, i) => {
|
|
101
|
+
const def = THEMES[name]
|
|
102
|
+
const c = def[theme.mode]
|
|
103
|
+
return (
|
|
104
|
+
<box flexDirection="row" paddingLeft={2}>
|
|
105
|
+
<text fg={themeIdx() === i ? c.primary : theme.colors.textMuted}>
|
|
106
|
+
{themeIdx() === i ? "❯ " : " "}
|
|
107
|
+
</text>
|
|
108
|
+
<text fg={themeIdx() === i ? c.text : theme.colors.textMuted}>
|
|
109
|
+
<span style={{ bold: themeIdx() === i }}>{name}</span>
|
|
110
|
+
</text>
|
|
111
|
+
<text fg={c.logo1}>{" ●"}</text>
|
|
112
|
+
<text fg={c.logo2}>{"●"}</text>
|
|
113
|
+
<text fg={c.primary}>{"●"}</text>
|
|
114
|
+
<text fg={c.success}>{"●"}</text>
|
|
115
|
+
<text fg={c.error}>{"●"}</text>
|
|
116
|
+
</box>
|
|
117
|
+
)
|
|
118
|
+
})}
|
|
119
|
+
<box height={1} />
|
|
120
|
+
<text fg={theme.colors.textMuted}>↑↓ to select · Enter to confirm · Esc to go back</text>
|
|
121
|
+
</box>
|
|
122
|
+
)}
|
|
123
|
+
|
|
124
|
+
<box flexGrow={1} minHeight={0} />
|
|
125
|
+
</box>
|
|
126
|
+
)
|
|
127
|
+
}
|