@yeshwanthyk/open-tui 0.1.0 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (132) hide show
  1. package/dist/app.d.ts +18 -0
  2. package/dist/app.d.ts.map +1 -0
  3. package/dist/app.js +28 -0
  4. package/dist/app.js.map +1 -0
  5. package/dist/autocomplete/autocomplete.d.ts +48 -0
  6. package/dist/autocomplete/autocomplete.d.ts.map +1 -0
  7. package/dist/autocomplete/autocomplete.js +391 -0
  8. package/dist/autocomplete/autocomplete.js.map +1 -0
  9. package/dist/autocomplete/file-index.d.ts +36 -0
  10. package/dist/autocomplete/file-index.d.ts.map +1 -0
  11. package/dist/autocomplete/file-index.js +143 -0
  12. package/dist/autocomplete/file-index.js.map +1 -0
  13. package/dist/autocomplete/index.d.ts +3 -0
  14. package/dist/autocomplete/index.d.ts.map +1 -0
  15. package/dist/autocomplete/index.js +3 -0
  16. package/dist/autocomplete/index.js.map +1 -0
  17. package/dist/components/badge.d.ts +11 -0
  18. package/dist/components/badge.d.ts.map +1 -0
  19. package/dist/components/badge.js +28 -0
  20. package/dist/components/badge.js.map +1 -0
  21. package/dist/components/code-block.d.ts +12 -0
  22. package/dist/components/code-block.d.ts.map +1 -0
  23. package/dist/components/code-block.js +21 -0
  24. package/dist/components/code-block.js.map +1 -0
  25. package/dist/components/dialog.d.ts +12 -0
  26. package/dist/components/dialog.d.ts.map +1 -0
  27. package/dist/components/dialog.js +15 -0
  28. package/dist/components/dialog.js.map +1 -0
  29. package/dist/components/diff.d.ts +11 -0
  30. package/dist/components/diff.d.ts.map +1 -0
  31. package/dist/components/diff.js +18 -0
  32. package/dist/components/diff.js.map +1 -0
  33. package/dist/components/divider.d.ts +9 -0
  34. package/dist/components/divider.d.ts.map +1 -0
  35. package/dist/components/divider.js +14 -0
  36. package/dist/components/divider.js.map +1 -0
  37. package/dist/components/editor.d.ts +87 -0
  38. package/dist/components/editor.d.ts.map +1 -0
  39. package/dist/components/editor.js +145 -0
  40. package/dist/components/editor.js.map +1 -0
  41. package/dist/components/image.d.ts +65 -0
  42. package/dist/components/image.d.ts.map +1 -0
  43. package/dist/components/image.js +315 -0
  44. package/dist/components/image.js.map +1 -0
  45. package/dist/components/loader.d.ts +25 -0
  46. package/dist/components/loader.d.ts.map +1 -0
  47. package/dist/components/loader.js +24 -0
  48. package/dist/components/loader.js.map +1 -0
  49. package/dist/components/markdown.d.ts +28 -0
  50. package/dist/components/markdown.d.ts.map +1 -0
  51. package/dist/components/markdown.js +28 -0
  52. package/dist/components/markdown.js.map +1 -0
  53. package/dist/components/panel.d.ts +12 -0
  54. package/dist/components/panel.d.ts.map +1 -0
  55. package/dist/components/panel.js +47 -0
  56. package/dist/components/panel.js.map +1 -0
  57. package/dist/components/select-list.d.ts +55 -0
  58. package/dist/components/select-list.d.ts.map +1 -0
  59. package/dist/components/select-list.js +116 -0
  60. package/dist/components/select-list.js.map +1 -0
  61. package/dist/components/spacer.d.ts +25 -0
  62. package/dist/components/spacer.d.ts.map +1 -0
  63. package/dist/components/spacer.js +25 -0
  64. package/dist/components/spacer.js.map +1 -0
  65. package/dist/components/toast.d.ts +19 -0
  66. package/dist/components/toast.d.ts.map +1 -0
  67. package/dist/components/toast.js +42 -0
  68. package/dist/components/toast.js.map +1 -0
  69. package/dist/context/terminal.d.ts +7 -0
  70. package/dist/context/terminal.d.ts.map +1 -0
  71. package/dist/context/terminal.js +6 -0
  72. package/dist/context/terminal.js.map +1 -0
  73. package/dist/context/theme.d.ts +117 -0
  74. package/dist/context/theme.d.ts.map +1 -0
  75. package/dist/context/theme.js +648 -0
  76. package/dist/context/theme.js.map +1 -0
  77. package/dist/hooks/use-keyboard.d.ts +7 -0
  78. package/dist/hooks/use-keyboard.d.ts.map +1 -0
  79. package/dist/hooks/use-keyboard.js +6 -0
  80. package/dist/hooks/use-keyboard.js.map +1 -0
  81. package/dist/index.d.ts +31 -0
  82. package/dist/index.d.ts.map +1 -0
  83. package/dist/index.js +45 -0
  84. package/dist/index.js.map +1 -0
  85. package/dist/opentui-augmentations.d.ts +9 -0
  86. package/dist/opentui-augmentations.d.ts.map +1 -0
  87. package/dist/opentui-augmentations.js +1 -0
  88. package/dist/opentui-augmentations.js.map +1 -0
  89. package/dist/parsers-config.d.ts +16 -0
  90. package/dist/parsers-config.d.ts.map +1 -0
  91. package/dist/parsers-config.js +119 -0
  92. package/dist/parsers-config.js.map +1 -0
  93. package/dist/themes/aura.json +69 -0
  94. package/dist/themes/ayu.json +80 -0
  95. package/dist/themes/catppuccin-macchiato.json +233 -0
  96. package/dist/themes/catppuccin.json +112 -0
  97. package/dist/themes/cobalt2.json +228 -0
  98. package/dist/themes/dracula.json +219 -0
  99. package/dist/themes/everforest.json +241 -0
  100. package/dist/themes/flexoki.json +237 -0
  101. package/dist/themes/github.json +233 -0
  102. package/dist/themes/gruvbox.json +95 -0
  103. package/dist/themes/kanagawa.json +77 -0
  104. package/dist/themes/lucent-orng.json +227 -0
  105. package/dist/themes/marvin.json +97 -0
  106. package/dist/themes/material.json +235 -0
  107. package/dist/themes/matrix.json +77 -0
  108. package/dist/themes/mercury.json +245 -0
  109. package/dist/themes/monokai.json +221 -0
  110. package/dist/themes/nightowl.json +221 -0
  111. package/dist/themes/nord.json +223 -0
  112. package/dist/themes/one-dark.json +84 -0
  113. package/dist/themes/opencode.json +245 -0
  114. package/dist/themes/orng.json +245 -0
  115. package/dist/themes/palenight.json +222 -0
  116. package/dist/themes/rosepine.json +234 -0
  117. package/dist/themes/solarized.json +223 -0
  118. package/dist/themes/synthwave84.json +226 -0
  119. package/dist/themes/tokyonight.json +243 -0
  120. package/dist/themes/vercel.json +245 -0
  121. package/dist/themes/vesper.json +218 -0
  122. package/dist/themes/zenburn.json +223 -0
  123. package/dist/utils/clipboard.d.ts +12 -0
  124. package/dist/utils/clipboard.d.ts.map +1 -0
  125. package/dist/utils/clipboard.js +52 -0
  126. package/dist/utils/clipboard.js.map +1 -0
  127. package/dist/utils/text-width.d.ts +26 -0
  128. package/dist/utils/text-width.d.ts.map +1 -0
  129. package/dist/utils/text-width.js +101 -0
  130. package/dist/utils/text-width.js.map +1 -0
  131. package/package.json +13 -4
  132. package/src/index.ts +0 -121
