pi-studio-opencode 0.1.0

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 (60) hide show
  1. package/ARCHITECTURE.md +122 -0
  2. package/LICENSE +21 -0
  3. package/README.md +108 -0
  4. package/dist/demo-host-pi.d.ts +1 -0
  5. package/dist/demo-host-pi.js +71 -0
  6. package/dist/demo-host-pi.js.map +1 -0
  7. package/dist/demo-host.d.ts +1 -0
  8. package/dist/demo-host.js +154 -0
  9. package/dist/demo-host.js.map +1 -0
  10. package/dist/host-opencode-plugin.d.ts +52 -0
  11. package/dist/host-opencode-plugin.js +396 -0
  12. package/dist/host-opencode-plugin.js.map +1 -0
  13. package/dist/host-opencode.d.ts +154 -0
  14. package/dist/host-opencode.js +627 -0
  15. package/dist/host-opencode.js.map +1 -0
  16. package/dist/host-pi.d.ts +45 -0
  17. package/dist/host-pi.js +258 -0
  18. package/dist/host-pi.js.map +1 -0
  19. package/dist/install-config.d.ts +36 -0
  20. package/dist/install-config.js +136 -0
  21. package/dist/install-config.js.map +1 -0
  22. package/dist/install.d.ts +16 -0
  23. package/dist/install.js +168 -0
  24. package/dist/install.js.map +1 -0
  25. package/dist/launcher.d.ts +2 -0
  26. package/dist/launcher.js +124 -0
  27. package/dist/launcher.js.map +1 -0
  28. package/dist/main.d.ts +1 -0
  29. package/dist/main.js +732 -0
  30. package/dist/main.js.map +1 -0
  31. package/dist/mock-pi-session.d.ts +27 -0
  32. package/dist/mock-pi-session.js +138 -0
  33. package/dist/mock-pi-session.js.map +1 -0
  34. package/dist/open-browser.d.ts +1 -0
  35. package/dist/open-browser.js +29 -0
  36. package/dist/open-browser.js.map +1 -0
  37. package/dist/opencode-plugin.d.ts +3 -0
  38. package/dist/opencode-plugin.js +326 -0
  39. package/dist/opencode-plugin.js.map +1 -0
  40. package/dist/prototype-pdf.d.ts +12 -0
  41. package/dist/prototype-pdf.js +991 -0
  42. package/dist/prototype-pdf.js.map +1 -0
  43. package/dist/prototype-server.d.ts +88 -0
  44. package/dist/prototype-server.js +1002 -0
  45. package/dist/prototype-server.js.map +1 -0
  46. package/dist/prototype-theme.d.ts +36 -0
  47. package/dist/prototype-theme.js +1471 -0
  48. package/dist/prototype-theme.js.map +1 -0
  49. package/dist/studio-core.d.ts +63 -0
  50. package/dist/studio-core.js +251 -0
  51. package/dist/studio-core.js.map +1 -0
  52. package/dist/studio-host-types.d.ts +50 -0
  53. package/dist/studio-host-types.js +14 -0
  54. package/dist/studio-host-types.js.map +1 -0
  55. package/examples/opencode/INSTALL.md +67 -0
  56. package/examples/opencode/opencode.local-path.jsonc +16 -0
  57. package/package.json +68 -0
  58. package/static/prototype.css +1277 -0
  59. package/static/prototype.html +173 -0
  60. package/static/prototype.js +3198 -0
