@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.
- package/dist/app.js +17 -15
- package/dist/app.jsx.map +1 -0
- package/dist/components/badge.js +60 -24
- package/dist/components/{badge.js.map → badge.jsx.map} +1 -1
- package/dist/components/code-block.js +110 -17
- package/dist/components/code-block.jsx.map +1 -0
- package/dist/components/dialog.js +86 -10
- package/dist/components/dialog.jsx.map +1 -0
- package/dist/components/diff.js +79 -13
- package/dist/components/diff.jsx.map +1 -0
- package/dist/components/divider.js +44 -11
- package/dist/components/divider.jsx.map +1 -0
- package/dist/components/editor.js +174 -127
- package/dist/components/{editor.js.map → editor.jsx.map} +1 -1
- package/dist/components/image.js +292 -228
- package/dist/components/{image.js.map → image.jsx.map} +1 -1
- package/dist/components/loader.js +50 -6
- package/dist/components/loader.jsx.map +1 -0
- package/dist/components/markdown.js +35 -18
- package/dist/components/markdown.jsx.map +1 -0
- package/dist/components/panel.js +86 -42
- package/dist/components/{panel.js.map → panel.jsx.map} +1 -1
- package/dist/components/select-list.js +193 -103
- package/dist/components/select-list.jsx.map +1 -0
- package/dist/components/spacer.js +17 -12
- package/dist/components/spacer.jsx.map +1 -0
- package/dist/components/toast.js +115 -34
- package/dist/components/toast.jsx.map +1 -0
- package/dist/context/terminal.js +1 -2
- package/dist/context/terminal.jsx.map +1 -0
- package/dist/context/theme.js +779 -555
- package/dist/context/theme.jsx.map +1 -0
- package/package.json +4 -4
- package/dist/app.js.map +0 -1
- package/dist/components/code-block.js.map +0 -1
- package/dist/components/dialog.js.map +0 -1
- package/dist/components/diff.js.map +0 -1
- package/dist/components/divider.js.map +0 -1
- package/dist/components/loader.js.map +0 -1
- package/dist/components/markdown.js.map +0 -1
- package/dist/components/select-list.js.map +0 -1
- package/dist/components/spacer.js.map +0 -1
- package/dist/components/toast.js.map +0 -1
- package/dist/context/terminal.js.map +0 -1
- package/dist/context/theme.js.map +0 -1
package/dist/context/theme.js
CHANGED
|
@@ -1,4 +1,7 @@
|
|
|
1
|
-
import {
|
|
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
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
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
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
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
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
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
|
-
|
|
206
|
-
|
|
207
|
-
|
|
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
|
-
|
|
213
|
-
|
|
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
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
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
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
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
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
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
|
-
|
|
239
|
-
|
|
271
|
+
const effective = compositeOver(background, foreground);
|
|
272
|
+
return contrastRatio(effective, background);
|
|
240
273
|
}
|
|
241
274
|
function generateGrayScale(background, isDark) {
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
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
|
-
|
|
286
|
-
|
|
315
|
+
const value = grays[index];
|
|
316
|
+
return value === undefined ? fallback : value;
|
|
287
317
|
}
|
|
288
318
|
function generateMutedTextColor(background, isDark) {
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
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
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
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
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
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
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
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
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
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
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
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
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
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
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
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
|
|
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
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
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
|
-
|
|
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";
|