@xp266/dshtui 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/dshtui.js +65 -0
- package/cordis.patch.yml +81 -0
- package/lib/contract/index.d.mts +2 -0
- package/lib/contract/index.mjs +5 -0
- package/lib/dialog.d.mts +156 -0
- package/lib/dialog.mjs +3 -0
- package/lib/index-DqnaSXqP.d.mts +823 -0
- package/lib/index.d.mts +17 -0
- package/lib/index.mjs +11404 -0
- package/lib/prod-react-Dzof4qJx.mjs +4 -0
- package/lib/surface-DdxzIgIY.mjs +3126 -0
- package/lib/vendor.d.mts +3 -0
- package/lib/vendor.mjs +4 -0
- package/package.json +128 -0
- package/src/apply-theme.ts +8 -0
- package/src/boot-log.ts +35 -0
- package/src/chat/blocks.ts +41 -0
- package/src/chat/bridge.ts +1076 -0
- package/src/chat/builtin-tool-views.ts +53 -0
- package/src/chat/chat-face.ts +16 -0
- package/src/chat/chat-nodes.ts +53 -0
- package/src/chat/efforts.ts +4 -0
- package/src/chat/interactions.ts +329 -0
- package/src/chat/models.ts +263 -0
- package/src/chat/partial-json.ts +125 -0
- package/src/chat/presets.ts +26 -0
- package/src/chat/question-view.ts +106 -0
- package/src/chat/retry-status.ts +33 -0
- package/src/chat/session-groups.ts +34 -0
- package/src/chat/session-list.ts +311 -0
- package/src/chat/store.ts +636 -0
- package/src/chat/todo-view.ts +66 -0
- package/src/chat/tool-view.ts +311 -0
- package/src/chat/tool-views.ts +114 -0
- package/src/contract/index.ts +704 -0
- package/src/contract/upstream.ts +165 -0
- package/src/core/caret-nonce.ts +19 -0
- package/src/core/composer-layout.ts +65 -0
- package/src/core/edit.ts +152 -0
- package/src/core/field-view.ts +52 -0
- package/src/core/fields.ts +185 -0
- package/src/core/metrics.ts +26 -0
- package/src/core/paste.ts +149 -0
- package/src/core/segments.ts +112 -0
- package/src/core/text.ts +290 -0
- package/src/dialog.ts +11 -0
- package/src/env.ts +38 -0
- package/src/harness-home.ts +22 -0
- package/src/hot-theme.ts +56 -0
- package/src/index.tsx +185 -0
- package/src/kernel/registry.ts +102 -0
- package/src/kernel/surface.ts +33 -0
- package/src/log.ts +249 -0
- package/src/model/message.ts +89 -0
- package/src/model/selection.ts +63 -0
- package/src/performance-guard.ts +28 -0
- package/src/runtime/prod-react.ts +10 -0
- package/src/terminal/background.ts +66 -0
- package/src/terminal/capabilities.ts +54 -0
- package/src/terminal/clipboard-backends.ts +69 -0
- package/src/terminal/clipboard.ts +161 -0
- package/src/terminal/cursor-shape.ts +13 -0
- package/src/terminal/glyphs.ts +44 -0
- package/src/terminal/mouse.ts +154 -0
- package/src/terminal/probe.ts +61 -0
- package/src/terminal/screen.ts +349 -0
- package/src/theme-settings.ts +31 -0
- package/src/theme.ts +319 -0
- package/src/ui/app.tsx +454 -0
- package/src/ui/chrome/caps.tsx +9 -0
- package/src/ui/chrome/hint-service.ts +87 -0
- package/src/ui/chrome/input-status.ts +27 -0
- package/src/ui/chrome/key-gate.tsx +29 -0
- package/src/ui/chrome/status-bar.tsx +147 -0
- package/src/ui/contributions.ts +47 -0
- package/src/ui/dialog/defaults-dialog.tsx +152 -0
- package/src/ui/dialog/dialog-item.tsx +38 -0
- package/src/ui/dialog/dialog.tsx +386 -0
- package/src/ui/dialog/effort-dialog.tsx +45 -0
- package/src/ui/dialog/geometry.ts +131 -0
- package/src/ui/dialog/items.ts +124 -0
- package/src/ui/dialog/list-dialog.tsx +63 -0
- package/src/ui/dialog/models-dialog.tsx +203 -0
- package/src/ui/dialog/presets-dialog.tsx +46 -0
- package/src/ui/dialog/providers-dialog.tsx +313 -0
- package/src/ui/dialog/sessions-dialog.tsx +163 -0
- package/src/ui/dialog/sizes.ts +7 -0
- package/src/ui/dialog/status-lines.ts +11 -0
- package/src/ui/dialog/todo-dialog.tsx +29 -0
- package/src/ui/dialog/use-dialog-input.ts +186 -0
- package/src/ui/extension-point.ts +188 -0
- package/src/ui/home-logo.ts +132 -0
- package/src/ui/hooks/use-async-action.ts +25 -0
- package/src/ui/hooks/use-async-list.ts +71 -0
- package/src/ui/hooks/use-caret.ts +24 -0
- package/src/ui/hooks/use-chat-events.ts +153 -0
- package/src/ui/hooks/use-mouse-selection.ts +352 -0
- package/src/ui/hooks/use-scroll.ts +49 -0
- package/src/ui/hooks/use-terminal-size.ts +20 -0
- package/src/ui/input/commands.ts +118 -0
- package/src/ui/input/composer-bus.ts +14 -0
- package/src/ui/input/composer-fields.ts +111 -0
- package/src/ui/input/composer-keys.ts +21 -0
- package/src/ui/input/composer-paste.ts +20 -0
- package/src/ui/input/input-bar.tsx +279 -0
- package/src/ui/input/use-composer.ts +495 -0
- package/src/ui/key-arbiter.ts +37 -0
- package/src/ui/keymap.ts +15 -0
- package/src/ui/layout-service.ts +77 -0
- package/src/ui/message/layout.ts +763 -0
- package/src/ui/message/md/block.ts +247 -0
- package/src/ui/message/md/engine.ts +430 -0
- package/src/ui/message/md/extensions.ts +58 -0
- package/src/ui/message/md/highlight.ts +626 -0
- package/src/ui/message/md/index.ts +14 -0
- package/src/ui/message/md/inline.ts +97 -0
- package/src/ui/message/md/palette.ts +87 -0
- package/src/ui/message/message-list.tsx +69 -0
- package/src/ui/message/message-row.tsx +137 -0
- package/src/ui/message/message-views.ts +16 -0
- package/src/ui/message/renderers.ts +39 -0
- package/src/ui/message/tool-diff.ts +188 -0
- package/src/ui/message/warmup.ts +41 -0
- package/src/ui/message/wave.ts +98 -0
- package/src/ui/overlay.ts +22 -0
- package/src/ui/panels/approval-panel.tsx +193 -0
- package/src/ui/panels/exports.ts +3 -0
- package/src/ui/panels/question-model.ts +424 -0
- package/src/ui/panels/question-panel.tsx +154 -0
- package/src/ui/panels/surface.tsx +106 -0
- package/src/ui/panels-builtin.tsx +86 -0
- package/src/ui/pointer/builtins.ts +281 -0
- package/src/ui/pointer/registry.ts +25 -0
- package/src/ui/region.tsx +31 -0
- package/src/ui/selection/service.ts +92 -0
- package/src/ui/selection-registry.ts +88 -0
- package/src/ui/selection.tsx +148 -0
- package/src/ui/spinner-tick.tsx +24 -0
- package/src/ui/use-available-windows.ts +31 -0
- package/src/ui/widgets/actions.tsx +84 -0
- package/src/ui/widgets/button.tsx +57 -0
- package/src/ui/widgets/checkbox.tsx +58 -0
- package/src/ui/widgets/header.tsx +36 -0
- package/src/ui/widgets/index.ts +8 -0
- package/src/ui/widgets/input.tsx +76 -0
- package/src/ui/widgets/registry.ts +32 -0
- package/src/ui/widgets/search.tsx +59 -0
- package/src/ui/widgets/select.tsx +97 -0
- package/src/ui/widgets/static.tsx +37 -0
- package/src/ui/widgets/types.ts +1 -0
- package/src/ui/window-services-bridge.ts +71 -0
- package/src/ui/window-services.ts +20 -0
- package/src/ui/windows/models-window.tsx +15 -0
- package/src/ui/windows/providers-window.tsx +14 -0
- package/src/ui/windows/service-window.tsx +18 -0
- package/src/ui/windows/sessions-window.tsx +25 -0
- package/src/ui/windows-builtin.tsx +94 -0
- package/src/ui/windows.ts +18 -0
- package/src/vendor.ts +12 -0
package/src/theme.ts
ADDED
|
@@ -0,0 +1,319 @@
|
|
|
1
|
+
import type { PERMISSION_PRESETS } from './chat/bridge.ts'
|
|
2
|
+
import { keyedRegistry } from './kernel/registry.ts'
|
|
3
|
+
import type { ThemePaletteContribution } from './contract/index.ts'
|
|
4
|
+
|
|
5
|
+
type PermissionModeId = typeof PERMISSION_PRESETS[number]
|
|
6
|
+
|
|
7
|
+
export type ThemeMode = 'dark' | 'light'
|
|
8
|
+
|
|
9
|
+
const paletteContributions = keyedRegistry<ThemePaletteContribution>()
|
|
10
|
+
|
|
11
|
+
export function registerPalette(contribution: ThemePaletteContribution): () => void {
|
|
12
|
+
const dispose = paletteContributions.register(contribution.id, contribution, { order: contribution.order })
|
|
13
|
+
materialize(currentMode)
|
|
14
|
+
return () => {
|
|
15
|
+
dispose()
|
|
16
|
+
materialize(currentMode)
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function subscribePalettes(listener: () => void): () => void {
|
|
21
|
+
return paletteContributions.subscribe(listener)
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* The palette is built from a fixed gray ladder plus one value per semantic
|
|
26
|
+
* hue, so near-duplicate grays cannot drift apart again: every surface picks
|
|
27
|
+
* its shade from the ladder, and every meaning (accent, info, success, error,
|
|
28
|
+
* added, removed) owns exactly one color.
|
|
29
|
+
*/
|
|
30
|
+
interface GrayLadder {
|
|
31
|
+
/** Page background (dialog backdrop). */
|
|
32
|
+
base: string
|
|
33
|
+
/** Sunken surface: tool cards, read bodies, terminal fills. */
|
|
34
|
+
sunken: string
|
|
35
|
+
/** Panel surface: dialogs, permission strip, user bubble. */
|
|
36
|
+
surface: string
|
|
37
|
+
/** Raised surface: inputs, hovered/selected rows. */
|
|
38
|
+
raised: string
|
|
39
|
+
/** Decorative gray: hr, quote bar, task markers, scroll thumb. */
|
|
40
|
+
line: string
|
|
41
|
+
/** Secondary text: hints, separators, tool body, thinking body. */
|
|
42
|
+
text: string
|
|
43
|
+
/** Primary text. */
|
|
44
|
+
ink: string
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const DARK_LADDER: GrayLadder = {
|
|
48
|
+
base: '#0d0d0d',
|
|
49
|
+
sunken: '#1a1a1a',
|
|
50
|
+
surface: '#262626',
|
|
51
|
+
raised: '#333333',
|
|
52
|
+
line: '#686868',
|
|
53
|
+
text: '#9a9a9a',
|
|
54
|
+
ink: '#cccccc',
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const LIGHT_LADDER: GrayLadder = {
|
|
58
|
+
base: '#ffffff',
|
|
59
|
+
sunken: '#f2f2f2',
|
|
60
|
+
surface: '#e6e6e6',
|
|
61
|
+
raised: '#d9d9d9',
|
|
62
|
+
line: '#999999',
|
|
63
|
+
text: '#595959',
|
|
64
|
+
ink: '#1a1a1a',
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
interface SemanticHues {
|
|
68
|
+
/** The one accent: section headers, list markers, effort, selection focus. */
|
|
69
|
+
accent: string
|
|
70
|
+
/** Informational blue: permission mode, links, focus labels. */
|
|
71
|
+
info: string
|
|
72
|
+
success: string
|
|
73
|
+
error: string
|
|
74
|
+
/** Diff/code-fence addition green (distinct from success so status and diff never share). */
|
|
75
|
+
added: string
|
|
76
|
+
/** Diff/code-fence removal red (distinct from error so status and diff never share). */
|
|
77
|
+
removed: string
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const DARK_HUES: SemanticHues = {
|
|
81
|
+
accent: '#ffae00',
|
|
82
|
+
info: '#4da0e8',
|
|
83
|
+
success: '#4caf50',
|
|
84
|
+
error: '#ff6753',
|
|
85
|
+
added: '#5d9e50',
|
|
86
|
+
removed: '#d25044',
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const LIGHT_HUES: SemanticHues = {
|
|
90
|
+
accent: '#a86800',
|
|
91
|
+
info: '#2f6fd0',
|
|
92
|
+
success: '#2e8b3d',
|
|
93
|
+
error: '#d44a3a',
|
|
94
|
+
added: '#0a8a2c',
|
|
95
|
+
removed: '#c22318',
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Code-fence hues: one value per syntax role, tuned per theme. Thinking uses
|
|
100
|
+
* the same hues desaturated toward the gray ladder instead of a hand-written
|
|
101
|
+
* second copy, so the two can never drift.
|
|
102
|
+
*/
|
|
103
|
+
interface CodeHues {
|
|
104
|
+
comment: string
|
|
105
|
+
string: string
|
|
106
|
+
number: string
|
|
107
|
+
keyword: string
|
|
108
|
+
fn: string
|
|
109
|
+
type: string
|
|
110
|
+
variable: string
|
|
111
|
+
fallback: string
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
const DARK_CODE: CodeHues = {
|
|
115
|
+
comment: '#5d9e50',
|
|
116
|
+
string: '#d78f6e',
|
|
117
|
+
number: '#82c46e',
|
|
118
|
+
keyword: '#c883c8',
|
|
119
|
+
fn: '#d8d89e',
|
|
120
|
+
type: '#52a898',
|
|
121
|
+
variable: '#4d9fd6',
|
|
122
|
+
fallback: '#a3b56a',
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
const LIGHT_CODE: CodeHues = {
|
|
126
|
+
comment: '#4a8a3d',
|
|
127
|
+
string: '#a05f42',
|
|
128
|
+
number: '#3f9a63',
|
|
129
|
+
keyword: '#8f4a88',
|
|
130
|
+
fn: '#7a7a3d',
|
|
131
|
+
type: '#2a7a6e',
|
|
132
|
+
variable: '#3a76a8',
|
|
133
|
+
fallback: '#647a34',
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/** Mix two hex colors channel-wise; t=0 keeps the hue, t=1 keeps the gray. */
|
|
137
|
+
function tint(hue: string, gray: string, t: number): string {
|
|
138
|
+
const channel = (hex: string, at: number): number => parseInt(hex.slice(at, at + 2), 16)
|
|
139
|
+
const mix = (at: number): string => {
|
|
140
|
+
const value = Math.round(channel(hue, at) * (1 - t) + channel(gray, at) * t)
|
|
141
|
+
return value.toString(16).padStart(2, '0')
|
|
142
|
+
}
|
|
143
|
+
return `#${mix(1)}${mix(3)}${mix(5)}`
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
const THINK_DESATURATE = 0.55
|
|
147
|
+
|
|
148
|
+
function buildPalette(ladder: GrayLadder, hues: SemanticHues, code: CodeHues, mode: 'dark' | 'light') {
|
|
149
|
+
const think = (hue: string): string => tint(hue, ladder.text, mode === 'dark' ? THINK_DESATURATE : 0.35)
|
|
150
|
+
const thinkCode = Object.fromEntries(
|
|
151
|
+
Object.entries(code).map(([key, hue]) => [key, think(hue)]),
|
|
152
|
+
) as Record<keyof CodeHues, string>
|
|
153
|
+
return {
|
|
154
|
+
ink: ladder.ink,
|
|
155
|
+
|
|
156
|
+
userBubbleBackground: ladder.surface,
|
|
157
|
+
aiBubbleBackground: ladder.sunken,
|
|
158
|
+
|
|
159
|
+
permissionBackground: ladder.sunken,
|
|
160
|
+
workspaceWriteText: hues.info,
|
|
161
|
+
dangerFullAccessText: hues.accent,
|
|
162
|
+
readOnlyText: hues.success,
|
|
163
|
+
|
|
164
|
+
dialogBackground: ladder.base,
|
|
165
|
+
dialogInputBackground: ladder.raised,
|
|
166
|
+
dialogHintText: ladder.text,
|
|
167
|
+
carouselCurrentBg: ladder.raised,
|
|
168
|
+
carouselButtonBg: ladder.sunken,
|
|
169
|
+
carouselButtonPressedBg: ladder.surface,
|
|
170
|
+
carouselSelectedText: hues.accent,
|
|
171
|
+
|
|
172
|
+
sectionHeader: hues.accent,
|
|
173
|
+
|
|
174
|
+
panelQuestionText: ladder.ink,
|
|
175
|
+
panelKeyText: ladder.ink,
|
|
176
|
+
modelText: ladder.ink,
|
|
177
|
+
effortText: hues.accent,
|
|
178
|
+
statusSeparator: ladder.text,
|
|
179
|
+
presetText: ladder.text,
|
|
180
|
+
|
|
181
|
+
cwdText: ladder.text,
|
|
182
|
+
statsText: ladder.text,
|
|
183
|
+
|
|
184
|
+
errorText: hues.error,
|
|
185
|
+
success: hues.success,
|
|
186
|
+
warning: hues.accent,
|
|
187
|
+
|
|
188
|
+
specialFieldText: mode === 'dark' ? '#1a1a1a' : '#3d2800',
|
|
189
|
+
specialFieldBackground: hues.accent,
|
|
190
|
+
|
|
191
|
+
toolLabel: mode === 'dark' ? '#2fc0e0' : '#0092b8',
|
|
192
|
+
toolBodyText: ladder.text,
|
|
193
|
+
|
|
194
|
+
diffAdded: hues.added,
|
|
195
|
+
diffRemoved: hues.removed,
|
|
196
|
+
diffAddedBackground: mode === 'dark' ? '#384751' : '#e1f1e5',
|
|
197
|
+
diffRemovedBackground: mode === 'dark' ? '#4f312c' : '#f5e8e5',
|
|
198
|
+
|
|
199
|
+
scrollTrackBackground: ladder.sunken,
|
|
200
|
+
scrollThumbBackground: ladder.line,
|
|
201
|
+
|
|
202
|
+
// The selection highlight is a fixed saturated blue in both modes; the
|
|
203
|
+
// foreground rides the terminal-default gray so selected text never
|
|
204
|
+
// outshines the selection itself.
|
|
205
|
+
selectionBg: '#0066ff',
|
|
206
|
+
selectionFg: '#cccccc',
|
|
207
|
+
|
|
208
|
+
mdLink: hues.info,
|
|
209
|
+
mdInlineCode: hues.added,
|
|
210
|
+
mdQuoteBar: ladder.line,
|
|
211
|
+
mdHr: ladder.line,
|
|
212
|
+
mdList: hues.accent,
|
|
213
|
+
mdTaskDone: hues.added,
|
|
214
|
+
mdTaskTodo: ladder.line,
|
|
215
|
+
mdH1: mode === 'dark' ? '#e0b568' : '#8a6d2f',
|
|
216
|
+
mdH3: mode === 'dark' ? '#64b5d6' : '#2f6fa8',
|
|
217
|
+
mdCodePlain: ladder.ink,
|
|
218
|
+
mdCodeFallback: code.fallback,
|
|
219
|
+
|
|
220
|
+
codeComment: code.comment,
|
|
221
|
+
codeString: code.string,
|
|
222
|
+
codeNumber: code.number,
|
|
223
|
+
codeKeyword: code.keyword,
|
|
224
|
+
codeFunction: code.fn,
|
|
225
|
+
codeType: code.type,
|
|
226
|
+
codeVariable: code.variable,
|
|
227
|
+
codeConstant: code.variable,
|
|
228
|
+
codeOperator: ladder.ink,
|
|
229
|
+
|
|
230
|
+
thinkLink: think(hues.info),
|
|
231
|
+
thinkInlineCode: think(hues.added),
|
|
232
|
+
thinkHr: tint(ladder.line, ladder.base, mode === 'dark' ? 0.35 : 0.15),
|
|
233
|
+
thinkList: ladder.text,
|
|
234
|
+
thinkTaskDone: think(hues.added),
|
|
235
|
+
thinkTaskTodo: ladder.line,
|
|
236
|
+
thinkH1: ladder.text,
|
|
237
|
+
thinkH3: ladder.text,
|
|
238
|
+
thinkCodePlain: ladder.text,
|
|
239
|
+
thinkCodeFallback: thinkCode.fallback,
|
|
240
|
+
thinkCodeComment: thinkCode.comment,
|
|
241
|
+
thinkCodeString: thinkCode.string,
|
|
242
|
+
thinkCodeNumber: thinkCode.number,
|
|
243
|
+
thinkCodeKeyword: thinkCode.keyword,
|
|
244
|
+
thinkCodeFunction: thinkCode.fn,
|
|
245
|
+
thinkCodeType: thinkCode.type,
|
|
246
|
+
thinkCodeVariable: thinkCode.variable,
|
|
247
|
+
thinkCodeConstant: thinkCode.variable,
|
|
248
|
+
|
|
249
|
+
homeLogoTop: mode === 'dark' ? '#2181ff' : '#0381ff',
|
|
250
|
+
homeLogoBottom: mode === 'dark' ? '#3a9aa0' : '#97c4ff',
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
const darkPalette = buildPalette(DARK_LADDER, DARK_HUES, DARK_CODE, 'dark')
|
|
255
|
+
const lightPalette = buildPalette(LIGHT_LADDER, LIGHT_HUES, LIGHT_CODE, 'light')
|
|
256
|
+
|
|
257
|
+
export interface Theme extends Readonly<Record<keyof typeof darkPalette, string>> {}
|
|
258
|
+
|
|
259
|
+
export const palettes: Record<ThemeMode, Theme> = { dark: darkPalette, light: lightPalette }
|
|
260
|
+
|
|
261
|
+
export const COLORS: Theme = { ...darkPalette }
|
|
262
|
+
|
|
263
|
+
let currentMode: ThemeMode = 'dark'
|
|
264
|
+
|
|
265
|
+
export function themeMode(): ThemeMode {
|
|
266
|
+
return currentMode
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
function permissionModes(): Record<PermissionModeId, { color: string; textColor: string; name: string }> {
|
|
270
|
+
return {
|
|
271
|
+
'workspace-write': { color: COLORS.permissionBackground, textColor: COLORS.workspaceWriteText, name: 'Workspace Write' },
|
|
272
|
+
'danger-full-access': { color: COLORS.permissionBackground, textColor: COLORS.dangerFullAccessText, name: 'Full access' },
|
|
273
|
+
'read-only': { color: COLORS.permissionBackground, textColor: COLORS.readOnlyText, name: 'Read Only' },
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
function effectivePalette(mode: ThemeMode): Theme {
|
|
278
|
+
const merged: Record<string, string> = { ...palettes[mode] }
|
|
279
|
+
for (const contribution of paletteContributions.values()) {
|
|
280
|
+
if (contribution.mode !== undefined && contribution.mode !== 'both' && contribution.mode !== mode) continue
|
|
281
|
+
Object.assign(merged, contribution.colors)
|
|
282
|
+
}
|
|
283
|
+
return merged as Theme
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
function materialize(mode: ThemeMode): void {
|
|
287
|
+
Object.assign(COLORS, effectivePalette(mode))
|
|
288
|
+
Object.assign(PERMISSION_MODES, permissionModes())
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
/**
|
|
292
|
+
* Read a palette color by key, including keys contributed by plugins that
|
|
293
|
+
* are not part of the built-in Theme type. Falls back to the raw key when
|
|
294
|
+
* nothing defines it, so custom semantic names remain visible.
|
|
295
|
+
*/
|
|
296
|
+
export function paletteColor(key: string): string {
|
|
297
|
+
const value = (COLORS as unknown as Record<string, string>)[key]
|
|
298
|
+
return value ?? key
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
export function setThemeMode(mode: ThemeMode): void {
|
|
302
|
+
currentMode = mode
|
|
303
|
+
materialize(mode)
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
export function replacePalettes(next: Record<ThemeMode, Theme>): void {
|
|
307
|
+
Object.assign(palettes.dark, next.dark)
|
|
308
|
+
Object.assign(palettes.light, next.light)
|
|
309
|
+
materialize(currentMode)
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
export const PERMISSION_MODES: Record<PermissionModeId, { color: string; textColor: string; name: string }> = permissionModes()
|
|
313
|
+
|
|
314
|
+
materialize(currentMode)
|
|
315
|
+
|
|
316
|
+
export function permissionModeInfo(mode: string): { color: string; textColor: string; name: string } {
|
|
317
|
+
return PERMISSION_MODES[mode as keyof typeof PERMISSION_MODES]
|
|
318
|
+
?? { color: COLORS.permissionBackground, textColor: COLORS.workspaceWriteText, name: mode }
|
|
319
|
+
}
|