@yeshwanthyk/open-tui 0.1.4 → 0.1.6

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