@@ -0,0 +1,1471 @@
1
+ import { readFile, readdir } from "node:fs/promises";
2
+ import { homedir } from "node:os";
3
+ import { dirname, join, resolve } from "node:path";
4
+ const BASE_DARK_PALETTE = {
5
+ bg: "#0f1117",
6
+ panel: "#171b24",
7
+ panel2: "#11161f",
8
+ text: "#e6edf3",
9
+ muted: "#9aa5b1",
10
+ accent: "#5ea1ff",
11
+ warn: "#f9c74f",
12
+ error: "#ff6b6b",
13
+ ok: "#73d13d",
14
+ markerBg: "rgba(94, 161, 255, 0.25)",
15
+ okBorder: "rgba(115, 209, 61, 0.70)",
16
+ warnBorder: "rgba(249, 199, 79, 0.70)",
17
+ mdHeading: "#f0c674",
18
+ mdLink: "#81a2be",
19
+ mdLinkUrl: "#666666",
20
+ mdCode: "#8abeb7",
21
+ mdCodeBlock: "#b5bd68",
22
+ mdCodeBlockBorder: "#808080",
23
+ mdQuote: "#808080",
24
+ mdQuoteBorder: "#808080",
25
+ mdHr: "#808080",
26
+ mdListBullet: "#8abeb7",
27
+ syntaxComment: "#6A9955",
28
+ syntaxKeyword: "#569CD6",
29
+ syntaxFunction: "#DCDCAA",
30
+ syntaxVariable: "#9CDCFE",
31
+ syntaxString: "#CE9178",
32
+ syntaxNumber: "#B5CEA8",
33
+ syntaxType: "#4EC9B0",
34
+ syntaxOperator: "#D4D4D4",
35
+ syntaxPunctuation: "#D4D4D4",
36
+ border: "#2d3748",
37
+ borderMuted: "#242b38",
38
+ accentSoft: "rgba(94, 161, 255, 0.35)",
39
+ accentSoftStrong: "rgba(94, 161, 255, 0.40)",
40
+ markerBorder: "rgba(94, 161, 255, 0.65)",
41
+ accentContrast: "#0e1616",
42
+ errorContrast: "#0e1616",
43
+ editorBg: "#171b24",
44
+ panelShadow: "0 1px 2px rgba(0, 0, 0, 0.36), 0 6px 18px rgba(0, 0, 0, 0.22)",
45
+ };
46
+ const BASE_LIGHT_PALETTE = {
47
+ bg: "#f5f7fb",
48
+ panel: "#ffffff",
49
+ panel2: "#f8fafc",
50
+ text: "#1f2328",
51
+ muted: "#57606a",
52
+ accent: "#0969da",
53
+ warn: "#9a6700",
54
+ error: "#cf222e",
55
+ ok: "#1a7f37",
56
+ markerBg: "rgba(9, 105, 218, 0.13)",
57
+ okBorder: "rgba(26, 127, 55, 0.55)",
58
+ warnBorder: "rgba(154, 103, 0, 0.55)",
59
+ mdHeading: "#9a7326",
60
+ mdLink: "#547da7",
61
+ mdLinkUrl: "#767676",
62
+ mdCode: "#5a8080",
63
+ mdCodeBlock: "#588458",
64
+ mdCodeBlockBorder: "#6c6c6c",
65
+ mdQuote: "#6c6c6c",
66
+ mdQuoteBorder: "#6c6c6c",
67
+ mdHr: "#6c6c6c",
68
+ mdListBullet: "#588458",
69
+ syntaxComment: "#008000",
70
+ syntaxKeyword: "#0000FF",
71
+ syntaxFunction: "#795E26",
72
+ syntaxVariable: "#001080",
73
+ syntaxString: "#A31515",
74
+ syntaxNumber: "#098658",
75
+ syntaxType: "#267F99",
76
+ syntaxOperator: "#000000",
77
+ syntaxPunctuation: "#000000",
78
+ border: "#d0d7de",
79
+ borderMuted: "#e0e6ee",
80
+ accentSoft: "rgba(9, 105, 218, 0.28)",
81
+ accentSoftStrong: "rgba(9, 105, 218, 0.35)",
82
+ markerBorder: "rgba(9, 105, 218, 0.45)",
83
+ accentContrast: "#ffffff",
84
+ errorContrast: "#ffffff",
85
+ editorBg: "#ffffff",
86
+ panelShadow: "0 1px 2px rgba(15, 23, 42, 0.03), 0 4px 14px rgba(15, 23, 42, 0.04)",
87
+ };
88
+ const KNOWN_THEME_FAMILIES = [
89
+ {
90
+ id: "aura",
91
+ match: /\baura\b/,
92
+ preference: "dark",
93
+ dark: {
94
+ bg: "#0f0f0f",
95
+ panel: "#15141b",
96
+ panel2: "#15141b",
97
+ text: "#edecee",
98
+ muted: "#6d6d6d",
99
+ accent: "#a277ff",
100
+ warn: "#ffca85",
101
+ error: "#ff6767",
102
+ ok: "#61ffca",
103
+ mdHeading: "#a277ff",
104
+ mdLink: "#f694ff",
105
+ mdLinkUrl: "#a277ff",
106
+ mdCode: "#61ffca",
107
+ mdQuote: "#6d6d6d",
108
+ mdListBullet: "#a277ff",
109
+ syntaxComment: "#6d6d6d",
110
+ syntaxKeyword: "#f694ff",
111
+ syntaxFunction: "#a277ff",
112
+ syntaxVariable: "#a277ff",
113
+ syntaxString: "#61ffca",
114
+ syntaxNumber: "#9dff65",
115
+ syntaxOperator: "#f694ff",
116
+ },
117
+ },
118
+ {
119
+ id: "github",
120
+ match: /github/,
121
+ light: {
122
+ bg: "#ffffff",
123
+ panel: "#f6f8fa",
124
+ panel2: "#f0f3f6",
125
+ text: "#24292f",
126
+ muted: "#57606a",
127
+ accent: "#1b7c83",
128
+ warn: "#9a6700",
129
+ error: "#cf222e",
130
+ ok: "#1a7f37",
131
+ mdHeading: "#0969da",
132
+ mdLink: "#0969da",
133
+ mdLinkUrl: "#1b7c83",
134
+ mdCode: "#bf3989",
135
+ mdQuote: "#57606a",
136
+ mdListBullet: "#0969da",
137
+ syntaxComment: "#57606a",
138
+ syntaxKeyword: "#cf222e",
139
+ syntaxFunction: "#8250df",
140
+ syntaxVariable: "#bc4c00",
141
+ syntaxString: "#0969da",
142
+ syntaxNumber: "#1b7c83",
143
+ syntaxOperator: "#cf222e",
144
+ },
145
+ dark: {
146
+ bg: "#0d1117",
147
+ panel: "#010409",
148
+ panel2: "#161b22",
149
+ text: "#c9d1d9",
150
+ muted: "#8b949e",
151
+ accent: "#39c5cf",
152
+ warn: "#e3b341",
153
+ error: "#f85149",
154
+ ok: "#3fb950",
155
+ mdHeading: "#58a6ff",
156
+ mdLink: "#58a6ff",
157
+ mdLinkUrl: "#39c5cf",
158
+ mdCode: "#ff7b72",
159
+ mdQuote: "#8b949e",
160
+ mdListBullet: "#58a6ff",
161
+ syntaxComment: "#8b949e",
162
+ syntaxKeyword: "#ff7b72",
163
+ syntaxFunction: "#bc8cff",
164
+ syntaxVariable: "#d29922",
165
+ syntaxString: "#39c5cf",
166
+ syntaxNumber: "#58a6ff",
167
+ syntaxOperator: "#ff7b72",
168
+ },
169
+ },
170
+ {
171
+ id: "momo-pro",
172
+ match: /(momo\s*pro|momo-pro|cutie\s*pro)/,
173
+ light: {
174
+ bg: "#ffffff",
175
+ panel: "#f6f8fa",
176
+ panel2: "#f0f3f6",
177
+ text: "#0e1116",
178
+ muted: "#656e77",
179
+ accent: "#1b7c83",
180
+ warn: "#4e2c00",
181
+ error: "#a0111f",
182
+ ok: "#024c1a",
183
+ mdHeading: "#622cbc",
184
+ mdLink: "#1b7c83",
185
+ mdLinkUrl: "#656e77",
186
+ mdCode: "#024c1a",
187
+ mdQuote: "#656e77",
188
+ mdListBullet: "#1b7c83",
189
+ syntaxComment: "#656e77",
190
+ syntaxKeyword: "#622cbc",
191
+ syntaxFunction: "#1b7c83",
192
+ syntaxVariable: "#0e1116",
193
+ syntaxString: "#024c1a",
194
+ syntaxNumber: "#a0111f",
195
+ syntaxOperator: "#0e1116",
196
+ },
197
+ dark: {
198
+ bg: "#181818",
199
+ panel: "#1f1f1f",
200
+ panel2: "#1a1a1a",
201
+ text: "#d5d0c9",
202
+ muted: "#88847f",
203
+ accent: "#42d9c5",
204
+ warn: "#f1bb79",
205
+ error: "#f56e7f",
206
+ ok: "#bec975",
207
+ mdHeading: "#d286b7",
208
+ mdLink: "#42d9c5",
209
+ mdLinkUrl: "#88847f",
210
+ mdCode: "#bec975",
211
+ mdQuote: "#f58669",
212
+ mdListBullet: "#42d9c5",
213
+ syntaxComment: "#88847f",
214
+ syntaxKeyword: "#d286b7",
215
+ syntaxFunction: "#42d9c5",
216
+ syntaxVariable: "#d5d0c9",
217
+ syntaxString: "#bec975",
218
+ syntaxNumber: "#f58669",
219
+ syntaxOperator: "#d5d0c9",
220
+ },
221
+ },
222
+ {
223
+ id: "catppuccin-latte",
224
+ match: /(catppuccin.*latte|\blatte\b)/,
225
+ preference: "light",
226
+ light: {
227
+ bg: "#eff1f5",
228
+ panel: "#ffffff",
229
+ panel2: "#e6e9ef",
230
+ text: "#4c4f69",
231
+ muted: "#6c6f85",
232
+ accent: "#1e66f5",
233
+ warn: "#df8e1d",
234
+ error: "#d20f39",
235
+ ok: "#40a02b",
236
+ mdHeading: "#8839ef",
237
+ mdLink: "#1e66f5",
238
+ mdLinkUrl: "#179299",
239
+ mdCode: "#fe640b",
240
+ mdQuote: "#8c8fa1",
241
+ mdListBullet: "#179299",
242
+ syntaxComment: "#8c8fa1",
243
+ syntaxKeyword: "#8839ef",
244
+ syntaxFunction: "#179299",
245
+ syntaxVariable: "#4c4f69",
246
+ syntaxString: "#40a02b",
247
+ syntaxNumber: "#fe640b",
248
+ syntaxOperator: "#4c4f69",
249
+ },
250
+ },
251
+ {
252
+ id: "catppuccin-frappe",
253
+ match: /(catppuccin.*frappe|\bfrappe\b)/,
254
+ preference: "dark",
255
+ dark: {
256
+ bg: "#303446",
257
+ panel: "#292c3c",
258
+ panel2: "#414559",
259
+ text: "#c6d0f5",
260
+ muted: "#a5adce",
261
+ accent: "#8caaee",
262
+ warn: "#e5c890",
263
+ error: "#e78284",
264
+ ok: "#a6d189",
265
+ mdHeading: "#f4b8e4",
266
+ mdLink: "#8caaee",
267
+ mdLinkUrl: "#81c8be",
268
+ mdCode: "#ef9f76",
269
+ mdQuote: "#737994",
270
+ mdListBullet: "#81c8be",
271
+ syntaxComment: "#737994",
272
+ syntaxKeyword: "#ca9ee6",
273
+ syntaxFunction: "#81c8be",
274
+ syntaxVariable: "#c6d0f5",
275
+ syntaxString: "#a6d189",
276
+ syntaxNumber: "#ef9f76",
277
+ syntaxOperator: "#b5bfe2",
278
+ },
279
+ },
280
+ {
281
+ id: "catppuccin-macchiato",
282
+ match: /(catppuccin.*macchiato|\bmacchiato\b)/,
283
+ preference: "dark",
284
+ dark: {
285
+ bg: "#24273a",
286
+ panel: "#1e2030",
287
+ panel2: "#363a4f",
288
+ text: "#cad3f5",
289
+ muted: "#a5adcb",
290
+ accent: "#8aadf4",
291
+ warn: "#eed49f",
292
+ error: "#ed8796",
293
+ ok: "#a6da95",
294
+ mdHeading: "#f5bde6",
295
+ mdLink: "#8aadf4",
296
+ mdLinkUrl: "#91d7e3",
297
+ mdCode: "#f5a97f",
298
+ mdQuote: "#6e738d",
299
+ mdListBullet: "#91d7e3",
300
+ syntaxComment: "#6e738d",
301
+ syntaxKeyword: "#c6a0f6",
302
+ syntaxFunction: "#8bd5ca",
303
+ syntaxVariable: "#cad3f5",
304
+ syntaxString: "#a6da95",
305
+ syntaxNumber: "#f5a97f",
306
+ syntaxOperator: "#b8c0e0",
307
+ },
308
+ },
309
+ {
310
+ id: "catppuccin-mocha",
311
+ match: /(catppuccin.*mocha|\bmocha\b|\bcatppuccin\b)/,
312
+ preference: "dark",
313
+ dark: {
314
+ bg: "#1e1e2e",
315
+ panel: "#181825",
316
+ panel2: "#313244",
317
+ text: "#cdd6f4",
318
+ muted: "#a6adc8",
319
+ accent: "#89b4fa",
320
+ warn: "#f9e2af",
321
+ error: "#f38ba8",
322
+ ok: "#a6e3a1",
323
+ mdHeading: "#f5c2e7",
324
+ mdLink: "#89b4fa",
325
+ mdLinkUrl: "#94e2d5",
326
+ mdCode: "#fab387",
327
+ mdQuote: "#6c7086",
328
+ mdListBullet: "#94e2d5",
329
+ syntaxComment: "#6c7086",
330
+ syntaxKeyword: "#cba6f7",
331
+ syntaxFunction: "#89dceb",
332
+ syntaxVariable: "#cdd6f4",
333
+ syntaxString: "#a6e3a1",
334
+ syntaxNumber: "#fab387",
335
+ syntaxOperator: "#bac2de",
336
+ },
337
+ },
338
+ {
339
+ id: "dracula",
340
+ match: /dracula/,
341
+ preference: "dark",
342
+ dark: {
343
+ bg: "#282a36",
344
+ panel: "#232530",
345
+ panel2: "#303341",
346
+ text: "#f8f8f2",
347
+ muted: "#a4acc4",
348
+ accent: "#bd93f9",
349
+ warn: "#ffb86c",
350
+ error: "#ff5555",
351
+ ok: "#50fa7b",
352
+ mdHeading: "#ff79c6",
353
+ mdLink: "#8be9fd",
354
+ mdLinkUrl: "#8be9fd",
355
+ mdCode: "#50fa7b",
356
+ mdQuote: "#6272a4",
357
+ mdListBullet: "#8be9fd",
358
+ syntaxComment: "#6272a4",
359
+ syntaxKeyword: "#ff79c6",
360
+ syntaxFunction: "#50fa7b",
361
+ syntaxVariable: "#8be9fd",
362
+ syntaxString: "#f1fa8c",
363
+ syntaxNumber: "#bd93f9",
364
+ syntaxOperator: "#f8f8f2",
365
+ },
366
+ },
367
+ {
368
+ id: "nord",
369
+ match: /nord/,
370
+ preference: "dark",
371
+ dark: {
372
+ bg: "#2e3440",
373
+ panel: "#3b4252",
374
+ panel2: "#434c5e",
375
+ text: "#eceff4",
376
+ muted: "#d8dee9",
377
+ accent: "#88c0d0",
378
+ warn: "#ebcb8b",
379
+ error: "#bf616a",
380
+ ok: "#a3be8c",
381
+ mdHeading: "#81a1c1",
382
+ mdLink: "#88c0d0",
383
+ mdLinkUrl: "#8fbcbb",
384
+ mdCode: "#a3be8c",
385
+ mdQuote: "#4c566a",
386
+ mdListBullet: "#81a1c1",
387
+ syntaxComment: "#616e88",
388
+ syntaxKeyword: "#81a1c1",
389
+ syntaxFunction: "#88c0d0",
390
+ syntaxVariable: "#d8dee9",
391
+ syntaxString: "#a3be8c",
392
+ syntaxNumber: "#b48ead",
393
+ syntaxOperator: "#eceff4",
394
+ },
395
+ },
396
+ {
397
+ id: "gruvbox-light",
398
+ match: /gruvbox.*light/,
399
+ preference: "light",
400
+ light: {
401
+ bg: "#fbf1c7",
402
+ panel: "#f9f5d7",
403
+ panel2: "#ebdbb2",
404
+ text: "#3c3836",
405
+ muted: "#7c6f64",
406
+ accent: "#458588",
407
+ warn: "#b57614",
408
+ error: "#cc241d",
409
+ ok: "#98971a",
410
+ mdHeading: "#d65d0e",
411
+ mdLink: "#458588",
412
+ mdLinkUrl: "#689d6a",
413
+ mdCode: "#b16286",
414
+ mdQuote: "#928374",
415
+ mdListBullet: "#98971a",
416
+ syntaxComment: "#928374",
417
+ syntaxKeyword: "#cc241d",
418
+ syntaxFunction: "#b57614",
419
+ syntaxVariable: "#458588",
420
+ syntaxString: "#98971a",
421
+ syntaxNumber: "#b16286",
422
+ syntaxOperator: "#3c3836",
423
+ },
424
+ },
425
+ {
426
+ id: "gruvbox-dark",
427
+ match: /gruvbox/,
428
+ preference: "dark",
429
+ dark: {
430
+ bg: "#282828",
431
+ panel: "#32302f",
432
+ panel2: "#3c3836",
433
+ text: "#ebdbb2",
434
+ muted: "#a89984",
435
+ accent: "#83a598",
436
+ warn: "#fabd2f",
437
+ error: "#fb4934",
438
+ ok: "#b8bb26",
439
+ mdHeading: "#fe8019",
440
+ mdLink: "#83a598",
441
+ mdLinkUrl: "#8ec07c",
442
+ mdCode: "#d3869b",
443
+ mdQuote: "#665c54",
444
+ mdListBullet: "#b8bb26",
445
+ syntaxComment: "#928374",
446
+ syntaxKeyword: "#fb4934",
447
+ syntaxFunction: "#fabd2f",
448
+ syntaxVariable: "#83a598",
449
+ syntaxString: "#b8bb26",
450
+ syntaxNumber: "#d3869b",
451
+ syntaxOperator: "#ebdbb2",
452
+ },
453
+ },
454
+ {
455
+ id: "tokyo-night",
456
+ match: /(tokyo\s*night|tokyonight)/,
457
+ preference: "dark",
458
+ dark: {
459
+ bg: "#1a1b26",
460
+ panel: "#1f2335",
461
+ panel2: "#24283b",
462
+ text: "#c0caf5",
463
+ muted: "#9aa5ce",
464
+ accent: "#7aa2f7",
465
+ warn: "#e0af68",
466
+ error: "#f7768e",
467
+ ok: "#9ece6a",
468
+ mdHeading: "#bb9af7",
469
+ mdLink: "#7aa2f7",
470
+ mdLinkUrl: "#7dcfff",
471
+ mdCode: "#9ece6a",
472
+ mdQuote: "#565f89",
473
+ mdListBullet: "#7dcfff",
474
+ syntaxComment: "#565f89",
475
+ syntaxKeyword: "#bb9af7",
476
+ syntaxFunction: "#7dcfff",
477
+ syntaxVariable: "#c0caf5",
478
+ syntaxString: "#9ece6a",
479
+ syntaxNumber: "#ff9e64",
480
+ syntaxOperator: "#c0caf5",
481
+ },
482
+ },
483
+ {
484
+ id: "rose-pine",
485
+ match: /(rose\s*pine|rosepine)/,
486
+ preference: "dark",
487
+ dark: {
488
+ bg: "#191724",
489
+ panel: "#1f1d2e",
490
+ panel2: "#26233a",
491
+ text: "#e0def4",
492
+ muted: "#908caa",
493
+ accent: "#9ccfd8",
494
+ warn: "#f6c177",
495
+ error: "#eb6f92",
496
+ ok: "#9ccfd8",
497
+ mdHeading: "#c4a7e7",
498
+ mdLink: "#9ccfd8",
499
+ mdLinkUrl: "#ebbcba",
500
+ mdCode: "#f6c177",
501
+ mdQuote: "#6e6a86",
502
+ mdListBullet: "#ebbcba",
503
+ syntaxComment: "#6e6a86",
504
+ syntaxKeyword: "#c4a7e7",
505
+ syntaxFunction: "#9ccfd8",
506
+ syntaxVariable: "#e0def4",
507
+ syntaxString: "#f6c177",
508
+ syntaxNumber: "#ea9a97",
509
+ syntaxOperator: "#e0def4",
510
+ },
511
+ },
512
+ {
513
+ id: "monokai",
514
+ match: /monokai/,
515
+ preference: "dark",
516
+ dark: {
517
+ bg: "#272822",
518
+ panel: "#2f3129",
519
+ panel2: "#3a3d32",
520
+ text: "#f8f8f2",
521
+ muted: "#a6a895",
522
+ accent: "#66d9ef",
523
+ warn: "#fd971f",
524
+ error: "#f92672",
525
+ ok: "#a6e22e",
526
+ mdHeading: "#ae81ff",
527
+ mdLink: "#66d9ef",
528
+ mdLinkUrl: "#a6e22e",
529
+ mdCode: "#fd971f",
530
+ mdQuote: "#75715e",
531
+ mdListBullet: "#a6e22e",
532
+ syntaxComment: "#75715e",
533
+ syntaxKeyword: "#f92672",
534
+ syntaxFunction: "#a6e22e",
535
+ syntaxVariable: "#66d9ef",
536
+ syntaxString: "#e6db74",
537
+ syntaxNumber: "#ae81ff",
538
+ syntaxOperator: "#f8f8f2",
539
+ },
540
+ },
541
+ {
542
+ id: "everforest",
543
+ match: /everforest/,
544
+ preference: "dark",
545
+ dark: {
546
+ bg: "#2d353b",
547
+ panel: "#343f44",
548
+ panel2: "#3d484d",
549
+ text: "#d3c6aa",
550
+ muted: "#859289",
551
+ accent: "#7fbbb3",
552
+ warn: "#dbbc7f",
553
+ error: "#e67e80",
554
+ ok: "#a7c080",
555
+ mdHeading: "#e69875",
556
+ mdLink: "#7fbbb3",
557
+ mdLinkUrl: "#83c092",
558
+ mdCode: "#d699b6",
559
+ mdQuote: "#5c6a72",
560
+ mdListBullet: "#83c092",
561
+ syntaxComment: "#5c6a72",
562
+ syntaxKeyword: "#e67e80",
563
+ syntaxFunction: "#dbbc7f",
564
+ syntaxVariable: "#7fbbb3",
565
+ syntaxString: "#a7c080",
566
+ syntaxNumber: "#d699b6",
567
+ syntaxOperator: "#d3c6aa",
568
+ },
569
+ },
570
+ {
571
+ id: "solarized-light",
572
+ match: /solarized.*light/,
573
+ preference: "light",
574
+ light: {
575
+ bg: "#fdf6e3",
576
+ panel: "#fffdf7",
577
+ panel2: "#f5efdc",
578
+ text: "#586e75",
579
+ muted: "#657b83",
580
+ accent: "#268bd2",
581
+ warn: "#b58900",
582
+ error: "#dc322f",
583
+ ok: "#859900",
584
+ mdHeading: "#cb4b16",
585
+ mdLink: "#268bd2",
586
+ mdLinkUrl: "#2aa198",
587
+ mdCode: "#859900",
588
+ mdQuote: "#93a1a1",
589
+ mdListBullet: "#2aa198",
590
+ syntaxComment: "#93a1a1",
591
+ syntaxKeyword: "#859900",
592
+ syntaxFunction: "#268bd2",
593
+ syntaxVariable: "#586e75",
594
+ syntaxString: "#2aa198",
595
+ syntaxNumber: "#d33682",
596
+ syntaxOperator: "#586e75",
597
+ },
598
+ },
599
+ {
600
+ id: "solarized-dark",
601
+ match: /solarized/,
602
+ preference: "dark",
603
+ dark: {
604
+ bg: "#002b36",
605
+ panel: "#073642",
606
+ panel2: "#0b3b46",
607
+ text: "#93a1a1",
608
+ muted: "#657b83",
609
+ accent: "#268bd2",
610
+ warn: "#b58900",
611
+ error: "#dc322f",
612
+ ok: "#859900",
613
+ mdHeading: "#cb4b16",
614
+ mdLink: "#268bd2",
615
+ mdLinkUrl: "#2aa198",
616
+ mdCode: "#859900",
617
+ mdQuote: "#586e75",
618
+ mdListBullet: "#2aa198",
619
+ syntaxComment: "#586e75",
620
+ syntaxKeyword: "#859900",
621
+ syntaxFunction: "#268bd2",
622
+ syntaxVariable: "#93a1a1",
623
+ syntaxString: "#2aa198",
624
+ syntaxNumber: "#d33682",
625
+ syntaxOperator: "#93a1a1",
626
+ },
627
+ },
628
+ ];
629
+ function getXdgStateDirectory() {
630
+ const configured = String(process.env.XDG_STATE_HOME ?? "").trim();
631
+ return configured || join(homedir(), ".local", "state");
632
+ }
633
+ function getXdgConfigDirectory() {
634
+ const configured = String(process.env.XDG_CONFIG_HOME ?? "").trim();
635
+ return configured || join(homedir(), ".config");
636
+ }
637
+ function normalizeThemeKey(raw) {
638
+ return raw.trim().toLowerCase().replace(/[^a-z0-9]+/g, " ").replace(/\s+/g, " ").trim();
639
+ }
640
+ function stripJsonComments(input) {
641
+ let out = "";
642
+ let quote = null;
643
+ let escaping = false;
644
+ let inLineComment = false;
645
+ let inBlockComment = false;
646
+ for (let i = 0; i < input.length; i += 1) {
647
+ const ch = input[i];
648
+ const next = input[i + 1] ?? "";
649
+ if (inLineComment) {
650
+ if (ch === "\n") {
651
+ inLineComment = false;
652
+ out += ch;
653
+ }
654
+ continue;
655
+ }
656
+ if (inBlockComment) {
657
+ if (ch === "*" && next === "/") {
658
+ inBlockComment = false;
659
+ i += 1;
660
+ }
661
+ continue;
662
+ }
663
+ if (quote) {
664
+ out += ch;
665
+ if (escaping) {
666
+ escaping = false;
667
+ }
668
+ else if (ch === "\\") {
669
+ escaping = true;
670
+ }
671
+ else if (ch === quote) {
672
+ quote = null;
673
+ }
674
+ continue;
675
+ }
676
+ if (ch === '"' || ch === "'") {
677
+ quote = ch;
678
+ out += ch;
679
+ continue;
680
+ }
681
+ if (ch === "/" && next === "/") {
682
+ inLineComment = true;
683
+ i += 1;
684
+ continue;
685
+ }
686
+ if (ch === "/" && next === "*") {
687
+ inBlockComment = true;
688
+ i += 1;
689
+ continue;
690
+ }
691
+ out += ch;
692
+ }
693
+ return out;
694
+ }
695
+ async function readJsonValue(path) {
696
+ try {
697
+ const raw = await readFile(path, "utf8");
698
+ const parsed = JSON.parse(stripJsonComments(raw));
699
+ return parsed && typeof parsed === "object" ? parsed : null;
700
+ }
701
+ catch {
702
+ return null;
703
+ }
704
+ }
705
+ async function readConfiguredTheme() {
706
+ const configPaths = [
707
+ join(getXdgConfigDirectory(), "opencode", "opencode.jsonc"),
708
+ join(getXdgConfigDirectory(), "opencode", "opencode.json"),
709
+ join(getXdgConfigDirectory(), "opencode", "config.json"),
710
+ ];
711
+ let configTheme = "";
712
+ let configMode = null;
713
+ for (const path of configPaths) {
714
+ const parsed = await readJsonValue(path);
715
+ const nextTheme = typeof parsed?.theme === "string" ? parsed.theme.trim() : "";
716
+ const nextMode = typeof parsed?.theme_mode === "string" ? parsed.theme_mode.trim().toLowerCase() : "";
717
+ if (!configTheme && nextTheme) {
718
+ configTheme = nextTheme;
719
+ }
720
+ if (!configMode && (nextMode === "light" || nextMode === "dark")) {
721
+ configMode = nextMode;
722
+ }
723
+ if (configTheme && configMode) {
724
+ break;
725
+ }
726
+ }
727
+ const localState = await readJsonValue(join(getXdgStateDirectory(), "opencode", "kv.json"));
728
+ const localTheme = typeof localState?.theme === "string" ? localState.theme.trim() : "";
729
+ const localMode = typeof localState?.theme_mode === "string" ? localState.theme_mode.trim().toLowerCase() : "";
730
+ const resolvedLocalMode = localMode === "light" || localMode === "dark" ? localMode : null;
731
+ if (configTheme) {
732
+ return {
733
+ raw: configTheme,
734
+ source: "opencode-config",
735
+ mode: resolvedLocalMode ?? configMode,
736
+ };
737
+ }
738
+ if (localTheme) {
739
+ return {
740
+ raw: localTheme,
741
+ source: "opencode-local-state",
742
+ mode: resolvedLocalMode ?? configMode,
743
+ };
744
+ }
745
+ return { raw: null, source: "default", mode: configMode };
746
+ }
747
+ async function readFirstExistingTextFile(paths) {
748
+ for (const path of paths) {
749
+ try {
750
+ const text = await readFile(path, "utf8");
751
+ if (text.trim())
752
+ return text;
753
+ }
754
+ catch {
755
+ // ignore missing/unreadable paths
756
+ }
757
+ }
758
+ return null;
759
+ }
760
+ function normalizeThemeNameToken(value) {
761
+ const trimmed = value.trim().replace(/^['"]|['"]$/g, "").trim();
762
+ return trimmed || null;
763
+ }
764
+ function normalizeThemeModeToken(value) {
765
+ const normalized = String(value ?? "").trim().toLowerCase();
766
+ return normalized === "light" || normalized === "dark" ? normalized : null;
767
+ }
768
+ function shouldUseGhosttyTheme(configured) {
769
+ const key = normalizeThemeKey(configured.raw ?? "");
770
+ return key === "system" || key === "auto";
771
+ }
772
+ function getGhosttyConfigPaths() {
773
+ return [
774
+ join(getXdgConfigDirectory(), "ghostty", "config"),
775
+ join(homedir(), "Library", "Application Support", "com.mitchellh.ghostty", "config"),
776
+ ];
777
+ }
778
+ function getGhosttyThemeSearchPaths(themeName) {
779
+ return [
780
+ join(getXdgConfigDirectory(), "ghostty", "themes", themeName),
781
+ join(homedir(), "Library", "Application Support", "com.mitchellh.ghostty", "themes", themeName),
782
+ join("/Applications", "Ghostty.app", "Contents", "Resources", "ghostty", "themes", themeName),
783
+ ];
784
+ }
785
+ async function resolveGhosttyThemePath(themeName) {
786
+ const candidates = getGhosttyThemeSearchPaths(themeName);
787
+ for (const candidate of candidates) {
788
+ try {
789
+ const text = await readFile(candidate, "utf8");
790
+ if (text.trim())
791
+ return candidate;
792
+ }
793
+ catch {
794
+ // ignore exact-path misses
795
+ }
796
+ }
797
+ for (const candidate of candidates) {
798
+ const parent = dirname(candidate);
799
+ const leaf = candidate.split("/").pop()?.toLowerCase();
800
+ if (!leaf)
801
+ continue;
802
+ try {
803
+ const entries = await readdir(parent);
804
+ const match = entries.find((entry) => entry.toLowerCase() === leaf);
805
+ if (match)
806
+ return join(parent, match);
807
+ }
808
+ catch {
809
+ // ignore missing directories
810
+ }
811
+ }
812
+ return null;
813
+ }
814
+ export function parseGhosttyThemeReference(value) {
815
+ const out = { single: null, light: null, dark: null };
816
+ const raw = String(value ?? "").trim();
817
+ if (!raw)
818
+ return out;
819
+ for (const segment of raw.split(",")) {
820
+ const trimmed = segment.trim();
821
+ if (!trimmed)
822
+ continue;
823
+ const modeMatch = trimmed.match(/^(dark|light)\s*:\s*(.+)$/i);
824
+ if (modeMatch) {
825
+ const mode = normalizeThemeModeToken(modeMatch[1]);
826
+ const name = normalizeThemeNameToken(modeMatch[2] ?? "");
827
+ if (mode === "dark")
828
+ out.dark = name;
829
+ if (mode === "light")
830
+ out.light = name;
831
+ continue;
832
+ }
833
+ out.single = normalizeThemeNameToken(trimmed);
834
+ }
835
+ if (!out.light && out.single)
836
+ out.light = out.single;
837
+ if (!out.dark && out.single)
838
+ out.dark = out.single;
839
+ return out;
840
+ }
841
+ async function readGhosttyThemeReference() {
842
+ const content = await readFirstExistingTextFile(getGhosttyConfigPaths());
843
+ if (!content)
844
+ return null;
845
+ let themeValue = null;
846
+ for (const rawLine of content.split(/\r?\n/)) {
847
+ const line = rawLine.trim();
848
+ if (!line || line.startsWith("#"))
849
+ continue;
850
+ const match = line.match(/^theme\s*=\s*(.+)$/i);
851
+ if (!match)
852
+ continue;
853
+ themeValue = match[1]?.trim() ?? null;
854
+ }
855
+ if (!themeValue)
856
+ return null;
857
+ const parsed = parseGhosttyThemeReference(themeValue);
858
+ return parsed.single || parsed.light || parsed.dark ? parsed : null;
859
+ }
860
+ async function readGhosttyThemeDefinition(themeName) {
861
+ const resolvedPath = await resolveGhosttyThemePath(themeName);
862
+ if (!resolvedPath)
863
+ return null;
864
+ const content = await readFile(resolvedPath, "utf8");
865
+ if (!content.trim())
866
+ return null;
867
+ const definition = {
868
+ name: themeName,
869
+ palette: new Map(),
870
+ };
871
+ for (const rawLine of content.split(/\r?\n/)) {
872
+ const line = rawLine.trim();
873
+ if (!line || line.startsWith("#"))
874
+ continue;
875
+ const paletteMatch = line.match(/^palette\s*=\s*(\d{1,2})\s*=\s*(#[0-9a-fA-F]{3,6})$/i);
876
+ if (paletteMatch) {
877
+ definition.palette.set(Number.parseInt(paletteMatch[1] ?? "", 10), paletteMatch[2].toLowerCase());
878
+ continue;
879
+ }
880
+ const colorMatch = line.match(/^(background|foreground|cursor-color|cursor-text|selection-background|selection-foreground)\s*=\s*(#[0-9a-fA-F]{3,6})$/i);
881
+ if (!colorMatch)
882
+ continue;
883
+ const key = colorMatch[1].toLowerCase();
884
+ const value = colorMatch[2].toLowerCase();
885
+ if (key === "background")
886
+ definition.background = value;
887
+ else if (key === "foreground")
888
+ definition.foreground = value;
889
+ else if (key === "cursor-color")
890
+ definition.cursorColor = value;
891
+ else if (key === "cursor-text")
892
+ definition.cursorText = value;
893
+ else if (key === "selection-background")
894
+ definition.selectionBackground = value;
895
+ else if (key === "selection-foreground")
896
+ definition.selectionForeground = value;
897
+ }
898
+ if (!definition.background || !definition.foreground) {
899
+ return null;
900
+ }
901
+ return definition;
902
+ }
903
+ function getOpencodeThemeNameCandidates(raw) {
904
+ const trimmed = raw.trim();
905
+ const normalized = normalizeThemeKey(trimmed);
906
+ const hyphenated = normalized.replace(/\s+/g, "-");
907
+ return Array.from(new Set([
908
+ trimmed,
909
+ trimmed.toLowerCase(),
910
+ normalized,
911
+ hyphenated,
912
+ ].map((value) => value.trim()).filter(Boolean)));
913
+ }
914
+ function isPrototypeOpencodeThemeInput(value) {
915
+ if (!value || typeof value !== "object")
916
+ return false;
917
+ const theme = value.theme;
918
+ return Boolean(theme && typeof theme === "object" && !Array.isArray(theme));
919
+ }
920
+ async function readFirstExistingJsonFile(paths, guard) {
921
+ for (const path of paths) {
922
+ const parsed = await readJsonValue(path);
923
+ if (guard(parsed)) {
924
+ return parsed;
925
+ }
926
+ }
927
+ return null;
928
+ }
929
+ function getPrototypeOpencodeThemeWorkspacePaths(themeName, directory) {
930
+ if (!directory)
931
+ return [];
932
+ const paths = [];
933
+ let current = resolve(directory);
934
+ for (;;) {
935
+ paths.push(join(current, ".opencode", "themes", `${themeName}.json`));
936
+ const parent = dirname(current);
937
+ if (parent === current)
938
+ break;
939
+ current = parent;
940
+ }
941
+ return paths;
942
+ }
943
+ async function getPrototypeOpencodeBuiltinThemePaths(themeName) {
944
+ const cacheDir = join(homedir(), ".bun", "install", "cache");
945
+ const paths = [join(cacheDir, "critique", "src", "themes", `${themeName}.json`)];
946
+ try {
947
+ const entries = await readdir(cacheDir);
948
+ for (const entry of entries) {
949
+ if (!entry.startsWith("critique@"))
950
+ continue;
951
+ paths.push(join(cacheDir, entry, "src", "themes", `${themeName}.json`));
952
+ }
953
+ }
954
+ catch {
955
+ // ignore missing bun cache directories
956
+ }
957
+ return paths;
958
+ }
959
+ async function readPrototypeOpencodeThemeDefinition(rawThemeName, directory) {
960
+ for (const themeName of getOpencodeThemeNameCandidates(rawThemeName)) {
961
+ const candidatePaths = [
962
+ join(getXdgConfigDirectory(), "opencode", "themes", `${themeName}.json`),
963
+ ...getPrototypeOpencodeThemeWorkspacePaths(themeName, directory),
964
+ ...(await getPrototypeOpencodeBuiltinThemePaths(themeName)),
965
+ ];
966
+ const definition = await readFirstExistingJsonFile(candidatePaths, isPrototypeOpencodeThemeInput);
967
+ if (definition) {
968
+ return definition;
969
+ }
970
+ }
971
+ return null;
972
+ }
973
+ function ansiColorCodeToHex(code) {
974
+ const normalized = Math.max(0, Math.min(255, Math.trunc(code)));
975
+ const standardAnsi = [
976
+ "#000000",
977
+ "#800000",
978
+ "#008000",
979
+ "#808000",
980
+ "#000080",
981
+ "#800080",
982
+ "#008080",
983
+ "#c0c0c0",
984
+ "#808080",
985
+ "#ff0000",
986
+ "#00ff00",
987
+ "#ffff00",
988
+ "#0000ff",
989
+ "#ff00ff",
990
+ "#00ffff",
991
+ "#ffffff",
992
+ ];
993
+ if (normalized < 16) {
994
+ return standardAnsi[normalized] ?? "#808080";
995
+ }
996
+ if (normalized < 232) {
997
+ const index = normalized - 16;
998
+ const blue = index % 6;
999
+ const green = Math.floor(index / 6) % 6;
1000
+ const red = Math.floor(index / 36);
1001
+ const component = (value) => (value === 0 ? 0 : value * 40 + 55);
1002
+ return rgbToHex(component(red), component(green), component(blue));
1003
+ }
1004
+ const gray = (normalized - 232) * 10 + 8;
1005
+ return rgbToHex(gray, gray, gray);
1006
+ }
1007
+ function isTransparentThemeColor(value) {
1008
+ const normalized = String(value ?? "").trim().toLowerCase();
1009
+ return normalized === "transparent" || normalized === "none";
1010
+ }
1011
+ function resolvePrototypeOpencodeThemeColor(definition, value, mode, seen = new Set()) {
1012
+ if (typeof value === "string") {
1013
+ const trimmed = value.trim();
1014
+ if (!trimmed)
1015
+ return null;
1016
+ if (isTransparentThemeColor(trimmed))
1017
+ return "transparent";
1018
+ if (/^#[0-9a-fA-F]{3,6}$/.test(trimmed))
1019
+ return trimmed.toLowerCase();
1020
+ if (definition.defs && Object.prototype.hasOwnProperty.call(definition.defs, trimmed)) {
1021
+ const key = `def:${trimmed}`;
1022
+ if (seen.has(key))
1023
+ return null;
1024
+ seen.add(key);
1025
+ return resolvePrototypeOpencodeThemeColor(definition, definition.defs[trimmed], mode, seen);
1026
+ }
1027
+ if (Object.prototype.hasOwnProperty.call(definition.theme, trimmed)) {
1028
+ const key = `theme:${trimmed}`;
1029
+ if (seen.has(key))
1030
+ return null;
1031
+ seen.add(key);
1032
+ return resolvePrototypeOpencodeThemeColor(definition, definition.theme[trimmed], mode, seen);
1033
+ }
1034
+ return null;
1035
+ }
1036
+ if (typeof value === "number" && Number.isFinite(value)) {
1037
+ return ansiColorCodeToHex(value);
1038
+ }
1039
+ if (value && typeof value === "object" && !Array.isArray(value)) {
1040
+ const variant = value;
1041
+ if (Object.prototype.hasOwnProperty.call(variant, "dark") || Object.prototype.hasOwnProperty.call(variant, "light")) {
1042
+ return resolvePrototypeOpencodeThemeColor(definition, variant[mode] ?? variant.dark ?? variant.light, mode, seen);
1043
+ }
1044
+ }
1045
+ return null;
1046
+ }
1047
+ function resolvePrototypeOpencodeThemeSlot(definition, key, mode) {
1048
+ return resolvePrototypeOpencodeThemeColor(definition, definition.theme[key], mode);
1049
+ }
1050
+ function pickSolidThemeColor(primary, fallback) {
1051
+ if (!primary || isTransparentThemeColor(primary))
1052
+ return fallback;
1053
+ return primary;
1054
+ }
1055
+ function buildOpencodePaletteSeed(definition, mode) {
1056
+ const base = mode === "light" ? BASE_LIGHT_PALETTE : BASE_DARK_PALETTE;
1057
+ const text = pickSolidThemeColor(resolvePrototypeOpencodeThemeSlot(definition, "text", mode), base.text);
1058
+ const muted = pickSolidThemeColor(resolvePrototypeOpencodeThemeSlot(definition, "textMuted", mode), base.muted);
1059
+ const bg = pickSolidThemeColor(resolvePrototypeOpencodeThemeSlot(definition, "background", mode), base.bg);
1060
+ const panel = pickSolidThemeColor(resolvePrototypeOpencodeThemeSlot(definition, "backgroundPanel", mode), pickSolidThemeColor(resolvePrototypeOpencodeThemeSlot(definition, "backgroundElement", mode), mode === "light" ? blendHexColors(bg, text, 0.03) : blendHexColors(bg, text, 0.07)));
1061
+ const panel2 = pickSolidThemeColor(resolvePrototypeOpencodeThemeSlot(definition, "backgroundElement", mode), derivePrototypePanel2Color(panel, bg, muted, mode));
1062
+ const primary = pickSolidThemeColor(resolvePrototypeOpencodeThemeSlot(definition, "primary", mode), base.accent);
1063
+ const secondary = pickSolidThemeColor(resolvePrototypeOpencodeThemeSlot(definition, "secondary", mode), primary);
1064
+ const info = pickSolidThemeColor(resolvePrototypeOpencodeThemeSlot(definition, "info", mode), secondary);
1065
+ const warning = pickSolidThemeColor(resolvePrototypeOpencodeThemeSlot(definition, "warning", mode), base.warn);
1066
+ const error = pickSolidThemeColor(resolvePrototypeOpencodeThemeSlot(definition, "error", mode), base.error);
1067
+ const success = pickSolidThemeColor(resolvePrototypeOpencodeThemeSlot(definition, "success", mode), base.ok);
1068
+ const border = pickSolidThemeColor(resolvePrototypeOpencodeThemeSlot(definition, "border", mode), blendHexColors(text, bg, mode === "light" ? 0.82 : 0.76));
1069
+ const borderSubtle = pickSolidThemeColor(resolvePrototypeOpencodeThemeSlot(definition, "borderSubtle", mode), blendHexColors(text, bg, mode === "light" ? 0.90 : 0.86));
1070
+ const borderActive = pickSolidThemeColor(resolvePrototypeOpencodeThemeSlot(definition, "borderActive", mode), primary);
1071
+ const syntaxComment = pickSolidThemeColor(resolvePrototypeOpencodeThemeSlot(definition, "syntaxComment", mode), muted);
1072
+ const syntaxKeyword = pickSolidThemeColor(resolvePrototypeOpencodeThemeSlot(definition, "syntaxKeyword", mode), primary);
1073
+ const syntaxFunction = pickSolidThemeColor(resolvePrototypeOpencodeThemeSlot(definition, "syntaxFunction", mode), secondary);
1074
+ const syntaxVariable = pickSolidThemeColor(resolvePrototypeOpencodeThemeSlot(definition, "syntaxVariable", mode), text);
1075
+ const syntaxString = pickSolidThemeColor(resolvePrototypeOpencodeThemeSlot(definition, "syntaxString", mode), success);
1076
+ const syntaxNumber = pickSolidThemeColor(resolvePrototypeOpencodeThemeSlot(definition, "syntaxNumber", mode), warning);
1077
+ const syntaxType = pickSolidThemeColor(resolvePrototypeOpencodeThemeSlot(definition, "syntaxType", mode), secondary);
1078
+ const syntaxOperator = pickSolidThemeColor(resolvePrototypeOpencodeThemeSlot(definition, "syntaxOperator", mode), text);
1079
+ const syntaxPunctuation = pickSolidThemeColor(resolvePrototypeOpencodeThemeSlot(definition, "syntaxPunctuation", mode), text);
1080
+ const mdHeading = pickSolidThemeColor(resolvePrototypeOpencodeThemeSlot(definition, "markdownHeading", mode), primary);
1081
+ const mdLink = pickSolidThemeColor(resolvePrototypeOpencodeThemeSlot(definition, "markdownLink", mode), primary);
1082
+ const mdLinkText = pickSolidThemeColor(resolvePrototypeOpencodeThemeSlot(definition, "markdownLinkText", mode), muted);
1083
+ const mdCode = pickSolidThemeColor(resolvePrototypeOpencodeThemeSlot(definition, "markdownCode", mode), syntaxString);
1084
+ const mdCodeBlock = pickSolidThemeColor(resolvePrototypeOpencodeThemeSlot(definition, "markdownCodeBlock", mode), text);
1085
+ const mdQuote = pickSolidThemeColor(resolvePrototypeOpencodeThemeSlot(definition, "markdownBlockQuote", mode), syntaxComment);
1086
+ const mdHr = pickSolidThemeColor(resolvePrototypeOpencodeThemeSlot(definition, "markdownHorizontalRule", mode), borderSubtle);
1087
+ const mdListBullet = pickSolidThemeColor(resolvePrototypeOpencodeThemeSlot(definition, "markdownListItem", mode), primary);
1088
+ return {
1089
+ bg,
1090
+ panel,
1091
+ panel2,
1092
+ text,
1093
+ muted,
1094
+ accent: primary,
1095
+ warn: warning,
1096
+ error,
1097
+ ok: success,
1098
+ mdHeading,
1099
+ mdLink,
1100
+ mdLinkUrl: mdLinkText,
1101
+ mdCode,
1102
+ mdCodeBlock,
1103
+ mdCodeBlockBorder: borderSubtle,
1104
+ mdQuote,
1105
+ mdQuoteBorder: borderActive,
1106
+ mdHr,
1107
+ mdListBullet,
1108
+ syntaxComment,
1109
+ syntaxKeyword,
1110
+ syntaxFunction,
1111
+ syntaxVariable,
1112
+ syntaxString,
1113
+ syntaxNumber,
1114
+ syntaxType,
1115
+ syntaxOperator,
1116
+ syntaxPunctuation,
1117
+ border,
1118
+ borderMuted: borderSubtle,
1119
+ editorBg: panel,
1120
+ };
1121
+ }
1122
+ function rgbToHex(r, g, b) {
1123
+ return `#${[r, g, b].map((value) => Math.max(0, Math.min(255, Math.round(value))).toString(16).padStart(2, "0")).join("")}`;
1124
+ }
1125
+ function blendHexColors(base, mix, amount) {
1126
+ const a = hexToRgb(base);
1127
+ const b = hexToRgb(mix);
1128
+ if (!a || !b)
1129
+ return base;
1130
+ const t = Math.max(0, Math.min(1, amount));
1131
+ return rgbToHex(a.r + (b.r - a.r) * t, a.g + (b.g - a.g) * t, a.b + (b.b - a.b) * t);
1132
+ }
1133
+ function pickPaletteColor(palette, indices, fallback) {
1134
+ for (const index of indices) {
1135
+ const value = palette.get(index);
1136
+ if (value)
1137
+ return value;
1138
+ }
1139
+ return fallback;
1140
+ }
1141
+ function buildGhosttyPaletteSeed(definition, mode) {
1142
+ const base = mode === "light" ? BASE_LIGHT_PALETTE : BASE_DARK_PALETTE;
1143
+ const bg = definition.background;
1144
+ const text = definition.foreground;
1145
+ const muted = pickPaletteColor(definition.palette, [8, 7], blendHexColors(text, bg, 0.45));
1146
+ const accent = pickPaletteColor(definition.palette, [4, 12, 6, 14], definition.cursorColor ?? base.accent);
1147
+ const warn = pickPaletteColor(definition.palette, [11, 3], base.warn);
1148
+ const error = pickPaletteColor(definition.palette, [1, 9], base.error);
1149
+ const ok = pickPaletteColor(definition.palette, [2, 10], base.ok);
1150
+ const keyword = pickPaletteColor(definition.palette, [5, 13], accent);
1151
+ const panel = blendHexColors(bg, text, mode === "light" ? 0.03 : 0.07);
1152
+ const panel2 = blendHexColors(bg, text, mode === "light" ? 0.06 : 0.12);
1153
+ return {
1154
+ bg,
1155
+ panel,
1156
+ panel2,
1157
+ text,
1158
+ muted,
1159
+ accent,
1160
+ warn,
1161
+ error,
1162
+ ok,
1163
+ mdHeading: keyword,
1164
+ mdLink: accent,
1165
+ mdLinkUrl: muted,
1166
+ mdCode: ok,
1167
+ mdCodeBlock: ok,
1168
+ mdCodeBlockBorder: muted,
1169
+ mdQuote: muted,
1170
+ mdQuoteBorder: accent,
1171
+ mdHr: muted,
1172
+ mdListBullet: accent,
1173
+ syntaxComment: muted,
1174
+ syntaxKeyword: keyword,
1175
+ syntaxFunction: accent,
1176
+ syntaxVariable: text,
1177
+ syntaxString: ok,
1178
+ syntaxNumber: warn,
1179
+ syntaxType: accent,
1180
+ syntaxOperator: text,
1181
+ syntaxPunctuation: text,
1182
+ editorBg: panel,
1183
+ border: blendHexColors(text, bg, mode === "light" ? 0.82 : 0.76),
1184
+ borderMuted: blendHexColors(text, bg, mode === "light" ? 0.90 : 0.86),
1185
+ };
1186
+ }
1187
+ export function buildPrototypeThemeVarsFromGhosttyTheme(definition, mode) {
1188
+ return buildThemeVars(mode, buildGhosttyPaletteSeed(definition, mode));
1189
+ }
1190
+ export function buildPrototypeThemeVarsFromOpencodeTheme(definition, mode) {
1191
+ return buildThemeVars(mode, buildOpencodePaletteSeed(definition, mode));
1192
+ }
1193
+ async function buildGhosttyThemeDescriptor(configured) {
1194
+ const reference = shouldUseGhosttyTheme(configured)
1195
+ ? await readGhosttyThemeReference()
1196
+ : (configured.raw ? parseGhosttyThemeReference(configured.raw) : null);
1197
+ if (!reference)
1198
+ return null;
1199
+ const effectiveMode = configured.mode;
1200
+ const lightName = reference.light;
1201
+ const darkName = reference.dark;
1202
+ const currentName = effectiveMode === "light"
1203
+ ? (lightName ?? darkName)
1204
+ : effectiveMode === "dark"
1205
+ ? (darkName ?? lightName)
1206
+ : (reference.single ?? darkName ?? lightName);
1207
+ const lightTheme = lightName ? await readGhosttyThemeDefinition(lightName) : null;
1208
+ const darkTheme = darkName ? await readGhosttyThemeDefinition(darkName) : null;
1209
+ const currentTheme = currentName ? await readGhosttyThemeDefinition(currentName) : null;
1210
+ if (!lightTheme && !darkTheme && !currentTheme) {
1211
+ return null;
1212
+ }
1213
+ const inferredPreference = effectiveMode ?? inferThemePreference(currentName ?? configured.raw);
1214
+ const singleThemeReference = Boolean(reference.single) && lightName === darkName;
1215
+ let lightSeed = lightTheme ? buildGhosttyPaletteSeed(lightTheme, "light") : undefined;
1216
+ let darkSeed = darkTheme ? buildGhosttyPaletteSeed(darkTheme, "dark") : undefined;
1217
+ if (singleThemeReference && currentTheme) {
1218
+ if (inferredPreference === "light") {
1219
+ lightSeed = buildGhosttyPaletteSeed(currentTheme, "light");
1220
+ darkSeed = undefined;
1221
+ }
1222
+ else if (inferredPreference === "dark") {
1223
+ lightSeed = undefined;
1224
+ darkSeed = buildGhosttyPaletteSeed(currentTheme, "dark");
1225
+ }
1226
+ else {
1227
+ lightSeed = buildGhosttyPaletteSeed(currentTheme, "light");
1228
+ darkSeed = buildGhosttyPaletteSeed(currentTheme, "dark");
1229
+ }
1230
+ }
1231
+ return {
1232
+ raw: currentName ?? configured.raw,
1233
+ preference: inferredPreference === "system" && lightSeed && darkSeed ? "system" : inferredPreference,
1234
+ source: shouldUseGhosttyTheme(configured) ? "ghostty-config" : configured.source,
1235
+ family: null,
1236
+ lightVars: buildThemeVars("light", lightSeed),
1237
+ darkVars: buildThemeVars("dark", darkSeed),
1238
+ };
1239
+ }
1240
+ async function buildOpencodeThemeDescriptor(configured, directory) {
1241
+ const raw = configured.raw?.trim();
1242
+ const normalized = normalizeThemeKey(raw ?? "");
1243
+ if (!raw || normalized === "system" || normalized === "auto") {
1244
+ return null;
1245
+ }
1246
+ const definition = await readPrototypeOpencodeThemeDefinition(raw, directory);
1247
+ if (!definition) {
1248
+ return null;
1249
+ }
1250
+ const preference = configured.mode ?? inferThemePreference(raw);
1251
+ return {
1252
+ raw,
1253
+ preference,
1254
+ source: configured.source,
1255
+ family: null,
1256
+ lightVars: buildPrototypeThemeVarsFromOpencodeTheme(definition, "light"),
1257
+ darkVars: buildPrototypeThemeVarsFromOpencodeTheme(definition, "dark"),
1258
+ };
1259
+ }
1260
+ function hexToRgb(color) {
1261
+ const value = color.trim();
1262
+ const long = value.match(/^#([0-9a-fA-F]{6})$/);
1263
+ if (long) {
1264
+ const hex = long[1];
1265
+ return {
1266
+ r: Number.parseInt(hex.slice(0, 2), 16),
1267
+ g: Number.parseInt(hex.slice(2, 4), 16),
1268
+ b: Number.parseInt(hex.slice(4, 6), 16),
1269
+ };
1270
+ }
1271
+ const short = value.match(/^#([0-9a-fA-F]{3})$/);
1272
+ if (short) {
1273
+ const hex = short[1];
1274
+ return {
1275
+ r: Number.parseInt(hex[0] + hex[0], 16),
1276
+ g: Number.parseInt(hex[1] + hex[1], 16),
1277
+ b: Number.parseInt(hex[2] + hex[2], 16),
1278
+ };
1279
+ }
1280
+ return null;
1281
+ }
1282
+ function withAlpha(color, alpha, fallback) {
1283
+ const rgb = hexToRgb(color);
1284
+ if (!rgb)
1285
+ return fallback;
1286
+ const clamped = Math.max(0, Math.min(1, alpha));
1287
+ return `rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, ${clamped.toFixed(2)})`;
1288
+ }
1289
+ function relativeLuminance(color) {
1290
+ const rgb = hexToRgb(color);
1291
+ if (!rgb)
1292
+ return 0;
1293
+ return (0.299 * rgb.r + 0.587 * rgb.g + 0.114 * rgb.b) / 255;
1294
+ }
1295
+ function defaultContrastColor(color, darkText, lightText) {
1296
+ return relativeLuminance(color) >= 0.6 ? darkText : lightText;
1297
+ }
1298
+ function inferThemePreference(raw) {
1299
+ if (!raw)
1300
+ return "system";
1301
+ const key = normalizeThemeKey(raw);
1302
+ if (!key)
1303
+ return "system";
1304
+ if (key === "system" || key === "auto" || key.includes("system"))
1305
+ return "system";
1306
+ if (/\b(light|day|latte|dawn)\b/.test(key))
1307
+ return "light";
1308
+ if (/\b(dark|night|mocha|macchiato|frappe|moon)\b/.test(key))
1309
+ return "dark";
1310
+ return "dark";
1311
+ }
1312
+ function resolveThemeFamily(raw) {
1313
+ if (!raw)
1314
+ return undefined;
1315
+ const key = normalizeThemeKey(raw);
1316
+ return KNOWN_THEME_FAMILIES.find((entry) => entry.match.test(key));
1317
+ }
1318
+ function getThemeFamilySeed(family, mode) {
1319
+ return mode === "light" ? family?.light : family?.dark;
1320
+ }
1321
+ function resolveThemePreference(raw, family, modeOverride) {
1322
+ if (modeOverride) {
1323
+ if (!family)
1324
+ return modeOverride;
1325
+ if (modeOverride === "light" && family.light)
1326
+ return "light";
1327
+ if (modeOverride === "dark" && family.dark)
1328
+ return "dark";
1329
+ }
1330
+ return family?.preference ?? inferThemePreference(raw);
1331
+ }
1332
+ function normalizeColorToken(value) {
1333
+ return String(value ?? "").trim().toLowerCase();
1334
+ }
1335
+ function derivePrototypePanel2Color(panel, bg, muted, mode) {
1336
+ return mode === "light"
1337
+ ? blendHexColors(panel, muted, 0.06)
1338
+ : blendHexColors(panel, bg, 0.45);
1339
+ }
1340
+ function buildThemeVars(mode, override) {
1341
+ const base = mode === "light" ? BASE_LIGHT_PALETTE : BASE_DARK_PALETTE;
1342
+ const explicit = override ?? {};
1343
+ const palette = {
1344
+ ...base,
1345
+ ...explicit,
1346
+ };
1347
+ const panel2 = !palette.panel2 || normalizeColorToken(palette.panel2) === normalizeColorToken(palette.panel)
1348
+ ? derivePrototypePanel2Color(palette.panel, palette.bg, palette.muted, mode)
1349
+ : palette.panel2;
1350
+ const editorBg = explicit.editorBg ?? (mode === "light"
1351
+ ? blendHexColors(palette.panel, "#ffffff", 0.5)
1352
+ : palette.panel);
1353
+ const border = palette.border ?? withAlpha(palette.text, mode === "light" ? 0.12 : 0.10, base.border ?? "rgba(0, 0, 0, 0.1)");
1354
+ const borderMuted = palette.borderMuted ?? withAlpha(palette.text, mode === "light" ? 0.10 : 0.08, base.borderMuted ?? "rgba(0, 0, 0, 0.08)");
1355
+ const accentSoft = explicit.accentSoft ?? withAlpha(palette.accent, mode === "light" ? 0.28 : 0.35, base.accentSoft ?? "rgba(0, 0, 0, 0.12)");
1356
+ const accentSoftStrong = explicit.accentSoftStrong ?? withAlpha(palette.accent, mode === "light" ? 0.35 : 0.40, base.accentSoftStrong ?? "rgba(0, 0, 0, 0.22)");
1357
+ const hasOverride = Object.keys(explicit).length > 0;
1358
+ const markerBg = explicit.markerBg ?? withAlpha(palette.accent, mode === "light" ? 0.13 : 0.25, base.markerBg);
1359
+ const markerBorder = explicit.markerBorder ?? withAlpha(palette.accent, mode === "light" ? 0.45 : 0.65, base.markerBorder ?? "rgba(0, 0, 0, 0.3)");
1360
+ const okBorder = explicit.okBorder ?? withAlpha(palette.ok, mode === "light" ? 0.55 : 0.70, base.okBorder);
1361
+ const warnBorder = explicit.warnBorder ?? withAlpha(palette.warn, mode === "light" ? 0.55 : 0.70, base.warnBorder);
1362
+ const mdCodeBlock = explicit.mdCodeBlock ?? (hasOverride
1363
+ ? blendHexColors(palette.mdCode, palette.text, mode === "light" ? 0.18 : 0.28)
1364
+ : palette.mdCodeBlock);
1365
+ const mdCodeBlockBorder = explicit.mdCodeBlockBorder ?? (hasOverride
1366
+ ? blendHexColors(palette.muted, palette.panel, mode === "light" ? 0.12 : 0.18)
1367
+ : palette.mdCodeBlockBorder);
1368
+ const mdQuoteBorder = explicit.mdQuoteBorder ?? (hasOverride
1369
+ ? blendHexColors(palette.mdQuote, palette.panel, mode === "light" ? 0.05 : 0.10)
1370
+ : palette.mdQuoteBorder);
1371
+ const mdHr = explicit.mdHr ?? (hasOverride ? mdQuoteBorder : palette.mdHr);
1372
+ const syntaxType = explicit.syntaxType ?? (hasOverride
1373
+ ? blendHexColors(palette.syntaxFunction, palette.syntaxVariable, 0.45)
1374
+ : palette.syntaxType);
1375
+ const syntaxPunctuation = explicit.syntaxPunctuation ?? (hasOverride ? palette.syntaxOperator : palette.syntaxPunctuation);
1376
+ const accentContrast = explicit.accentContrast ?? defaultContrastColor(palette.accent, "#08101f", "#ffffff");
1377
+ const errorContrast = explicit.errorContrast ?? defaultContrastColor(palette.error, "#210908", "#ffffff");
1378
+ const panelShadow = explicit.panelShadow ?? (mode === "light"
1379
+ ? "0 1px 2px rgba(15, 23, 42, 0.03), 0 4px 14px rgba(15, 23, 42, 0.04)"
1380
+ : "0 1px 2px rgba(0, 0, 0, 0.36), 0 6px 18px rgba(0, 0, 0, 0.22)");
1381
+ const blockquoteBg = withAlpha(mdQuoteBorder, mode === "light" ? 0.10 : 0.16, mode === "light" ? "rgba(15, 23, 42, 0.04)" : "rgba(255, 255, 255, 0.05)");
1382
+ const tableAltBg = withAlpha(mdCodeBlockBorder, mode === "light" ? 0.10 : 0.14, mode === "light" ? "rgba(15, 23, 42, 0.03)" : "rgba(255, 255, 255, 0.04)");
1383
+ return {
1384
+ "--bg": palette.bg,
1385
+ "--panel": palette.panel,
1386
+ "--panel-2": panel2,
1387
+ "--card": palette.panel,
1388
+ "--editor-bg": editorBg,
1389
+ "--text": palette.text,
1390
+ "--muted": palette.muted,
1391
+ "--border": border,
1392
+ "--border-muted": borderMuted,
1393
+ "--accent": palette.accent,
1394
+ "--accent-contrast": accentContrast,
1395
+ "--accent-soft": accentSoft,
1396
+ "--accent-soft-strong": accentSoftStrong,
1397
+ "--ok": palette.ok,
1398
+ "--warn": palette.warn,
1399
+ "--error": palette.error,
1400
+ "--error-contrast": errorContrast,
1401
+ "--marker-bg": markerBg,
1402
+ "--marker-border": markerBorder,
1403
+ "--ok-border": okBorder,
1404
+ "--warn-border": warnBorder,
1405
+ "--panel-shadow": panelShadow,
1406
+ "--md-heading": palette.mdHeading,
1407
+ "--md-link": palette.mdLink,
1408
+ "--md-link-url": palette.mdLinkUrl,
1409
+ "--md-code": palette.mdCode,
1410
+ "--md-codeblock": mdCodeBlock,
1411
+ "--md-codeblock-border": mdCodeBlockBorder,
1412
+ "--md-quote": palette.mdQuote,
1413
+ "--md-quote-border": mdQuoteBorder,
1414
+ "--md-hr": mdHr,
1415
+ "--md-list-bullet": palette.mdListBullet,
1416
+ "--syntax-comment": palette.syntaxComment,
1417
+ "--syntax-keyword": palette.syntaxKeyword,
1418
+ "--syntax-function": palette.syntaxFunction,
1419
+ "--syntax-variable": palette.syntaxVariable,
1420
+ "--syntax-string": palette.syntaxString,
1421
+ "--syntax-number": palette.syntaxNumber,
1422
+ "--syntax-type": syntaxType,
1423
+ "--syntax-operator": palette.syntaxOperator,
1424
+ "--syntax-punctuation": syntaxPunctuation,
1425
+ "--blockquote-bg": blockquoteBg,
1426
+ "--table-alt-bg": tableAltBg,
1427
+ };
1428
+ }
1429
+ function cssDeclarations(vars) {
1430
+ return Object.entries(vars)
1431
+ .map(([key, value]) => ` ${key}: ${value};`)
1432
+ .join("\n");
1433
+ }
1434
+ export function buildPrototypeThemeDescriptor(raw, source = "default", modeOverride) {
1435
+ const normalizedRaw = typeof raw === "string" && raw.trim() ? raw.trim() : null;
1436
+ const inferredPreference = modeOverride ?? inferThemePreference(normalizedRaw);
1437
+ const lightVars = buildThemeVars("light", undefined);
1438
+ const darkVars = buildThemeVars("dark", undefined);
1439
+ return {
1440
+ raw: normalizedRaw,
1441
+ preference: inferredPreference,
1442
+ source,
1443
+ family: null,
1444
+ darkVars,
1445
+ lightVars,
1446
+ };
1447
+ }
1448
+ export function buildPrototypeThemeStylesheet(theme) {
1449
+ const lightBlock = cssDeclarations(theme.lightVars);
1450
+ const darkBlock = cssDeclarations(theme.darkVars);
1451
+ if (theme.preference === "light") {
1452
+ return `:root {\n color-scheme: light;\n${lightBlock}\n}`;
1453
+ }
1454
+ if (theme.preference === "dark") {
1455
+ return `:root {\n color-scheme: dark;\n${darkBlock}\n}`;
1456
+ }
1457
+ return `:root {\n color-scheme: dark;\n${darkBlock}\n}\n@media (prefers-color-scheme: light) {\n :root {\n color-scheme: light;\n${lightBlock}\n }\n}`;
1458
+ }
1459
+ export async function readPrototypeThemeDescriptor(directory) {
1460
+ const configured = await readConfiguredTheme();
1461
+ const opencodeTheme = await buildOpencodeThemeDescriptor(configured, directory);
1462
+ if (opencodeTheme) {
1463
+ return opencodeTheme;
1464
+ }
1465
+ const ghosttyTheme = await buildGhosttyThemeDescriptor(configured);
1466
+ if (ghosttyTheme) {
1467
+ return ghosttyTheme;
1468
+ }
1469
+ return buildPrototypeThemeDescriptor(configured.raw, configured.source, configured.mode);
1470
+ }
1471
+ //# sourceMappingURL=prototype-theme.js.map