@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.
Files changed (159) hide show
  1. package/bin/dshtui.js +65 -0
  2. package/cordis.patch.yml +81 -0
  3. package/lib/contract/index.d.mts +2 -0
  4. package/lib/contract/index.mjs +5 -0
  5. package/lib/dialog.d.mts +156 -0
  6. package/lib/dialog.mjs +3 -0
  7. package/lib/index-DqnaSXqP.d.mts +823 -0
  8. package/lib/index.d.mts +17 -0
  9. package/lib/index.mjs +11404 -0
  10. package/lib/prod-react-Dzof4qJx.mjs +4 -0
  11. package/lib/surface-DdxzIgIY.mjs +3126 -0
  12. package/lib/vendor.d.mts +3 -0
  13. package/lib/vendor.mjs +4 -0
  14. package/package.json +128 -0
  15. package/src/apply-theme.ts +8 -0
  16. package/src/boot-log.ts +35 -0
  17. package/src/chat/blocks.ts +41 -0
  18. package/src/chat/bridge.ts +1076 -0
  19. package/src/chat/builtin-tool-views.ts +53 -0
  20. package/src/chat/chat-face.ts +16 -0
  21. package/src/chat/chat-nodes.ts +53 -0
  22. package/src/chat/efforts.ts +4 -0
  23. package/src/chat/interactions.ts +329 -0
  24. package/src/chat/models.ts +263 -0
  25. package/src/chat/partial-json.ts +125 -0
  26. package/src/chat/presets.ts +26 -0
  27. package/src/chat/question-view.ts +106 -0
  28. package/src/chat/retry-status.ts +33 -0
  29. package/src/chat/session-groups.ts +34 -0
  30. package/src/chat/session-list.ts +311 -0
  31. package/src/chat/store.ts +636 -0
  32. package/src/chat/todo-view.ts +66 -0
  33. package/src/chat/tool-view.ts +311 -0
  34. package/src/chat/tool-views.ts +114 -0
  35. package/src/contract/index.ts +704 -0
  36. package/src/contract/upstream.ts +165 -0
  37. package/src/core/caret-nonce.ts +19 -0
  38. package/src/core/composer-layout.ts +65 -0
  39. package/src/core/edit.ts +152 -0
  40. package/src/core/field-view.ts +52 -0
  41. package/src/core/fields.ts +185 -0
  42. package/src/core/metrics.ts +26 -0
  43. package/src/core/paste.ts +149 -0
  44. package/src/core/segments.ts +112 -0
  45. package/src/core/text.ts +290 -0
  46. package/src/dialog.ts +11 -0
  47. package/src/env.ts +38 -0
  48. package/src/harness-home.ts +22 -0
  49. package/src/hot-theme.ts +56 -0
  50. package/src/index.tsx +185 -0
  51. package/src/kernel/registry.ts +102 -0
  52. package/src/kernel/surface.ts +33 -0
  53. package/src/log.ts +249 -0
  54. package/src/model/message.ts +89 -0
  55. package/src/model/selection.ts +63 -0
  56. package/src/performance-guard.ts +28 -0
  57. package/src/runtime/prod-react.ts +10 -0
  58. package/src/terminal/background.ts +66 -0
  59. package/src/terminal/capabilities.ts +54 -0
  60. package/src/terminal/clipboard-backends.ts +69 -0
  61. package/src/terminal/clipboard.ts +161 -0
  62. package/src/terminal/cursor-shape.ts +13 -0
  63. package/src/terminal/glyphs.ts +44 -0
  64. package/src/terminal/mouse.ts +154 -0
  65. package/src/terminal/probe.ts +61 -0
  66. package/src/terminal/screen.ts +349 -0
  67. package/src/theme-settings.ts +31 -0
  68. package/src/theme.ts +319 -0
  69. package/src/ui/app.tsx +454 -0
  70. package/src/ui/chrome/caps.tsx +9 -0
  71. package/src/ui/chrome/hint-service.ts +87 -0
  72. package/src/ui/chrome/input-status.ts +27 -0
  73. package/src/ui/chrome/key-gate.tsx +29 -0
  74. package/src/ui/chrome/status-bar.tsx +147 -0
  75. package/src/ui/contributions.ts +47 -0
  76. package/src/ui/dialog/defaults-dialog.tsx +152 -0
  77. package/src/ui/dialog/dialog-item.tsx +38 -0
  78. package/src/ui/dialog/dialog.tsx +386 -0
  79. package/src/ui/dialog/effort-dialog.tsx +45 -0
  80. package/src/ui/dialog/geometry.ts +131 -0
  81. package/src/ui/dialog/items.ts +124 -0
  82. package/src/ui/dialog/list-dialog.tsx +63 -0
  83. package/src/ui/dialog/models-dialog.tsx +203 -0
  84. package/src/ui/dialog/presets-dialog.tsx +46 -0
  85. package/src/ui/dialog/providers-dialog.tsx +313 -0
  86. package/src/ui/dialog/sessions-dialog.tsx +163 -0
  87. package/src/ui/dialog/sizes.ts +7 -0
  88. package/src/ui/dialog/status-lines.ts +11 -0
  89. package/src/ui/dialog/todo-dialog.tsx +29 -0
  90. package/src/ui/dialog/use-dialog-input.ts +186 -0
  91. package/src/ui/extension-point.ts +188 -0
  92. package/src/ui/home-logo.ts +132 -0
  93. package/src/ui/hooks/use-async-action.ts +25 -0
  94. package/src/ui/hooks/use-async-list.ts +71 -0
  95. package/src/ui/hooks/use-caret.ts +24 -0
  96. package/src/ui/hooks/use-chat-events.ts +153 -0
  97. package/src/ui/hooks/use-mouse-selection.ts +352 -0
  98. package/src/ui/hooks/use-scroll.ts +49 -0
  99. package/src/ui/hooks/use-terminal-size.ts +20 -0
  100. package/src/ui/input/commands.ts +118 -0
  101. package/src/ui/input/composer-bus.ts +14 -0
  102. package/src/ui/input/composer-fields.ts +111 -0
  103. package/src/ui/input/composer-keys.ts +21 -0
  104. package/src/ui/input/composer-paste.ts +20 -0
  105. package/src/ui/input/input-bar.tsx +279 -0
  106. package/src/ui/input/use-composer.ts +495 -0
  107. package/src/ui/key-arbiter.ts +37 -0
  108. package/src/ui/keymap.ts +15 -0
  109. package/src/ui/layout-service.ts +77 -0
  110. package/src/ui/message/layout.ts +763 -0
  111. package/src/ui/message/md/block.ts +247 -0
  112. package/src/ui/message/md/engine.ts +430 -0
  113. package/src/ui/message/md/extensions.ts +58 -0
  114. package/src/ui/message/md/highlight.ts +626 -0
  115. package/src/ui/message/md/index.ts +14 -0
  116. package/src/ui/message/md/inline.ts +97 -0
  117. package/src/ui/message/md/palette.ts +87 -0
  118. package/src/ui/message/message-list.tsx +69 -0
  119. package/src/ui/message/message-row.tsx +137 -0
  120. package/src/ui/message/message-views.ts +16 -0
  121. package/src/ui/message/renderers.ts +39 -0
  122. package/src/ui/message/tool-diff.ts +188 -0
  123. package/src/ui/message/warmup.ts +41 -0
  124. package/src/ui/message/wave.ts +98 -0
  125. package/src/ui/overlay.ts +22 -0
  126. package/src/ui/panels/approval-panel.tsx +193 -0
  127. package/src/ui/panels/exports.ts +3 -0
  128. package/src/ui/panels/question-model.ts +424 -0
  129. package/src/ui/panels/question-panel.tsx +154 -0
  130. package/src/ui/panels/surface.tsx +106 -0
  131. package/src/ui/panels-builtin.tsx +86 -0
  132. package/src/ui/pointer/builtins.ts +281 -0
  133. package/src/ui/pointer/registry.ts +25 -0
  134. package/src/ui/region.tsx +31 -0
  135. package/src/ui/selection/service.ts +92 -0
  136. package/src/ui/selection-registry.ts +88 -0
  137. package/src/ui/selection.tsx +148 -0
  138. package/src/ui/spinner-tick.tsx +24 -0
  139. package/src/ui/use-available-windows.ts +31 -0
  140. package/src/ui/widgets/actions.tsx +84 -0
  141. package/src/ui/widgets/button.tsx +57 -0
  142. package/src/ui/widgets/checkbox.tsx +58 -0
  143. package/src/ui/widgets/header.tsx +36 -0
  144. package/src/ui/widgets/index.ts +8 -0
  145. package/src/ui/widgets/input.tsx +76 -0
  146. package/src/ui/widgets/registry.ts +32 -0
  147. package/src/ui/widgets/search.tsx +59 -0
  148. package/src/ui/widgets/select.tsx +97 -0
  149. package/src/ui/widgets/static.tsx +37 -0
  150. package/src/ui/widgets/types.ts +1 -0
  151. package/src/ui/window-services-bridge.ts +71 -0
  152. package/src/ui/window-services.ts +20 -0
  153. package/src/ui/windows/models-window.tsx +15 -0
  154. package/src/ui/windows/providers-window.tsx +14 -0
  155. package/src/ui/windows/service-window.tsx +18 -0
  156. package/src/ui/windows/sessions-window.tsx +25 -0
  157. package/src/ui/windows-builtin.tsx +94 -0
  158. package/src/ui/windows.ts +18 -0
  159. 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
+ }