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