@kitlangton/ghui 0.3.0 → 0.3.3

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.
Files changed (55) hide show
  1. package/README.md +13 -3
  2. package/bin/ghui.js +6 -1
  3. package/dist/index.js +9456 -0
  4. package/package.json +7 -4
  5. package/src/App.tsx +0 -2779
  6. package/src/appCommands.ts +0 -409
  7. package/src/commands.ts +0 -73
  8. package/src/config.ts +0 -20
  9. package/src/date.ts +0 -20
  10. package/src/domain.ts +0 -149
  11. package/src/errors.ts +0 -10
  12. package/src/index.tsx +0 -50
  13. package/src/keyboard/opentuiAdapter.ts +0 -43
  14. package/src/keymap/all.ts +0 -116
  15. package/src/keymap/changedFilesModal.ts +0 -23
  16. package/src/keymap/closeModal.ts +0 -13
  17. package/src/keymap/commandPalette.ts +0 -16
  18. package/src/keymap/commentModal.ts +0 -73
  19. package/src/keymap/commentThreadModal.ts +0 -24
  20. package/src/keymap/detailView.ts +0 -30
  21. package/src/keymap/diffView.ts +0 -93
  22. package/src/keymap/filterMode.ts +0 -13
  23. package/src/keymap/helpers.ts +0 -24
  24. package/src/keymap/labelModal.ts +0 -16
  25. package/src/keymap/listNav.ts +0 -119
  26. package/src/keymap/mergeModal.ts +0 -23
  27. package/src/keymap/openRepositoryModal.ts +0 -13
  28. package/src/keymap/submitReviewModal.ts +0 -48
  29. package/src/keymap/themeModal.ts +0 -49
  30. package/src/mergeActions.ts +0 -88
  31. package/src/observability.ts +0 -46
  32. package/src/pullRequestCache.ts +0 -19
  33. package/src/pullRequestViews.ts +0 -43
  34. package/src/services/BrowserOpener.ts +0 -22
  35. package/src/services/Clipboard.ts +0 -46
  36. package/src/services/CommandRunner.ts +0 -91
  37. package/src/services/GitHubService.ts +0 -756
  38. package/src/services/MockGitHubService.ts +0 -182
  39. package/src/themeStore.ts +0 -60
  40. package/src/ui/CommandPalette.tsx +0 -176
  41. package/src/ui/DetailsPane.tsx +0 -656
  42. package/src/ui/FooterHints.tsx +0 -90
  43. package/src/ui/LoadingLogo.tsx +0 -75
  44. package/src/ui/PullRequestDiffPane.tsx +0 -249
  45. package/src/ui/PullRequestList.tsx +0 -194
  46. package/src/ui/colors.ts +0 -821
  47. package/src/ui/commentEditor.ts +0 -126
  48. package/src/ui/comments.tsx +0 -143
  49. package/src/ui/diff.ts +0 -650
  50. package/src/ui/diffStats.tsx +0 -25
  51. package/src/ui/modals.tsx +0 -964
  52. package/src/ui/primitives.tsx +0 -350
  53. package/src/ui/pullRequests.ts +0 -106
  54. package/src/ui/singleLineInput.ts +0 -26
  55. package/src/ui/spinner.ts +0 -1