@@ -0,0 +1,648 @@
1
+ import { jsx as _jsx } from "@opentui/solid/jsx-runtime";
2
+ import { parseColor, RGBA, SyntaxStyle } from "@opentui/core";
3
+ import { createContext, createEffect, createMemo, useContext } from "solid-js";
4
+ import { createStore } from "solid-js/store";
5
+ // Theme JSON imports
6
+ import marvin from "../themes/marvin.json";
7
+ import aura from "../themes/aura.json";
8
+ import ayu from "../themes/ayu.json";
9
+ import catppuccin from "../themes/catppuccin.json";
10
+ import catppuccinMacchiato from "../themes/catppuccin-macchiato.json";
11
+ import cobalt2 from "../themes/cobalt2.json";
12
+ import dracula from "../themes/dracula.json";
13
+ import everforest from "../themes/everforest.json";
14
+ import flexoki from "../themes/flexoki.json";
15
+ import github from "../themes/github.json";
16
+ import gruvbox from "../themes/gruvbox.json";
17
+ import kanagawa from "../themes/kanagawa.json";
18
+ import lucentOrng from "../themes/lucent-orng.json";
19
+ import material from "../themes/material.json";
20
+ import matrix from "../themes/matrix.json";
21
+ import mercury from "../themes/mercury.json";
22
+ import monokai from "../themes/monokai.json";
23
+ import nightowl from "../themes/nightowl.json";
24
+ import nord from "../themes/nord.json";
25
+ import onedark from "../themes/one-dark.json";
26
+ import opencode from "../themes/opencode.json";
27
+ import orng from "../themes/orng.json";
28
+ import palenight from "../themes/palenight.json";
29
+ import rosepine from "../themes/rosepine.json";
30
+ import solarized from "../themes/solarized.json";
31
+ import synthwave84 from "../themes/synthwave84.json";
32
+ import tokyonight from "../themes/tokyonight.json";
33
+ import vercel from "../themes/vercel.json";
34
+ import vesper from "../themes/vesper.json";
35
+ import zenburn from "../themes/zenburn.json";
36
+ export const BUILTIN_THEMES = {
37
+ marvin: marvin,
38
+ aura: aura,
39
+ ayu: ayu,
40
+ catppuccin: catppuccin,
41
+ "catppuccin-macchiato": catppuccinMacchiato,
42
+ cobalt2: cobalt2,
43
+ dracula: dracula,
44
+ everforest: everforest,
45
+ flexoki: flexoki,
46
+ github: github,
47
+ gruvbox: gruvbox,
48
+ kanagawa: kanagawa,
49
+ "lucent-orng": lucentOrng,
50
+ material: material,
51
+ matrix: matrix,
52
+ mercury: mercury,
53
+ monokai: monokai,
54
+ nightowl: nightowl,
55
+ nord: nord,
56
+ "one-dark": onedark,
57
+ opencode: opencode,
58
+ orng: orng,
59
+ palenight: palenight,
60
+ rosepine: rosepine,
61
+ solarized: solarized,
62
+ synthwave84: synthwave84,
63
+ tokyonight: tokyonight,
64
+ vercel: vercel,
65
+ vesper: vesper,
66
+ zenburn: zenburn,
67
+ };
68
+ /**
69
+ * Default dark theme colors - soft contrast, minimal aesthetic
70
+ */
71
+ const defaultDarkTheme = {
72
+ // Muted, desaturated primaries
73
+ primary: parseColor("#d4a373"), // warm muted tan
74
+ secondary: parseColor("#7d9bba"), // soft steel blue
75
+ accent: parseColor("#87a987"), // sage green
76
+ error: parseColor("#c47a7a"), // soft coral
77
+ warning: parseColor("#d4b483"), // muted gold
78
+ success: parseColor("#87a987"), // sage green
79
+ info: parseColor("#7d9bba"), // soft steel blue
80
+ text: parseColor("#c8c8c8"), // soft white
81
+ textMuted: parseColor("#6b6b6b"), // medium gray
82
+ background: parseColor("#161616"), // near black
83
+ backgroundPanel: parseColor("#1a1a1a"), // slightly lifted
84
+ backgroundElement: parseColor("#222222"), // subtle contrast
85
+ backgroundMenu: parseColor("#131313"),
86
+ border: parseColor("#2a2a2a"), // very subtle
87
+ borderSubtle: parseColor("#222222"),
88
+ borderActive: parseColor("#7d9bba"),
89
+ selectionBg: parseColor("#333333"),
90
+ selectionFg: parseColor("#e0e0e0"),
91
+ // Softer diff colors
92
+ diffAdded: parseColor("#87a987"),
93
+ diffRemoved: parseColor("#c47a7a"),
94
+ diffContext: parseColor("#6b6b6b"),
95
+ diffAddedBg: parseColor("#1a2a1a"),
96
+ diffRemovedBg: parseColor("#2a1a1a"),
97
+ diffContextBg: parseColor("transparent"),
98
+ diffLineNumberFg: parseColor("#4a4a4a"),
99
+ diffLineNumberBg: parseColor("transparent"),
100
+ diffAddedLineNumberBg: parseColor("#1a2a1a"),
101
+ diffRemovedLineNumberBg: parseColor("#2a1a1a"),
102
+ diffAddedSign: parseColor("#6b9b6b"),
103
+ diffRemovedSign: parseColor("#b06060"),
104
+ diffHighlightAddedBg: parseColor("#253525"),
105
+ diffHighlightRemovedBg: parseColor("#352525"),
106
+ // Markdown - muted
107
+ markdownText: parseColor("#c8c8c8"),
108
+ markdownHeading: parseColor("#a0a0a0"),
109
+ markdownLink: parseColor("#9090a0"),
110
+ markdownLinkUrl: parseColor("#606060"),
111
+ markdownCode: parseColor("#b0a090"),
112
+ markdownCodeBlock: parseColor("#b0b0b0"),
113
+ markdownCodeBlockBorder: parseColor("#2a2a2a"),
114
+ markdownBlockQuote: parseColor("#707070"),
115
+ markdownBlockQuoteBorder: parseColor("#303030"),
116
+ markdownHr: parseColor("#303030"),
117
+ markdownListBullet: parseColor("#707070"),
118
+ markdownStrong: parseColor("#c8c8c8"),
119
+ markdownEmph: parseColor("#d4c48a"),
120
+ markdownListEnumeration: parseColor("#7d9bba"),
121
+ markdownImage: parseColor("#9090a0"),
122
+ markdownStrikethrough: parseColor("#6b6b6b"),
123
+ // Syntax - soft but readable contrast
124
+ syntaxComment: parseColor("#5a5a5a"),
125
+ syntaxString: parseColor("#98b998"), // sage green, slightly brighter
126
+ syntaxKeyword: parseColor("#b09cc0"), // soft lavender
127
+ syntaxFunction: parseColor("#8aafc8"), // steel blue
128
+ syntaxVariable: parseColor("#c0c0c0"),
129
+ syntaxType: parseColor("#d4c48a"), // warm gold
130
+ syntaxNumber: parseColor("#d4a87a"), // soft orange
131
+ syntaxConstant: parseColor("#d4a87a"),
132
+ syntaxOperator: parseColor("#a0a0a0"),
133
+ syntaxPunctuation: parseColor("#909090"),
134
+ syntaxProperty: parseColor("#8ac0b0"), // teal
135
+ syntaxTag: parseColor("#c09090"), // dusty rose
136
+ syntaxAttribute: parseColor("#d4c48a"),
137
+ };
138
+ /**
139
+ * Default light theme colors - optimized for high contrast on light backgrounds
140
+ */
141
+ const defaultLightTheme = {
142
+ primary: parseColor("#b06000"), // darker orange for better contrast
143
+ secondary: parseColor("#0550ae"), // darker blue
144
+ accent: parseColor("#1a7f37"), // darker green for tool labels
145
+ error: parseColor("#c21f3a"),
146
+ warning: parseColor("#9a6700"), // darker gold/amber
147
+ success: parseColor("#1a7f37"),
148
+ info: parseColor("#0550ae"),
149
+ text: parseColor("#1f2328"), // near-black for main text
150
+ textMuted: parseColor("#656d76"), // darker gray for better readability
151
+ background: parseColor("#ffffff"),
152
+ backgroundPanel: parseColor("#f6f8fa"),
153
+ backgroundElement: parseColor("#eaeef2"),
154
+ backgroundMenu: parseColor("#f6f8fa"),
155
+ border: parseColor("#d0d7de"),
156
+ borderSubtle: parseColor("#e6e9ef"),
157
+ borderActive: parseColor("#0550ae"),
158
+ selectionBg: parseColor("#ddf4ff"),
159
+ selectionFg: parseColor("#1f2328"),
160
+ diffAdded: parseColor("#1a7f37"),
161
+ diffRemoved: parseColor("#c21f3a"),
162
+ diffContext: parseColor("#656d76"),
163
+ diffAddedBg: parseColor("#dafbe1"),
164
+ diffRemovedBg: parseColor("#ffebe9"),
165
+ diffContextBg: parseColor("transparent"),
166
+ diffLineNumberFg: parseColor("#656d76"),
167
+ diffLineNumberBg: parseColor("transparent"),
168
+ diffAddedLineNumberBg: parseColor("#dafbe1"),
169
+ diffRemovedLineNumberBg: parseColor("#ffebe9"),
170
+ diffAddedSign: parseColor("#1a7f37"),
171
+ diffRemovedSign: parseColor("#c21f3a"),
172
+ diffHighlightAddedBg: parseColor("#aceebb"),
173
+ diffHighlightRemovedBg: parseColor("#ffcecb"),
174
+ markdownText: parseColor("#1f2328"),
175
+ markdownHeading: parseColor("#0550ae"),
176
+ markdownLink: parseColor("#8250df"), // purple for links
177
+ markdownLinkUrl: parseColor("#656d76"),
178
+ markdownCode: parseColor("#9a6700"), // darker gold
179
+ markdownCodeBlock: parseColor("#1f2328"),
180
+ markdownCodeBlockBorder: parseColor("#d0d7de"),
181
+ markdownBlockQuote: parseColor("#656d76"),
182
+ markdownBlockQuoteBorder: parseColor("#d0d7de"),
183
+ markdownHr: parseColor("#d0d7de"),
184
+ markdownListBullet: parseColor("#1a7f37"),
185
+ markdownStrong: parseColor("#1f2328"),
186
+ markdownEmph: parseColor("#9a6700"),
187
+ markdownListEnumeration: parseColor("#0550ae"),
188
+ markdownImage: parseColor("#8250df"),
189
+ markdownStrikethrough: parseColor("#656d76"),
190
+ syntaxComment: parseColor("#6e7781"), // darker gray
191
+ syntaxString: parseColor("#0a3069"), // dark blue for strings
192
+ syntaxKeyword: parseColor("#8250df"), // purple
193
+ syntaxFunction: parseColor("#8250df"), // purple for functions
194
+ syntaxVariable: parseColor("#1f2328"),
195
+ syntaxType: parseColor("#953800"), // dark orange
196
+ syntaxNumber: parseColor("#0550ae"), // blue for numbers
197
+ syntaxConstant: parseColor("#0550ae"),
198
+ syntaxOperator: parseColor("#1f2328"),
199
+ syntaxPunctuation: parseColor("#1f2328"),
200
+ syntaxProperty: parseColor("#0550ae"),
201
+ syntaxTag: parseColor("#1a7f37"), // green for tags
202
+ syntaxAttribute: parseColor("#953800"),
203
+ };
204
+ function clamp01(value) {
205
+ if (value < 0)
206
+ return 0;
207
+ if (value > 1)
208
+ return 1;
209
+ return value;
210
+ }
211
+ function srgbToLinear(channel) {
212
+ if (channel <= 0.04045)
213
+ return channel / 12.92;
214
+ return Math.pow((channel + 0.055) / 1.055, 2.4);
215
+ }
216
+ function relativeLuminance(color) {
217
+ const r = srgbToLinear(clamp01(color.r));
218
+ const g = srgbToLinear(clamp01(color.g));
219
+ const b = srgbToLinear(clamp01(color.b));
220
+ return 0.2126 * r + 0.7152 * g + 0.0722 * b;
221
+ }
222
+ function contrastRatio(a, b) {
223
+ const l1 = relativeLuminance(a);
224
+ const l2 = relativeLuminance(b);
225
+ const lighter = Math.max(l1, l2);
226
+ const darker = Math.min(l1, l2);
227
+ return (lighter + 0.05) / (darker + 0.05);
228
+ }
229
+ function compositeOver(background, overlay) {
230
+ if (overlay.a >= 0.99)
231
+ return overlay;
232
+ if (overlay.a <= 0.01)
233
+ return background;
234
+ const inv = 1 - overlay.a;
235
+ return RGBA.fromValues(overlay.r * overlay.a + background.r * inv, overlay.g * overlay.a + background.g * inv, overlay.b * overlay.a + background.b * inv, 1);
236
+ }
237
+ function contrastAgainst(background, foreground) {
238
+ const effective = compositeOver(background, foreground);
239
+ return contrastRatio(effective, background);
240
+ }
241
+ function generateGrayScale(background, isDark) {
242
+ const grays = {};
243
+ const bgR = background.r * 255;
244
+ const bgG = background.g * 255;
245
+ const bgB = background.b * 255;
246
+ const luminance = 0.299 * bgR + 0.587 * bgG + 0.114 * bgB;
247
+ for (let i = 1; i <= 12; i += 1) {
248
+ const factor = i / 12.0;
249
+ let newR = 0;
250
+ let newG = 0;
251
+ let newB = 0;
252
+ if (isDark) {
253
+ if (luminance < 10) {
254
+ const grayValue = Math.floor(factor * 0.4 * 255);
255
+ newR = grayValue;
256
+ newG = grayValue;
257
+ newB = grayValue;
258
+ }
259
+ else {
260
+ const newLum = luminance + (255 - luminance) * factor * 0.4;
261
+ const ratio = newLum / luminance;
262
+ newR = Math.min(bgR * ratio, 255);
263
+ newG = Math.min(bgG * ratio, 255);
264
+ newB = Math.min(bgB * ratio, 255);
265
+ }
266
+ }
267
+ else if (luminance > 245) {
268
+ const grayValue = Math.floor(255 - factor * 0.4 * 255);
269
+ newR = grayValue;
270
+ newG = grayValue;
271
+ newB = grayValue;
272
+ }
273
+ else {
274
+ const newLum = luminance * (1 - factor * 0.4);
275
+ const ratio = newLum / luminance;
276
+ newR = Math.max(bgR * ratio, 0);
277
+ newG = Math.max(bgG * ratio, 0);
278
+ newB = Math.max(bgB * ratio, 0);
279
+ }
280
+ grays[i] = RGBA.fromInts(Math.floor(newR), Math.floor(newG), Math.floor(newB));
281
+ }
282
+ return grays;
283
+ }
284
+ function getGray(grays, index, fallback) {
285
+ const value = grays[index];
286
+ return value === undefined ? fallback : value;
287
+ }
288
+ function generateMutedTextColor(background, isDark) {
289
+ const bgR = background.r * 255;
290
+ const bgG = background.g * 255;
291
+ const bgB = background.b * 255;
292
+ const luminance = 0.299 * bgR + 0.587 * bgG + 0.114 * bgB;
293
+ let grayValue = 0;
294
+ if (isDark) {
295
+ if (luminance < 10) {
296
+ grayValue = 180;
297
+ }
298
+ else {
299
+ grayValue = Math.min(Math.floor(160 + luminance * 0.3), 200);
300
+ }
301
+ }
302
+ else if (luminance > 245) {
303
+ grayValue = 75;
304
+ }
305
+ else {
306
+ grayValue = Math.max(Math.floor(100 - (255 - luminance) * 0.2), 60);
307
+ }
308
+ return RGBA.fromInts(grayValue, grayValue, grayValue);
309
+ }
310
+ function ensureLightModeContrast(theme) {
311
+ const effectiveBackground = theme.background.a >= 0.99 ? theme.background : defaultLightTheme.background;
312
+ const overrides = {};
313
+ const selectionBgMatchesElement = theme.selectionBg === theme.backgroundElement;
314
+ const muted = generateMutedTextColor(effectiveBackground, false);
315
+ if (theme.background.a >= 0.99) {
316
+ const grays = generateGrayScale(effectiveBackground, false);
317
+ const panelFallback = getGray(grays, 2, theme.backgroundPanel);
318
+ const panel = contrastAgainst(effectiveBackground, theme.backgroundPanel) >= 1.04 ? theme.backgroundPanel : panelFallback;
319
+ if (panel !== theme.backgroundPanel)
320
+ overrides.backgroundPanel = panel;
321
+ const elementFallback = getGray(grays, 3, theme.backgroundElement);
322
+ const element = contrastAgainst(effectiveBackground, theme.backgroundElement) >= 1.08 ? theme.backgroundElement : elementFallback;
323
+ if (element !== theme.backgroundElement)
324
+ overrides.backgroundElement = element;
325
+ const menuFallback = getGray(grays, 3, theme.backgroundMenu);
326
+ const menu = contrastAgainst(effectiveBackground, theme.backgroundMenu) >= 1.08 ? theme.backgroundMenu : menuFallback;
327
+ if (menu !== theme.backgroundMenu)
328
+ overrides.backgroundMenu = menu;
329
+ const borderSubtleFallback = getGray(grays, 6, theme.borderSubtle);
330
+ const borderSubtle = contrastAgainst(effectiveBackground, theme.borderSubtle) >= 1.12 ? theme.borderSubtle : borderSubtleFallback;
331
+ if (borderSubtle !== theme.borderSubtle)
332
+ overrides.borderSubtle = borderSubtle;
333
+ const borderFallback = getGray(grays, 7, theme.border);
334
+ const border = contrastAgainst(effectiveBackground, theme.border) >= 1.2 ? theme.border : borderFallback;
335
+ if (border !== theme.border)
336
+ overrides.border = border;
337
+ }
338
+ const textMuted = contrastAgainst(effectiveBackground, theme.textMuted) >= 3 ? theme.textMuted : muted;
339
+ if (textMuted !== theme.textMuted)
340
+ overrides.textMuted = textMuted;
341
+ const backgroundElementOverride = overrides.backgroundElement;
342
+ if (selectionBgMatchesElement && backgroundElementOverride !== undefined) {
343
+ overrides.selectionBg = backgroundElementOverride;
344
+ }
345
+ const selectionBg = overrides.selectionBg ?? theme.selectionBg;
346
+ const selectionTarget = compositeOver(effectiveBackground, selectionBg);
347
+ const currentSelection = contrastAgainst(selectionTarget, theme.selectionFg);
348
+ if (currentSelection < 3) {
349
+ const textRatio = contrastAgainst(selectionTarget, theme.text);
350
+ const backgroundRatio = contrastAgainst(selectionTarget, theme.background);
351
+ let best = theme.text;
352
+ let bestRatio = textRatio;
353
+ if (backgroundRatio > bestRatio) {
354
+ best = theme.background;
355
+ bestRatio = backgroundRatio;
356
+ }
357
+ if (bestRatio < 3) {
358
+ const black = RGBA.fromInts(0, 0, 0);
359
+ const white = RGBA.fromInts(255, 255, 255);
360
+ best = contrastAgainst(selectionTarget, black) >= contrastAgainst(selectionTarget, white) ? black : white;
361
+ }
362
+ overrides.selectionFg = best;
363
+ }
364
+ if (Object.keys(overrides).length === 0)
365
+ return theme;
366
+ return { ...theme, ...overrides };
367
+ }
368
+ /**
369
+ * Resolve a ThemeJson to concrete RGBA colors for a given mode
370
+ */
371
+ function resolveThemeJson(themeJson, mode) {
372
+ const defs = themeJson.defs ?? {};
373
+ function resolveColor(c) {
374
+ if (c instanceof RGBA)
375
+ return c;
376
+ if (typeof c === "string") {
377
+ if (c === "transparent" || c === "none")
378
+ return parseColor("transparent");
379
+ if (c.startsWith("#"))
380
+ return parseColor(c);
381
+ // Reference to defs
382
+ if (defs[c] != null)
383
+ return resolveColor(defs[c]);
384
+ // Reference to another theme key
385
+ if (themeJson.theme[c] !== undefined)
386
+ return resolveColor(themeJson.theme[c]);
387
+ // Unknown reference - return magenta as debug indicator
388
+ console.warn(`Unknown color reference: ${c}`);
389
+ return parseColor("#ff00ff");
390
+ }
391
+ // Variant object { dark: ..., light: ... }
392
+ if (typeof c === "object" && c !== null && "dark" in c && "light" in c) {
393
+ return resolveColor(c[mode]);
394
+ }
395
+ // Unknown - return magenta
396
+ return parseColor("#ff00ff");
397
+ }
398
+ const resolved = {};
399
+ for (const [key, value] of Object.entries(themeJson.theme)) {
400
+ if (key === "$schema")
401
+ continue;
402
+ resolved[key] = resolveColor(value);
403
+ }
404
+ return resolved;
405
+ }
406
+ /**
407
+ * Map resolved opencode theme colors to marvin ThemeColors with fallbacks
408
+ */
409
+ function mapToThemeColors(resolved, mode) {
410
+ const base = mode === "dark" ? defaultDarkTheme : defaultLightTheme;
411
+ // Helper to get color with fallback
412
+ const get = (key, ...fallbacks) => {
413
+ const direct = resolved[key];
414
+ if (direct !== undefined)
415
+ return direct;
416
+ for (const fb of fallbacks) {
417
+ const fallback = resolved[fb];
418
+ if (fallback !== undefined)
419
+ return fallback;
420
+ }
421
+ return base[key] ?? base.text;
422
+ };
423
+ return {
424
+ primary: get("primary"),
425
+ secondary: get("secondary"),
426
+ accent: get("accent"),
427
+ error: get("error"),
428
+ warning: get("warning"),
429
+ success: get("success"),
430
+ info: get("info"),
431
+ text: get("text"),
432
+ textMuted: get("textMuted"),
433
+ background: get("background"),
434
+ backgroundPanel: get("backgroundPanel"),
435
+ backgroundElement: get("backgroundElement"),
436
+ backgroundMenu: get("backgroundMenu", "backgroundElement"),
437
+ border: get("border"),
438
+ borderSubtle: get("borderSubtle"),
439
+ borderActive: get("borderActive"),
440
+ selectionBg: get("selectionBg", "backgroundElement"),
441
+ selectionFg: get("selectionFg", "text"),
442
+ // Diff colors - map from opencode names
443
+ diffAdded: get("diffAdded"),
444
+ diffRemoved: get("diffRemoved"),
445
+ diffContext: get("diffContext"),
446
+ diffAddedBg: get("diffAddedBg"),
447
+ diffRemovedBg: get("diffRemovedBg"),
448
+ diffContextBg: get("diffContextBg"),
449
+ diffLineNumberFg: get("diffLineNumber", "textMuted"),
450
+ diffLineNumberBg: get("diffLineNumberBg", "background"),
451
+ diffAddedLineNumberBg: get("diffAddedLineNumberBg", "diffAddedBg"),
452
+ diffRemovedLineNumberBg: get("diffRemovedLineNumberBg", "diffRemovedBg"),
453
+ diffAddedSign: get("diffAddedSign", "diffAdded"),
454
+ diffRemovedSign: get("diffRemovedSign", "diffRemoved"),
455
+ diffHighlightAddedBg: get("diffHighlightAddedBg", "diffAddedBg"),
456
+ diffHighlightRemovedBg: get("diffHighlightRemovedBg", "diffRemovedBg"),
457
+ // Markdown colors
458
+ markdownText: get("markdownText", "text"),
459
+ markdownHeading: get("markdownHeading", "primary"),
460
+ markdownLink: get("markdownLink", "accent"),
461
+ markdownLinkUrl: get("markdownLinkUrl", "markdownLinkText", "textMuted"),
462
+ markdownCode: get("markdownCode", "success"),
463
+ markdownCodeBlock: get("markdownCodeBlock", "text"),
464
+ markdownCodeBlockBorder: get("markdownCodeBlockBorder", "border"),
465
+ markdownBlockQuote: get("markdownBlockQuote", "textMuted"),
466
+ markdownBlockQuoteBorder: get("markdownBlockQuoteBorder", "border"),
467
+ markdownHr: get("markdownHorizontalRule", "border"),
468
+ markdownListBullet: get("markdownListBullet", "markdownListItem", "accent"),
469
+ markdownStrong: get("markdownStrong", "text"),
470
+ markdownEmph: get("markdownEmph", "warning"),
471
+ markdownListEnumeration: get("markdownListEnumeration", "markdownListBullet"),
472
+ markdownImage: get("markdownImage", "markdownLink"),
473
+ markdownStrikethrough: get("markdownStrikethrough", "textMuted"),
474
+ // Syntax colors
475
+ syntaxComment: get("syntaxComment"),
476
+ syntaxString: get("syntaxString"),
477
+ syntaxKeyword: get("syntaxKeyword"),
478
+ syntaxFunction: get("syntaxFunction"),
479
+ syntaxVariable: get("syntaxVariable"),
480
+ syntaxType: get("syntaxType"),
481
+ syntaxNumber: get("syntaxNumber"),
482
+ syntaxConstant: get("syntaxConstant", "syntaxNumber"),
483
+ syntaxOperator: get("syntaxOperator"),
484
+ syntaxPunctuation: get("syntaxPunctuation"),
485
+ syntaxProperty: get("syntaxProperty", "syntaxVariable"),
486
+ syntaxTag: get("syntaxTag", "syntaxKeyword"),
487
+ syntaxAttribute: get("syntaxAttribute", "syntaxProperty"),
488
+ };
489
+ }
490
+ export function createSyntaxStyle(theme, variant = "normal") {
491
+ const rules = getSyntaxRules(theme);
492
+ if (variant === "subtle") {
493
+ return SyntaxStyle.fromTheme(rules.map((rule) => ({
494
+ ...rule,
495
+ style: { ...rule.style, dim: true },
496
+ })));
497
+ }
498
+ return SyntaxStyle.fromTheme(rules);
499
+ }
500
+ function getSyntaxRules(theme) {
501
+ return [
502
+ // Default text
503
+ { scope: ["default"], style: { foreground: theme.text } },
504
+ // Comments
505
+ { scope: ["comment", "comment.documentation"], style: { foreground: theme.syntaxComment, italic: true } },
506
+ // Strings
507
+ { scope: ["string", "symbol"], style: { foreground: theme.syntaxString } },
508
+ { scope: ["string.escape", "string.regexp"], style: { foreground: theme.syntaxKeyword } },
509
+ { scope: ["character", "character.special"], style: { foreground: theme.syntaxString } },
510
+ // Numbers and constants
511
+ { scope: ["number", "boolean", "float"], style: { foreground: theme.syntaxNumber } },
512
+ { scope: ["constant", "constant.builtin"], style: { foreground: theme.syntaxConstant } },
513
+ // Keywords
514
+ { scope: ["keyword"], style: { foreground: theme.syntaxKeyword, italic: true } },
515
+ {
516
+ scope: ["keyword.function", "keyword.return", "keyword.conditional", "keyword.repeat"],
517
+ style: { foreground: theme.syntaxKeyword, italic: true },
518
+ },
519
+ { scope: ["keyword.operator", "operator"], style: { foreground: theme.syntaxOperator } },
520
+ { scope: ["keyword.import", "keyword.export"], style: { foreground: theme.syntaxKeyword } },
521
+ { scope: ["keyword.type"], style: { foreground: theme.syntaxType, bold: true, italic: true } },
522
+ // Functions
523
+ {
524
+ scope: ["function", "function.call", "function.method", "function.method.call", "function.builtin"],
525
+ style: { foreground: theme.syntaxFunction },
526
+ },
527
+ { scope: ["constructor"], style: { foreground: theme.syntaxFunction } },
528
+ // Variables and parameters
529
+ { scope: ["variable", "variable.parameter", "parameter"], style: { foreground: theme.syntaxVariable } },
530
+ { scope: ["variable.member", "property", "field"], style: { foreground: theme.syntaxProperty } },
531
+ { scope: ["variable.builtin", "variable.super"], style: { foreground: theme.error } },
532
+ // Types
533
+ { scope: ["type", "type.builtin", "type.definition"], style: { foreground: theme.syntaxType } },
534
+ { scope: ["class", "module", "namespace"], style: { foreground: theme.syntaxType } },
535
+ // Punctuation
536
+ { scope: ["punctuation", "punctuation.bracket", "punctuation.delimiter"], style: { foreground: theme.syntaxPunctuation } },
537
+ { scope: ["punctuation.special"], style: { foreground: theme.syntaxOperator } },
538
+ // Tags (HTML/XML)
539
+ { scope: ["tag"], style: { foreground: theme.syntaxTag } },
540
+ { scope: ["tag.attribute"], style: { foreground: theme.syntaxAttribute } },
541
+ { scope: ["tag.delimiter"], style: { foreground: theme.syntaxOperator } },
542
+ // Attributes and annotations
543
+ { scope: ["attribute", "annotation"], style: { foreground: theme.warning } },
544
+ // Markdown specific
545
+ {
546
+ scope: ["markup.heading", "markup.heading.1", "markup.heading.2", "markup.heading.3", "markup.heading.4", "markup.heading.5", "markup.heading.6"],
547
+ style: { foreground: theme.markdownHeading, bold: true },
548
+ },
549
+ { scope: ["markup.bold", "markup.strong"], style: { foreground: theme.markdownStrong, bold: true } },
550
+ { scope: ["markup.italic"], style: { foreground: theme.markdownEmph, italic: true } },
551
+ { scope: ["markup.strikethrough"], style: { foreground: theme.markdownStrikethrough } },
552
+ { scope: ["markup.link", "markup.link.url"], style: { foreground: theme.markdownLink, underline: true } },
553
+ { scope: ["markup.link.label", "label"], style: { foreground: theme.markdownLinkUrl } },
554
+ { scope: ["markup.raw", "markup.raw.inline", "markup.raw.block"], style: { foreground: theme.markdownCode } },
555
+ { scope: ["markup.list"], style: { foreground: theme.markdownListBullet } },
556
+ { scope: ["markup.list.checked"], style: { foreground: theme.success } },
557
+ { scope: ["markup.list.unchecked"], style: { foreground: theme.textMuted } },
558
+ { scope: ["markup.quote"], style: { foreground: theme.markdownBlockQuote, italic: true } },
559
+ // Diff
560
+ { scope: ["diff.plus"], style: { foreground: theme.diffAdded, background: theme.diffAddedBg } },
561
+ { scope: ["diff.minus"], style: { foreground: theme.diffRemoved, background: theme.diffRemovedBg } },
562
+ { scope: ["diff.delta"], style: { foreground: theme.diffContext, background: theme.diffContextBg } },
563
+ // Conceal (for hidden markdown syntax)
564
+ { scope: ["conceal"], style: { foreground: theme.textMuted } },
565
+ // Misc
566
+ { scope: ["spell", "nospell"], style: { foreground: theme.text } },
567
+ { scope: ["error"], style: { foreground: theme.error, bold: true } },
568
+ { scope: ["warning"], style: { foreground: theme.warning, bold: true } },
569
+ { scope: ["info"], style: { foreground: theme.info } },
570
+ ];
571
+ }
572
+ const ThemeContext = createContext();
573
+ export function ThemeProvider(props) {
574
+ const [store, setStore] = createStore({
575
+ mode: props.mode ?? "dark",
576
+ themeName: props.themeName ?? "marvin",
577
+ });
578
+ // Sync themeName prop changes to store (for external control)
579
+ createEffect(() => {
580
+ if (props.themeName !== undefined && props.themeName !== store.themeName) {
581
+ setStore("themeName", props.themeName);
582
+ }
583
+ });
584
+ // Sync mode prop changes to store (for external light/dark toggle)
585
+ createEffect(() => {
586
+ if (props.mode !== undefined && props.mode !== store.mode) {
587
+ setStore("mode", props.mode);
588
+ }
589
+ });
590
+ const resolvedTheme = createMemo(() => {
591
+ const name = store.themeName;
592
+ const mode = store.mode;
593
+ // Fallback to defaults for unknown themes
594
+ if (!BUILTIN_THEMES[name]) {
595
+ const base = mode === "dark" ? defaultDarkTheme : defaultLightTheme;
596
+ const merged = { ...base, ...props.customTheme };
597
+ return mode === "light" ? ensureLightModeContrast(merged) : merged;
598
+ }
599
+ // Resolve named theme (including marvin)
600
+ const themeJson = BUILTIN_THEMES[name];
601
+ const resolved = resolveThemeJson(themeJson, mode);
602
+ const mapped = mapToThemeColors(resolved, mode);
603
+ const merged = { ...mapped, ...props.customTheme };
604
+ return mode === "light" ? ensureLightModeContrast(merged) : merged;
605
+ });
606
+ // Use createMemo for syntax styles - they'll recompute when theme changes
607
+ const syntaxStyle = createMemo(() => createSyntaxStyle(resolvedTheme(), "normal"));
608
+ const subtleSyntaxStyle = createMemo(() => createSyntaxStyle(resolvedTheme(), "subtle"));
609
+ // Note: SyntaxStyle cleanup is handled internally by opentui when memos recompute
610
+ const value = {
611
+ get theme() {
612
+ return resolvedTheme();
613
+ },
614
+ mode: () => store.mode,
615
+ setMode: (mode) => {
616
+ setStore("mode", mode);
617
+ },
618
+ get syntaxStyle() {
619
+ return syntaxStyle();
620
+ },
621
+ get subtleSyntaxStyle() {
622
+ return subtleSyntaxStyle();
623
+ },
624
+ themeName: () => store.themeName,
625
+ setTheme: (name) => {
626
+ setStore("themeName", name);
627
+ props.onThemeChange?.(name);
628
+ },
629
+ availableThemes: () => Object.keys(BUILTIN_THEMES),
630
+ };
631
+ return _jsx(ThemeContext.Provider, { value: value, children: props.children });
632
+ }
633
+ export function useTheme() {
634
+ const context = useContext(ThemeContext);
635
+ if (!context) {
636
+ throw new Error("useTheme must be used within a ThemeProvider");
637
+ }
638
+ return context;
639
+ }
640
+ /**
641
+ * Parse a color input to RGBA
642
+ */
643
+ export function toRGBA(color) {
644
+ return parseColor(color);
645
+ }
646
+ // Re-export RGBA for convenience
647
+ export { RGBA } from "@opentui/core";
648
+ //# sourceMappingURL=theme.js.map