package/src/ui/colors.ts DELETED
@@ -1,821 +0,0 @@
1
- export type ThemeId =
2
- | "system"
3
- | "ghui"
4
- | "tokyo-night"
5
- | "catppuccin"
6
- | "rose-pine"
7
- | "gruvbox"
8
- | "nord"
9
- | "dracula"
10
- | "kanagawa"
11
- | "one-dark"
12
- | "monokai"
13
- | "solarized-dark"
14
- | "everforest"
15
- | "vesper"
16
- | "opencode"
17
-
18
- export interface ColorPalette {
19
- readonly background: string
20
- readonly modalBackground: string
21
- readonly text: string
22
- readonly muted: string
23
- readonly separator: string
24
- readonly accent: string
25
- readonly inlineCode: string
26
- readonly error: string
27
- readonly selectedBg: string
28
- readonly selectedText: string
29
- readonly count: string
30
- readonly status: {
31
- readonly draft: string
32
- readonly approved: string
33
- readonly changes: string
34
- readonly review: string
35
- readonly none: string
36
- readonly passing: string
37
- readonly pending: string
38
- readonly failing: string
39
- }
40
- readonly repos: {
41
- readonly opencode: string
42
- readonly "effect-smol": string
43
- readonly "opencode-console": string
44
- readonly opencontrol: string
45
- readonly default: string
46
- }
47
- readonly diff: {
48
- readonly addedBg: string
49
- readonly removedBg: string
50
- readonly contextBg: string
51
- readonly lineNumberBg: string
52
- readonly addedLineNumberBg: string
53
- readonly removedLineNumberBg: string
54
- }
55
- }
56
-
57
- export interface ThemeDefinition {
58
- readonly id: ThemeId
59
- readonly name: string
60
- readonly description: string
61
- readonly colors: ColorPalette
62
- }
63
-
64
- interface TerminalThemeColors {
65
- readonly palette: readonly (string | null)[]
66
- readonly defaultForeground: string | null
67
- readonly defaultBackground: string | null
68
- readonly highlightBackground: string | null
69
- readonly highlightForeground: string | null
70
- }
71
-
72
- const readableHex = (value: string | null | undefined, fallback: string) =>
73
- typeof value === "string" && /^#[0-9a-fA-F]{6}(?:[0-9a-fA-F]{2})?$/.test(value) ? value : fallback
74
-
75
- const hexToRgb = (hex: string) => {
76
- const value = hex.replace(/^#/, "").slice(0, 6)
77
- return {
78
- r: parseInt(value.slice(0, 2), 16),
79
- g: parseInt(value.slice(2, 4), 16),
80
- b: parseInt(value.slice(4, 6), 16),
81
- }
82
- }
83
-
84
- const luminance = (hex: string) => {
85
- const { r, g, b } = hexToRgb(hex)
86
- return 0.299 * r + 0.587 * g + 0.114 * b
87
- }
88
-
89
- const rgbToHex = ({ r, g, b }: { readonly r: number; readonly g: number; readonly b: number }) =>
90
- `#${[r, g, b].map((component) => Math.max(0, Math.min(255, Math.round(component))).toString(16).padStart(2, "0")).join("")}`
91
-
92
- export const mixHex = (base: string, overlay: string, amount: number) => {
93
- const from = hexToRgb(base)
94
- const to = hexToRgb(overlay)
95
- return rgbToHex({
96
- r: from.r + (to.r - from.r) * amount,
97
- g: from.g + (to.g - from.g) * amount,
98
- b: from.b + (to.b - from.b) * amount,
99
- })
100
- }
101
-
102
- const grayscaleRamp = (background: string) => {
103
- const bg = hexToRgb(background)
104
- const bgLum = luminance(background)
105
- const isDark = bgLum < 128
106
- const grays: Record<number, string> = {}
107
-
108
- for (let i = 1; i <= 12; i++) {
109
- const factor = i / 12
110
- let r: number
111
- let g: number
112
- let b: number
113
-
114
- if (isDark) {
115
- if (bgLum < 10) {
116
- const value = Math.floor(factor * 0.4 * 255)
117
- r = value
118
- g = value
119
- b = value
120
- } else {
121
- const nextLum = bgLum + (255 - bgLum) * factor * 0.4
122
- const ratio = nextLum / bgLum
123
- r = Math.min(bg.r * ratio, 255)
124
- g = Math.min(bg.g * ratio, 255)
125
- b = Math.min(bg.b * ratio, 255)
126
- }
127
- } else if (bgLum > 245) {
128
- const value = Math.floor(255 - factor * 0.4 * 255)
129
- r = value
130
- g = value
131
- b = value
132
- } else {
133
- const nextLum = bgLum * (1 - factor * 0.4)
134
- const ratio = nextLum / bgLum
135
- r = Math.max(bg.r * ratio, 0)
136
- g = Math.max(bg.g * ratio, 0)
137
- b = Math.max(bg.b * ratio, 0)
138
- }
139
-
140
- grays[i] = rgbToHex({ r, g, b })
141
- }
142
-
143
- return grays
144
- }
145
-
146
- const mutedTextColor = (background: string) => {
147
- const bgLum = luminance(background)
148
- const isDark = bgLum < 128
149
- const value = isDark
150
- ? bgLum < 10 ? 180 : Math.min(Math.floor(160 + bgLum * 0.3), 200)
151
- : bgLum > 245 ? 75 : Math.max(Math.floor(100 - (255 - bgLum) * 0.2), 60)
152
- return rgbToHex({ r: value, g: value, b: value })
153
- }
154
-
155
- const contrastText = (background: string) => luminance(background) > 128 ? "#000000" : "#ffffff"
156
-
157
- export const lineNumberTextColor = (background: string, foreground: string) => {
158
- const bg = readableHex(background, foreground)
159
- const fg = readableHex(foreground, contrastText(bg))
160
- const contrast = Math.abs(luminance(bg) - luminance(fg))
161
- return mixHex(bg, fg, contrast < 90 ? 0.62 : 0.5)
162
- }
163
-
164
- const ghuiColors: ColorPalette = {
165
- background: "#111018",
166
- modalBackground: "#1a1a2e",
167
- text: "#ede7da",
168
- muted: "#9f9788",
169
- separator: "#6f685d",
170
- accent: "#f4a51c",
171
- inlineCode: "#d7c5a1",
172
- error: "#f97316",
173
- selectedBg: "#1d2430",
174
- selectedText: "#f8fafc",
175
- count: "#d7c5a1",
176
- status: {
177
- draft: "#f59e0b",
178
- approved: "#7dd3a3",
179
- changes: "#f87171",
180
- review: "#93c5fd",
181
- none: "#9f9788",
182
- passing: "#7dd3a3",
183
- pending: "#f4a51c",
184
- failing: "#f87171",
185
- },
186
- repos: {
187
- opencode: "#60a5fa",
188
- "effect-smol": "#34d399",
189
- "opencode-console": "#f472b6",
190
- opencontrol: "#f59e0b",
191
- default: "#93c5fd",
192
- },
193
- diff: {
194
- addedBg: "#17351f",
195
- removedBg: "#3a1e22",
196
- contextBg: "transparent",
197
- lineNumberBg: "#151515",
198
- addedLineNumberBg: "#12301a",
199
- removedLineNumberBg: "#35171b",
200
- },
201
- }
202
-
203
- const makeSystemColors = (terminal?: TerminalThemeColors): ColorPalette => {
204
- const palette = terminal?.palette ?? []
205
- const terminalBackground = readableHex(terminal?.defaultBackground, readableHex(palette[0], "#000000"))
206
- const text = readableHex(terminal?.defaultForeground, readableHex(palette[7], "#ffffff"))
207
- const grays = grayscaleRamp(terminalBackground)
208
- const isDark = luminance(terminalBackground) < 128
209
- const red = readableHex(palette[1], "#cc0000")
210
- const green = readableHex(palette[2], "#4e9a06")
211
- const yellow = readableHex(palette[3], "#c4a000")
212
- const blue = readableHex(palette[4], "#3465a4")
213
- const magenta = readableHex(palette[5], "#75507b")
214
- const brightBlack = readableHex(palette[8], mutedTextColor(terminalBackground))
215
- const brightGreen = readableHex(palette[10], green)
216
- const brightBlue = readableHex(palette[12], blue)
217
- const brightMagenta = readableHex(palette[13], magenta)
218
- const primary = brightBlue
219
- const panel = grays[2] ?? mixHex(terminalBackground, text, isDark ? 0.07 : 0.08)
220
- const element = grays[3] ?? mixHex(terminalBackground, text, isDark ? 0.1 : 0.1)
221
- const border = grays[7] ?? mixHex(terminalBackground, text, isDark ? 0.24 : 0.24)
222
- const borderSubtle = grays[6] ?? border
223
- const diffAlpha = isDark ? 0.22 : 0.14
224
-
225
- return {
226
- background: "transparent",
227
- modalBackground: panel,
228
- text,
229
- muted: mutedTextColor(terminalBackground),
230
- separator: border,
231
- accent: primary,
232
- inlineCode: brightGreen,
233
- error: red,
234
- selectedBg: primary,
235
- selectedText: contrastText(primary),
236
- count: primary,
237
- status: {
238
- draft: yellow,
239
- approved: green,
240
- changes: red,
241
- review: primary,
242
- none: brightBlack,
243
- passing: green,
244
- pending: yellow,
245
- failing: red,
246
- },
247
- repos: {
248
- opencode: primary,
249
- "effect-smol": green,
250
- "opencode-console": brightMagenta,
251
- opencontrol: yellow,
252
- default: blue,
253
- },
254
- diff: {
255
- addedBg: mixHex(terminalBackground, green, diffAlpha),
256
- removedBg: mixHex(terminalBackground, red, diffAlpha),
257
- contextBg: panel,
258
- lineNumberBg: borderSubtle,
259
- addedLineNumberBg: mixHex(element, green, diffAlpha),
260
- removedLineNumberBg: mixHex(element, red, diffAlpha),
261
- },
262
- }
263
- }
264
-
265
- const systemColors: ColorPalette = makeSystemColors()
266
-
267
- const tokyoNightColors: ColorPalette = {
268
- background: "#1a1b26",
269
- modalBackground: "#24283b",
270
- text: "#c0caf5",
271
- muted: "#787c99",
272
- separator: "#3b4261",
273
- accent: "#7aa2f7",
274
- inlineCode: "#bb9af7",
275
- error: "#f7768e",
276
- selectedBg: "#283457",
277
- selectedText: "#ffffff",
278
- count: "#ff9e64",
279
- status: {
280
- draft: "#e0af68",
281
- approved: "#9ece6a",
282
- changes: "#f7768e",
283
- review: "#7dcfff",
284
- none: "#787c99",
285
- passing: "#9ece6a",
286
- pending: "#e0af68",
287
- failing: "#f7768e",
288
- },
289
- repos: {
290
- opencode: "#7aa2f7",
291
- "effect-smol": "#9ece6a",
292
- "opencode-console": "#bb9af7",
293
- opencontrol: "#ff9e64",
294
- default: "#7dcfff",
295
- },
296
- diff: {
297
- addedBg: "#203326",
298
- removedBg: "#3a222c",
299
- contextBg: "transparent",
300
- lineNumberBg: "#16161e",
301
- addedLineNumberBg: "#1b2f23",
302
- removedLineNumberBg: "#33202a",
303
- },
304
- }
305
-
306
- const opencodeColors: ColorPalette = {
307
- background: "#0a0a0a",
308
- modalBackground: "#1e1e1e",
309
- text: "#eeeeee",
310
- muted: "#808080",
311
- separator: "#484848",
312
- accent: "#fab283",
313
- inlineCode: "#7fd88f",
314
- error: "#e06c75",
315
- selectedBg: "#323232",
316
- selectedText: "#eeeeee",
317
- count: "#fab283",
318
- status: {
319
- draft: "#f5a742",
320
- approved: "#7fd88f",
321
- changes: "#e06c75",
322
- review: "#5c9cf5",
323
- none: "#808080",
324
- passing: "#7fd88f",
325
- pending: "#f5a742",
326
- failing: "#e06c75",
327
- },
328
- repos: {
329
- opencode: "#fab283",
330
- "effect-smol": "#7fd88f",
331
- "opencode-console": "#9d7cd8",
332
- opencontrol: "#f5a742",
333
- default: "#5c9cf5",
334
- },
335
- diff: {
336
- addedBg: "#20303b",
337
- removedBg: "#37222c",
338
- contextBg: "transparent",
339
- lineNumberBg: "#141414",
340
- addedLineNumberBg: "#1b2b34",
341
- removedLineNumberBg: "#2d1f26",
342
- },
343
- }
344
-
345
- const catppuccinColors: ColorPalette = {
346
- background: "#1e1e2e",
347
- modalBackground: "#313244",
348
- text: "#cdd6f4",
349
- muted: "#7f849c",
350
- separator: "#45475a",
351
- accent: "#cba6f7",
352
- inlineCode: "#f5c2e7",
353
- error: "#f38ba8",
354
- selectedBg: "#45475a",
355
- selectedText: "#f5e0dc",
356
- count: "#fab387",
357
- status: {
358
- draft: "#f9e2af",
359
- approved: "#a6e3a1",
360
- changes: "#f38ba8",
361
- review: "#89b4fa",
362
- none: "#7f849c",
363
- passing: "#a6e3a1",
364
- pending: "#f9e2af",
365
- failing: "#f38ba8",
366
- },
367
- repos: {
368
- opencode: "#89b4fa",
369
- "effect-smol": "#a6e3a1",
370
- "opencode-console": "#f5c2e7",
371
- opencontrol: "#fab387",
372
- default: "#74c7ec",
373
- },
374
- diff: {
375
- addedBg: "#243927",
376
- removedBg: "#3b2532",
377
- contextBg: "transparent",
378
- lineNumberBg: "#181825",
379
- addedLineNumberBg: "#203524",
380
- removedLineNumberBg: "#36232f",
381
- },
382
- }
383
-
384
- const rosePineColors: ColorPalette = {
385
- background: "#191724",
386
- modalBackground: "#26233a",
387
- text: "#e0def4",
388
- muted: "#908caa",
389
- separator: "#524f67",
390
- accent: "#c4a7e7",
391
- inlineCode: "#f6c177",
392
- error: "#eb6f92",
393
- selectedBg: "#403d52",
394
- selectedText: "#f6f1ff",
395
- count: "#ebbcba",
396
- status: {
397
- draft: "#f6c177",
398
- approved: "#9ccfd8",
399
- changes: "#eb6f92",
400
- review: "#31748f",
401
- none: "#908caa",
402
- passing: "#9ccfd8",
403
- pending: "#f6c177",
404
- failing: "#eb6f92",
405
- },
406
- repos: {
407
- opencode: "#31748f",
408
- "effect-smol": "#9ccfd8",
409
- "opencode-console": "#c4a7e7",
410
- opencontrol: "#f6c177",
411
- default: "#ebbcba",
412
- },
413
- diff: {
414
- addedBg: "#23343a",
415
- removedBg: "#3a2534",
416
- contextBg: "transparent",
417
- lineNumberBg: "#1f1d2e",
418
- addedLineNumberBg: "#203137",
419
- removedLineNumberBg: "#352330",
420
- },
421
- }
422
-
423
- const gruvboxColors: ColorPalette = {
424
- background: "#282828",
425
- modalBackground: "#3c3836",
426
- text: "#ebdbb2",
427
- muted: "#928374",
428
- separator: "#665c54",
429
- accent: "#fabd2f",
430
- inlineCode: "#d3869b",
431
- error: "#fb4934",
432
- selectedBg: "#504945",
433
- selectedText: "#fbf1c7",
434
- count: "#fe8019",
435
- status: {
436
- draft: "#fabd2f",
437
- approved: "#b8bb26",
438
- changes: "#fb4934",
439
- review: "#83a598",
440
- none: "#928374",
441
- passing: "#b8bb26",
442
- pending: "#fabd2f",
443
- failing: "#fb4934",
444
- },
445
- repos: {
446
- opencode: "#83a598",
447
- "effect-smol": "#b8bb26",
448
- "opencode-console": "#d3869b",
449
- opencontrol: "#fe8019",
450
- default: "#8ec07c",
451
- },
452
- diff: {
453
- addedBg: "#32361f",
454
- removedBg: "#3c2927",
455
- contextBg: "transparent",
456
- lineNumberBg: "#1d2021",
457
- addedLineNumberBg: "#2f331e",
458
- removedLineNumberBg: "#382726",
459
- },
460
- }
461
-
462
- const nordColors: ColorPalette = {
463
- background: "#2e3440",
464
- modalBackground: "#3b4252",
465
- text: "#eceff4",
466
- muted: "#8892a7",
467
- separator: "#4c566a",
468
- accent: "#88c0d0",
469
- inlineCode: "#b48ead",
470
- error: "#bf616a",
471
- selectedBg: "#434c5e",
472
- selectedText: "#eceff4",
473
- count: "#ebcb8b",
474
- status: {
475
- draft: "#ebcb8b",
476
- approved: "#a3be8c",
477
- changes: "#bf616a",
478
- review: "#81a1c1",
479
- none: "#8892a7",
480
- passing: "#a3be8c",
481
- pending: "#ebcb8b",
482
- failing: "#bf616a",
483
- },
484
- repos: {
485
- opencode: "#81a1c1",
486
- "effect-smol": "#a3be8c",
487
- "opencode-console": "#b48ead",
488
- opencontrol: "#d08770",
489
- default: "#88c0d0",
490
- },
491
- diff: {
492
- addedBg: "#334033",
493
- removedBg: "#433238",
494
- contextBg: "transparent",
495
- lineNumberBg: "#242933",
496
- addedLineNumberBg: "#303d31",
497
- removedLineNumberBg: "#3f3036",
498
- },
499
- }
500
-
501
- const draculaColors: ColorPalette = {
502
- background: "#282a36",
503
- modalBackground: "#343746",
504
- text: "#f8f8f2",
505
- muted: "#8f94b8",
506
- separator: "#4f5268",
507
- accent: "#bd93f9",
508
- inlineCode: "#ff79c6",
509
- error: "#ff5555",
510
- selectedBg: "#44475a",
511
- selectedText: "#f8f8f2",
512
- count: "#ffb86c",
513
- status: {
514
- draft: "#f1fa8c",
515
- approved: "#50fa7b",
516
- changes: "#ff5555",
517
- review: "#8be9fd",
518
- none: "#8f94b8",
519
- passing: "#50fa7b",
520
- pending: "#f1fa8c",
521
- failing: "#ff5555",
522
- },
523
- repos: {
524
- opencode: "#8be9fd",
525
- "effect-smol": "#50fa7b",
526
- "opencode-console": "#ff79c6",
527
- opencontrol: "#ffb86c",
528
- default: "#bd93f9",
529
- },
530
- diff: {
531
- addedBg: "#203a29",
532
- removedBg: "#43272f",
533
- contextBg: "transparent",
534
- lineNumberBg: "#21222c",
535
- addedLineNumberBg: "#1d3627",
536
- removedLineNumberBg: "#3d252c",
537
- },
538
- }
539
-
540
- const kanagawaColors: ColorPalette = {
541
- background: "#1f1f28",
542
- modalBackground: "#2a2a37",
543
- text: "#dcd7ba",
544
- muted: "#727169",
545
- separator: "#54546d",
546
- accent: "#7e9cd8",
547
- inlineCode: "#d27e99",
548
- error: "#c34043",
549
- selectedBg: "#363646",
550
- selectedText: "#fff7d6",
551
- count: "#ffa066",
552
- status: {
553
- draft: "#c0a36e",
554
- approved: "#76946a",
555
- changes: "#c34043",
556
- review: "#7e9cd8",
557
- none: "#727169",
558
- passing: "#76946a",
559
- pending: "#c0a36e",
560
- failing: "#c34043",
561
- },
562
- repos: {
563
- opencode: "#7e9cd8",
564
- "effect-smol": "#98bb6c",
565
- "opencode-console": "#957fb8",
566
- opencontrol: "#ffa066",
567
- default: "#7fb4ca",
568
- },
569
- diff: {
570
- addedBg: "#253326",
571
- removedBg: "#3a2528",
572
- contextBg: "transparent",
573
- lineNumberBg: "#16161d",
574
- addedLineNumberBg: "#223025",
575
- removedLineNumberBg: "#352326",
576
- },
577
- }
578
-
579
- const oneDarkColors: ColorPalette = {
580
- background: "#282c34",
581
- modalBackground: "#2c313c",
582
- text: "#abb2bf",
583
- muted: "#7f848e",
584
- separator: "#4b5263",
585
- accent: "#61afef",
586
- inlineCode: "#c678dd",
587
- error: "#e06c75",
588
- selectedBg: "#3e4451",
589
- selectedText: "#ffffff",
590
- count: "#d19a66",
591
- status: {
592
- draft: "#e5c07b",
593
- approved: "#98c379",
594
- changes: "#e06c75",
595
- review: "#61afef",
596
- none: "#7f848e",
597
- passing: "#98c379",
598
- pending: "#e5c07b",
599
- failing: "#e06c75",
600
- },
601
- repos: {
602
- opencode: "#61afef",
603
- "effect-smol": "#98c379",
604
- "opencode-console": "#c678dd",
605
- opencontrol: "#d19a66",
606
- default: "#56b6c2",
607
- },
608
- diff: {
609
- addedBg: "#27362b",
610
- removedBg: "#3a282c",
611
- contextBg: "transparent",
612
- lineNumberBg: "#21252b",
613
- addedLineNumberBg: "#243228",
614
- removedLineNumberBg: "#35262a",
615
- },
616
- }
617
-
618
- const monokaiColors: ColorPalette = {
619
- background: "#272822",
620
- modalBackground: "#383830",
621
- text: "#f8f8f2",
622
- muted: "#90908a",
623
- separator: "#5b5b50",
624
- accent: "#66d9ef",
625
- inlineCode: "#ae81ff",
626
- error: "#f92672",
627
- selectedBg: "#49483e",
628
- selectedText: "#ffffff",
629
- count: "#fd971f",
630
- status: {
631
- draft: "#e6db74",
632
- approved: "#a6e22e",
633
- changes: "#f92672",
634
- review: "#66d9ef",
635
- none: "#90908a",
636
- passing: "#a6e22e",
637
- pending: "#e6db74",
638
- failing: "#f92672",
639
- },
640
- repos: {
641
- opencode: "#66d9ef",
642
- "effect-smol": "#a6e22e",
643
- "opencode-console": "#ae81ff",
644
- opencontrol: "#fd971f",
645
- default: "#a1efe4",
646
- },
647
- diff: {
648
- addedBg: "#2f3a22",
649
- removedBg: "#3d2430",
650
- contextBg: "transparent",
651
- lineNumberBg: "#1f201b",
652
- addedLineNumberBg: "#2b3620",
653
- removedLineNumberBg: "#38222d",
654
- },
655
- }
656
-
657
- const solarizedDarkColors: ColorPalette = {
658
- background: "#002b36",
659
- modalBackground: "#123d48",
660
- text: "#eee8d5",
661
- muted: "#839496",
662
- separator: "#586e75",
663
- accent: "#268bd2",
664
- inlineCode: "#2aa198",
665
- error: "#dc322f",
666
- selectedBg: "#174652",
667
- selectedText: "#fdf6e3",
668
- count: "#cb4b16",
669
- status: {
670
- draft: "#b58900",
671
- approved: "#859900",
672
- changes: "#dc322f",
673
- review: "#268bd2",
674
- none: "#839496",
675
- passing: "#859900",
676
- pending: "#b58900",
677
- failing: "#dc322f",
678
- },
679
- repos: {
680
- opencode: "#268bd2",
681
- "effect-smol": "#859900",
682
- "opencode-console": "#d33682",
683
- opencontrol: "#cb4b16",
684
- default: "#2aa198",
685
- },
686
- diff: {
687
- addedBg: "#123c2e",
688
- removedBg: "#3c262a",
689
- contextBg: "transparent",
690
- lineNumberBg: "#073642",
691
- addedLineNumberBg: "#10372b",
692
- removedLineNumberBg: "#362429",
693
- },
694
- }
695
-
696
- const everforestColors: ColorPalette = {
697
- background: "#2d353b",
698
- modalBackground: "#343f44",
699
- text: "#d3c6aa",
700
- muted: "#859289",
701
- separator: "#56635f",
702
- accent: "#7fbbb3",
703
- inlineCode: "#d699b6",
704
- error: "#e67e80",
705
- selectedBg: "#465258",
706
- selectedText: "#fff4d6",
707
- count: "#e69875",
708
- status: {
709
- draft: "#dbbc7f",
710
- approved: "#a7c080",
711
- changes: "#e67e80",
712
- review: "#7fbbb3",
713
- none: "#859289",
714
- passing: "#a7c080",
715
- pending: "#dbbc7f",
716
- failing: "#e67e80",
717
- },
718
- repos: {
719
- opencode: "#7fbbb3",
720
- "effect-smol": "#a7c080",
721
- "opencode-console": "#d699b6",
722
- opencontrol: "#e69875",
723
- default: "#83c092",
724
- },
725
- diff: {
726
- addedBg: "#33422f",
727
- removedBg: "#463333",
728
- contextBg: "transparent",
729
- lineNumberBg: "#232a2e",
730
- addedLineNumberBg: "#303d2d",
731
- removedLineNumberBg: "#403030",
732
- },
733
- }
734
-
735
- const vesperColors: ColorPalette = {
736
- background: "#101010",
737
- modalBackground: "#1A1A1A",
738
- text: "#FFFFFF",
739
- muted: "#A0A0A0",
740
- separator: "#282828",
741
- accent: "#FFC799",
742
- inlineCode: "#99FFE4",
743
- error: "#FF8080",
744
- selectedBg: "#232323",
745
- selectedText: "#FFFFFF",
746
- count: "#FFC799",
747
- status: {
748
- draft: "#FFC799",
749
- approved: "#99FFE4",
750
- changes: "#FF8080",
751
- review: "#B0B0B0",
752
- none: "#7E7E7E",
753
- passing: "#99FFE4",
754
- pending: "#FFC799",
755
- failing: "#FF8080",
756
- },
757
- repos: {
758
- opencode: "#FFC799",
759
- "effect-smol": "#99FFE4",
760
- "opencode-console": "#FFD1A8",
761
- opencontrol: "#FFC799",
762
- default: "#B0B0B0",
763
- },
764
- diff: {
765
- addedBg: "#17312d",
766
- removedBg: "#351f1f",
767
- contextBg: "transparent",
768
- lineNumberBg: "#101010",
769
- addedLineNumberBg: "#142b28",
770
- removedLineNumberBg: "#2f1c1c",
771
- },
772
- }
773
-
774
- export const themeDefinitions: readonly ThemeDefinition[] = [
775
- { id: "system", name: "System", description: "Use the terminal foreground, background, and ANSI palette", colors: systemColors },
776
- { id: "ghui", name: "GHUI", description: "Warm parchment accents on a deep slate background", colors: ghuiColors },
777
- { id: "tokyo-night", name: "Tokyo Night", description: "Cool indigo surfaces with neon editor accents", colors: tokyoNightColors },
778
- { id: "catppuccin", name: "Catppuccin", description: "Mocha lavender, peach, and soft pastel contrast", colors: catppuccinColors },
779
- { id: "rose-pine", name: "Rose Pine", description: "Muted rose, pine, and gold on dusky violet", colors: rosePineColors },
780
- { id: "gruvbox", name: "Gruvbox", description: "Retro warm earth tones with punchy semantic accents", colors: gruvboxColors },
781
- { id: "nord", name: "Nord", description: "Arctic blue-gray surfaces with frosty accents", colors: nordColors },
782
- { id: "dracula", name: "Dracula", description: "High-contrast purple, pink, cyan, and green", colors: draculaColors },
783
- { id: "kanagawa", name: "Kanagawa", description: "Ink-wash indigo, wave blues, and autumn accents", colors: kanagawaColors },
784
- { id: "one-dark", name: "One Dark", description: "Atom-style charcoal with clean blue and green accents", colors: oneDarkColors },
785
- { id: "monokai", name: "Monokai", description: "Classic dark olive with electric syntax colors", colors: monokaiColors },
786
- { id: "solarized-dark", name: "Solarized Dark", description: "Low-contrast blue-green base with calibrated accents", colors: solarizedDarkColors },
787
- { id: "everforest", name: "Everforest", description: "Soft green-gray forest tones with warm highlights", colors: everforestColors },
788
- { id: "vesper", name: "Vesper", description: "Minimal black surfaces with peach and aqua accents", colors: vesperColors },
789
- { id: "opencode", name: "OpenCode", description: "Charcoal panels with peach, violet, and blue highlights", colors: opencodeColors },
790
- ] as const
791
-
792
- let activeTheme = themeDefinitions.find((theme) => theme.id === "ghui") ?? themeDefinitions[0]!
793
-
794
- export const colors: ColorPalette = { ...ghuiColors }
795
-
796
- export const getThemeDefinition = (id: ThemeId) => themeDefinitions.find((theme) => theme.id === id) ?? themeDefinitions[0]!
797
-
798
- export const isThemeId = (value: unknown): value is ThemeId => typeof value === "string" && themeDefinitions.some((theme) => theme.id === value)
799
-
800
- export const filterThemeDefinitions = (query: string) => {
801
- const normalized = query.trim().toLowerCase()
802
- if (normalized.length === 0) return themeDefinitions
803
- return themeDefinitions.filter((theme) =>
804
- theme.id.includes(normalized) ||
805
- theme.name.toLowerCase().includes(normalized) ||
806
- theme.description.toLowerCase().includes(normalized),
807
- )
808
- }
809
-
810
- export const setActiveTheme = (id: ThemeId) => {
811
- if (activeTheme.id === id) return
812
- activeTheme = getThemeDefinition(id)
813
- Object.assign(colors, activeTheme.colors)
814
- }
815
-
816
- export const setSystemThemeColors = (terminalColors: TerminalThemeColors) => {
817
- Object.assign(systemColors, makeSystemColors(terminalColors))
818
- if (activeTheme.id === "system") {
819
- Object.assign(colors, systemColors)
820
- }
821
- }