@deepseek-harness-tui/dsh-tui 0.7.2 → 0.8.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 (119) hide show
  1. package/README.md +3 -3
  2. package/bin/dsh-tui.js +24 -3
  3. package/cordis.patch.yml +24 -11
  4. package/cordis.yml +9 -1
  5. package/lib/types/commands.d.ts.map +1 -1
  6. package/lib/types/commands.js +2 -1
  7. package/lib/types/components/GoalTodoPanel.d.ts.map +1 -1
  8. package/lib/types/components/GoalTodoPanel.js +6 -1
  9. package/lib/types/components/HistorySearchDialog.d.ts.map +1 -1
  10. package/lib/types/components/HistorySearchDialog.js +5 -1
  11. package/lib/types/components/MessageList.d.ts +3 -1
  12. package/lib/types/components/MessageList.d.ts.map +1 -1
  13. package/lib/types/components/MessageList.js +9 -5
  14. package/lib/types/components/PluginSceneBoundary.d.ts +31 -0
  15. package/lib/types/components/PluginSceneBoundary.d.ts.map +1 -0
  16. package/lib/types/components/PluginSceneBoundary.js +29 -0
  17. package/lib/types/components/PromptInput.d.ts.map +1 -1
  18. package/lib/types/components/PromptInput.js +52 -74
  19. package/lib/types/components/SearchBox.d.ts +4 -0
  20. package/lib/types/components/SearchBox.d.ts.map +1 -1
  21. package/lib/types/components/SearchBox.js +101 -2
  22. package/lib/types/components/SkillsPicker.d.ts +18 -0
  23. package/lib/types/components/SkillsPicker.d.ts.map +1 -0
  24. package/lib/types/components/SkillsPicker.js +50 -0
  25. package/lib/types/components/Spinner/SpinnerAnimationRow.d.ts +4 -1
  26. package/lib/types/components/Spinner/SpinnerAnimationRow.d.ts.map +1 -1
  27. package/lib/types/components/Spinner/SpinnerAnimationRow.js +10 -5
  28. package/lib/types/components/SplitDiffView.d.ts +28 -0
  29. package/lib/types/components/SplitDiffView.d.ts.map +1 -0
  30. package/lib/types/components/SplitDiffView.js +417 -0
  31. package/lib/types/components/WorkingSpinner.d.ts +3 -1
  32. package/lib/types/components/WorkingSpinner.d.ts.map +1 -1
  33. package/lib/types/components/WorkingSpinner.js +2 -2
  34. package/lib/types/components/design-system/ThemedText.d.ts +7 -1
  35. package/lib/types/components/design-system/ThemedText.d.ts.map +1 -1
  36. package/lib/types/components/design-system/ThemedText.js +2 -2
  37. package/lib/types/components/messages/AssistantToolUseMessage.d.ts +3 -1
  38. package/lib/types/components/messages/AssistantToolUseMessage.d.ts.map +1 -1
  39. package/lib/types/components/messages/AssistantToolUseMessage.js +17 -7
  40. package/lib/types/components/questions/AskUserQuestionPanel.d.ts.map +1 -1
  41. package/lib/types/components/questions/AskUserQuestionPanel.js +52 -8
  42. package/lib/types/components/questions/PlanReviewPanel.d.ts.map +1 -1
  43. package/lib/types/components/questions/PlanReviewPanel.js +12 -3
  44. package/lib/types/dsh-adapter/channel.d.ts +78 -0
  45. package/lib/types/dsh-adapter/channel.d.ts.map +1 -1
  46. package/lib/types/dsh-adapter/channel.js +165 -14
  47. package/lib/types/dsh-adapter/contract.d.ts +1 -1
  48. package/lib/types/dsh-adapter/contract.d.ts.map +1 -1
  49. package/lib/types/dsh-adapter/contract.js +1 -0
  50. package/lib/types/dsh-adapter/index.d.ts +4 -0
  51. package/lib/types/dsh-adapter/index.d.ts.map +1 -1
  52. package/lib/types/dsh-adapter/index.js +1 -0
  53. package/lib/types/dsh-adapter/packaged-presets.d.ts.map +1 -1
  54. package/lib/types/dsh-adapter/packaged-presets.js +5 -2
  55. package/lib/types/dsh-adapter/plugin.d.ts.map +1 -1
  56. package/lib/types/dsh-adapter/plugin.js +66 -3
  57. package/lib/types/dsh-adapter/promptDebug.d.ts +10 -0
  58. package/lib/types/dsh-adapter/promptDebug.d.ts.map +1 -0
  59. package/lib/types/dsh-adapter/promptDebug.js +148 -0
  60. package/lib/types/dsh-adapter/providerWizard.d.ts.map +1 -1
  61. package/lib/types/dsh-adapter/providerWizard.js +3 -1
  62. package/lib/types/dsh-adapter/scenes.d.ts +75 -0
  63. package/lib/types/dsh-adapter/scenes.d.ts.map +1 -0
  64. package/lib/types/dsh-adapter/scenes.js +78 -0
  65. package/lib/types/dsh-adapter/settings-sections.d.ts +116 -0
  66. package/lib/types/dsh-adapter/settings-sections.d.ts.map +1 -0
  67. package/lib/types/dsh-adapter/settings-sections.js +63 -0
  68. package/lib/types/dsh-adapter/settingsEditor.d.ts +128 -0
  69. package/lib/types/dsh-adapter/settingsEditor.d.ts.map +1 -0
  70. package/lib/types/dsh-adapter/settingsEditor.js +244 -0
  71. package/lib/types/i18n.d.ts +169 -18
  72. package/lib/types/i18n.d.ts.map +1 -1
  73. package/lib/types/i18n.js +45 -6
  74. package/lib/types/ink/components/Text.d.ts +9 -1
  75. package/lib/types/ink/components/Text.d.ts.map +1 -1
  76. package/lib/types/ink/components/Text.js +7 -6
  77. package/lib/types/ink/ink.d.ts.map +1 -1
  78. package/lib/types/ink/ink.js +4 -2
  79. package/lib/types/ink/log-update.d.ts.map +1 -1
  80. package/lib/types/ink/log-update.js +76 -9
  81. package/lib/types/ink/parse-keypress.d.ts.map +1 -1
  82. package/lib/types/ink/parse-keypress.js +4 -1
  83. package/lib/types/ink/terminal.d.ts +40 -4
  84. package/lib/types/ink/terminal.d.ts.map +1 -1
  85. package/lib/types/ink/terminal.js +57 -5
  86. package/lib/types/ink/termio/tokenize.d.ts +6 -0
  87. package/lib/types/ink/termio/tokenize.d.ts.map +1 -1
  88. package/lib/types/ink/termio/tokenize.js +18 -4
  89. package/lib/types/jsx-runtime.d.ts +2 -0
  90. package/lib/types/jsx-runtime.d.ts.map +1 -0
  91. package/lib/types/jsx-runtime.js +13 -0
  92. package/lib/types/modelRoute.d.ts +10 -2
  93. package/lib/types/modelRoute.d.ts.map +1 -1
  94. package/lib/types/modelRoute.js +33 -4
  95. package/lib/types/scenes.d.ts +3 -0
  96. package/lib/types/scenes.d.ts.map +1 -0
  97. package/lib/types/scenes.js +4 -0
  98. package/lib/types/screens/Chat.d.ts.map +1 -1
  99. package/lib/types/screens/Chat.js +182 -26
  100. package/lib/types/screens/SessionBrowser.d.ts.map +1 -1
  101. package/lib/types/screens/SessionBrowser.js +30 -15
  102. package/lib/types/screens/Settings.d.ts +20 -0
  103. package/lib/types/screens/Settings.d.ts.map +1 -0
  104. package/lib/types/screens/Settings.js +333 -0
  105. package/lib/types/screens/StatusLine.js +1 -1
  106. package/lib/types/sessions/view.d.ts.map +1 -1
  107. package/lib/types/sessions/view.js +13 -1
  108. package/lib/types/settings-sections.d.ts +3 -0
  109. package/lib/types/settings-sections.d.ts.map +1 -0
  110. package/lib/types/settings-sections.js +4 -0
  111. package/lib/types/theme.d.ts +12 -0
  112. package/lib/types/theme.d.ts.map +1 -1
  113. package/lib/types/theme.js +36 -0
  114. package/lib/types/update.d.ts +21 -7
  115. package/lib/types/update.d.ts.map +1 -1
  116. package/lib/types/update.js +66 -16
  117. package/lib/types/utils/paths.d.ts.map +1 -1
  118. package/lib/types/utils/paths.js +4 -1
  119. package/package.json +49 -9
@@ -0,0 +1,333 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import React from 'react';
3
+ import { Box, Text, useInput, useTerminalSize } from '../ui.js';
4
+ import { Divider } from '../components/design-system/Divider.js';
5
+ import { HintLine } from '../components/design-system/HintLine.js';
6
+ import { isMod, isPlainReturn } from '../utils/modifiers.js';
7
+ import { truncateWidth } from '../sessions/format.js';
8
+ import { getLang, t } from '../i18n.js';
9
+ import { SettingsForm } from '../dsh-adapter/settingsEditor.js';
10
+ /** Pick the provider-owned translation for the active language. */
11
+ function pick(text, descriptions) {
12
+ return descriptions?.[getLang()] ?? text;
13
+ }
14
+ /** Compact one-line preview of a read-only namespace's resolved value. */
15
+ function valuePreview(value, budget) {
16
+ let raw;
17
+ try {
18
+ raw = JSON.stringify(value) ?? 'undefined';
19
+ }
20
+ catch {
21
+ raw = String(value);
22
+ }
23
+ return truncateWidth(raw, Math.max(8, budget));
24
+ }
25
+ /**
26
+ * The settings screen — `/settings` as a screen of its own (issue #165).
27
+ *
28
+ * The TUI owns only presentation here: plugin-declared sections from the
29
+ * `tuiSettingsSections` seam render as editable forms; every write goes back
30
+ * through the dsh settings service (revision-fenced `mutate` path ops) or the
31
+ * credentials seam (secret fields, blank-until-typed). Namespaces no plugin
32
+ * declared a section for stay read-only with a YAML hint — the same fallback
33
+ * the web front door gives namespaces without a card.
34
+ *
35
+ * Edits are staged, never settled live: typing changes a draft, and only the
36
+ * explicit save writes the durable document (see settingsEditor.ts for why).
37
+ */
38
+ export function Settings({ channel, onClose, }) {
39
+ // Explicit terminal size (not flexGrow) — the same rule SessionBrowser's
40
+ // root follows inside the alternate screen's fixed-height box.
41
+ const { columns, rows } = useTerminalSize();
42
+ // channel caches the host — a fresh object per call would re-fire the
43
+ // host-keyed effects below on every render (an endless render loop).
44
+ const host = channel.settingsHost();
45
+ const [namespaces, setNamespaces] = React.useState(() => host?.listNamespaces() ?? []);
46
+ const [sections, setSections] = React.useState(() => channel.settingsSections());
47
+ const [mode, setMode] = React.useState('list');
48
+ const [editing, setEditing] = React.useState(null);
49
+ const [focusIndex, setFocusIndex] = React.useState(0);
50
+ const [notice, setNotice] = React.useState(undefined);
51
+ /** Configured status of every secret field's credential ref. */
52
+ const [secrets, setSecrets] = React.useState(new Map());
53
+ /** First line of the windowed entry list (focus-follow scrolling). */
54
+ const [windowStart, setWindowStart] = React.useState(0);
55
+ /** Repaint after a form mutation — staged drafts live in the (React-free)
56
+ * SettingsForm, so editing one changes no React state by itself. */
57
+ const [, bump] = React.useReducer((count) => count + 1, 0);
58
+ /** Async save completions must not touch state after the screen closes. */
59
+ const mountedRef = React.useRef(true);
60
+ React.useEffect(() => () => {
61
+ mountedRef.current = false;
62
+ }, []);
63
+ React.useEffect(() => channel.subscribeSettingsSections(() => {
64
+ setSections(channel.settingsSections());
65
+ }), [channel]);
66
+ // One form per section namespace. A fresh namespace view replaces the form
67
+ // only while it holds no edits — replacing a dirty form would discard the
68
+ // drafts the user is still typing.
69
+ const formsRef = React.useRef(new Map());
70
+ const forms = new Map();
71
+ if (host !== undefined) {
72
+ for (const section of sections) {
73
+ const view = namespaces.find(entry => entry.ns === section.ns);
74
+ const kept = formsRef.current.get(section.ns);
75
+ const reuse = kept !== undefined && (kept.namespace === view || kept.shell().dirty);
76
+ const form = reuse ? kept : new SettingsForm(host, view, section.fields);
77
+ forms.set(section.ns, form);
78
+ }
79
+ }
80
+ formsRef.current = forms;
81
+ const refresh = () => {
82
+ setNamespaces(host?.listNamespaces() ?? []);
83
+ };
84
+ // Secret fields report only configured/unconfigured; resolve each ref once
85
+ // per section-list change (and after every save, which may have set one).
86
+ const [secretProbe, setSecretProbe] = React.useState(0);
87
+ React.useEffect(() => {
88
+ if (host === undefined)
89
+ return;
90
+ let stale = false;
91
+ const pending = sections.flatMap(section => section.fields
92
+ .filter((field) => field.secret !== undefined)
93
+ .map(async (field) => [`${section.ns}:${field.path.join('.')}`, await host.credentialConfigured(field.secret.ref)]));
94
+ void Promise.all(pending).then(entries => {
95
+ if (!stale && mountedRef.current)
96
+ setSecrets(new Map(entries));
97
+ });
98
+ return () => {
99
+ stale = true;
100
+ };
101
+ // eslint-disable-next-line react-hooks/exhaustive-deps
102
+ }, [host, sections, secretProbe]);
103
+ /** Focusable rows in display order: every field of every section. */
104
+ const focusable = sections.flatMap(section => section.fields.map(field => ({ ns: section.ns, field })));
105
+ const effFocus = Math.min(focusIndex, Math.max(0, focusable.length - 1));
106
+ const focused = focusable.length === 0 ? undefined : focusable[effFocus];
107
+ const focusedForm = focused === undefined ? undefined : forms.get(focused.ns);
108
+ const commitSave = (ns) => {
109
+ const form = forms.get(ns);
110
+ if (form === undefined || form.saving || !form.shell().dirty)
111
+ return;
112
+ void form.save().then(ok => {
113
+ if (!mountedRef.current)
114
+ return;
115
+ if (ok) {
116
+ setNotice({ text: t('settings-saved', { ns }), tone: 'success' });
117
+ refresh();
118
+ setSecretProbe(count => count + 1);
119
+ }
120
+ else {
121
+ setNotice({ text: t('settings-save-failed', { ns }), tone: 'error' });
122
+ }
123
+ });
124
+ };
125
+ /** Stage the next choice for a boolean/select field (Enter cycles). */
126
+ const cycleField = (ns, field) => {
127
+ const form = forms.get(ns);
128
+ if (form === undefined || !form.available || form.saving)
129
+ return;
130
+ const current = form.field(field).text;
131
+ if (field.kind === 'boolean') {
132
+ form.edit(field, current === 'true' ? 'false' : 'true');
133
+ }
134
+ else {
135
+ const options = field.options ?? [];
136
+ if (options.length === 0)
137
+ return;
138
+ const index = options.findIndex(option => option.value === current);
139
+ form.edit(field, options[(index + 1) % options.length]?.value ?? options[0]?.value ?? '');
140
+ }
141
+ setNotice(undefined);
142
+ bump();
143
+ };
144
+ useInput((input, key) => {
145
+ if (mode === 'edit' && editing !== null) {
146
+ if (isPlainReturn(key)) {
147
+ const form = forms.get(editing.ns);
148
+ form?.edit(editing.field, editing.draft);
149
+ bump();
150
+ setMode('list');
151
+ setEditing(null);
152
+ }
153
+ else if (key.escape) {
154
+ setMode('list');
155
+ setEditing(null);
156
+ }
157
+ else if (key.backspace || key.delete) {
158
+ setEditing(state => state === null ? null : { ...state, draft: state.draft.slice(0, -1) });
159
+ }
160
+ else if (!isMod(key) && !key.meta && !key.super && input && !key.return) {
161
+ // Only real characters reach the draft (see SessionBrowser's query).
162
+ const typed = input.replace(/\p{Cc}/gu, '');
163
+ if (typed.length > 0) {
164
+ setEditing(state => state === null ? null : { ...state, draft: state.draft + typed });
165
+ }
166
+ }
167
+ return;
168
+ }
169
+ if (key.upArrow) {
170
+ setFocusIndex(Math.max(0, effFocus - 1));
171
+ }
172
+ else if (key.downArrow) {
173
+ setFocusIndex(Math.min(Math.max(0, focusable.length - 1), effFocus + 1));
174
+ }
175
+ else if (isPlainReturn(key) && focused !== undefined) {
176
+ // A save in flight owns the section's drafts; starting an edit mid-write
177
+ // is exactly the lost-draft race the staged model exists to prevent.
178
+ if (focusedForm === undefined || !focusedForm.available || focusedForm.saving)
179
+ return;
180
+ if (focused.field.kind === 'boolean' || focused.field.kind === 'select') {
181
+ cycleField(focused.ns, focused.field);
182
+ }
183
+ else {
184
+ setEditing({ ns: focused.ns, field: focused.field, draft: focusedForm.field(focused.field).text });
185
+ setMode('edit');
186
+ }
187
+ }
188
+ else if (input === 's' && focused !== undefined) {
189
+ commitSave(focused.ns);
190
+ }
191
+ else if (input === 'd' && focused !== undefined) {
192
+ if (focusedForm?.saving)
193
+ return;
194
+ focusedForm?.discard();
195
+ setNotice(undefined);
196
+ bump();
197
+ }
198
+ else if (key.escape) {
199
+ // Esc backs out one layer at a time: staged drafts first — ANY dirty
200
+ // section, not just the focused one, so leaving never silently drops
201
+ // an edit made two sections ago — the screen second. A save in flight
202
+ // cannot be undone from here; let it settle instead of discarding
203
+ // around it.
204
+ const dirty = [...forms.values()].filter(form => form.shell().dirty);
205
+ if (dirty.length > 0) {
206
+ if (dirty.some(form => form.saving))
207
+ return;
208
+ for (const form of dirty)
209
+ form.discard();
210
+ setNotice({ text: t('settings-discarded'), tone: 'success' });
211
+ bump();
212
+ }
213
+ else {
214
+ onClose();
215
+ }
216
+ }
217
+ });
218
+ const renderField = (section, field) => {
219
+ const ns = section.ns;
220
+ const form = forms.get(ns);
221
+ const state = form?.field(field) ?? { text: '', overridden: false, invalid: false };
222
+ const isFocused = focused !== undefined && focused.ns === ns && focused.field === field;
223
+ const isEditing = isFocused && mode === 'edit' && editing !== null;
224
+ const label = pick(field.label, field.descriptions);
225
+ const hint = field.hint !== undefined ? pick(field.hint, field.hintDescriptions) : undefined;
226
+ let value;
227
+ if (field.secret !== undefined) {
228
+ const configured = secrets.get(`${ns}:${field.path.join('.')}`) === true;
229
+ if (isEditing) {
230
+ value = '•'.repeat(editing?.draft.length ?? 0) + '▌';
231
+ }
232
+ else if (state.text !== '') {
233
+ value = `${'•'.repeat(state.text.length)} ${t('settings-secret-staged')}`;
234
+ }
235
+ else {
236
+ value = configured ? t('settings-secret-set') : t('settings-secret-unset');
237
+ }
238
+ }
239
+ else if (isEditing) {
240
+ value = `${editing?.draft ?? ''}▌`;
241
+ }
242
+ else if (state.text === '') {
243
+ value = t('settings-field-empty');
244
+ }
245
+ else {
246
+ value = state.text;
247
+ }
248
+ return (_jsxs(Box, { children: [_jsx(Text, { color: isFocused ? 'suggestion' : undefined, children: isFocused ? '❯ ' : ' ' }), _jsx(Text, { bold: isFocused, children: label }), _jsx(Box, { flexGrow: 1 }), state.invalid && _jsxs(Text, { color: "error", children: [t('settings-field-invalid'), " "] }), state.overridden && _jsxs(Text, { dimColor: true, children: ["[", t('settings-badge-override'), "] "] }), form?.isStaged(field) === true && !isEditing && _jsx(Text, { color: "suggestion", children: "* " }), _jsx(Text, { color: isEditing || isFocused ? 'suggestion' : undefined, dimColor: !isFocused && !isEditing && state.text === '', children: value }), hint !== undefined && isFocused && (_jsxs(Text, { dimColor: true, children: ['\n ', hint] }))] }));
249
+ };
250
+ // ── Layout: a flat entry list with accounted line heights, windowed so the
251
+ // focused field is always on screen no matter how long the list gets. ────
252
+ const entries = [];
253
+ let focusCursor = 0;
254
+ sections.forEach((section, sectionIndex) => {
255
+ const form = forms.get(section.ns);
256
+ const view = form?.namespace;
257
+ const shell = form?.shell();
258
+ entries.push({
259
+ key: `section:${section.ns}`,
260
+ lines: sectionIndex === 0 ? 1 : 2,
261
+ node: (_jsxs(Box, { flexDirection: "column", children: [sectionIndex > 0 && _jsx(Text, { children: " " }), _jsxs(Box, { children: [_jsx(Text, { bold: true, color: "permission", children: pick(section.title, section.descriptions) }), _jsxs(Text, { dimColor: true, children: [" (", section.ns, ")"] }), view?.applies === 'restart' && _jsxs(Text, { color: "warning", children: [" [", t('settings-badge-restart'), "]"] }), view === undefined && _jsxs(Text, { color: "warning", children: [" [", t('settings-section-unavailable'), "]"] }), shell?.dirty === true && _jsxs(Text, { color: "suggestion", children: [" [", t('settings-badge-dirty'), "]"] }), shell?.saving === true && _jsxs(Text, { dimColor: true, children: [" [", t('settings-badge-saving'), "]"] }), shell?.failed === true && _jsxs(Text, { color: "error", children: [" [", t('settings-badge-failed'), "]"] })] })] })),
262
+ });
263
+ for (const field of section.fields) {
264
+ const isFocused = focused !== undefined && focused.ns === section.ns && focused.field === field;
265
+ const hasHint = field.hint !== undefined && isFocused;
266
+ const index = focusCursor;
267
+ focusCursor += 1;
268
+ entries.push({
269
+ key: `field:${section.ns}:${field.path.join('.')}`,
270
+ lines: hasHint ? 2 : 1,
271
+ focus: index,
272
+ node: renderField(section, field),
273
+ });
274
+ }
275
+ });
276
+ const registeredNs = new Set(sections.map(section => section.ns));
277
+ const readonlyNamespaces = namespaces.filter(entry => !registeredNs.has(entry.ns));
278
+ if (readonlyNamespaces.length > 0) {
279
+ entries.push({
280
+ key: 'readonly:heading',
281
+ lines: 2,
282
+ node: (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { children: " " }), _jsx(Text, { bold: true, dimColor: true, children: t('settings-readonly-heading') })] })),
283
+ });
284
+ for (const entry of readonlyNamespaces) {
285
+ entries.push({
286
+ key: `readonly:${entry.ns}`,
287
+ lines: 1,
288
+ node: (_jsxs(Box, { children: [_jsxs(Text, { children: [' ', entry.ns] }), entry.applies === 'restart' && _jsxs(Text, { color: "warning", children: [" [", t('settings-badge-restart'), "]"] }), _jsxs(Text, { dimColor: true, children: [' ', valuePreview(entry.value, 60)] })] })),
289
+ });
290
+ }
291
+ entries.push({
292
+ key: 'readonly:hint',
293
+ lines: 1,
294
+ node: _jsxs(Text, { dimColor: true, children: [' ', t('settings-readonly-hint', { path: '~/.dsh/settings.yaml' })] }),
295
+ });
296
+ }
297
+ if (sections.length === 0 && readonlyNamespaces.length === 0) {
298
+ entries.push({
299
+ key: 'empty',
300
+ lines: 1,
301
+ node: _jsx(Text, { dimColor: true, children: t('settings-empty') }),
302
+ });
303
+ }
304
+ // Focus-follow window: keep the focused entry fully inside the viewport.
305
+ let totalLines = 0;
306
+ let focusedOffset = 0;
307
+ let focusedLines = 1;
308
+ for (const entry of entries) {
309
+ if (entry.focus === effFocus) {
310
+ focusedOffset = totalLines;
311
+ focusedLines = entry.lines;
312
+ }
313
+ totalLines += entry.lines;
314
+ }
315
+ // Chrome: title row, top rule, bottom rule, hint row, plus the notice.
316
+ const viewport = Math.max(1, rows - 4 - (notice === undefined ? 0 : 1));
317
+ React.useEffect(() => {
318
+ setWindowStart(start => {
319
+ if (focusedOffset < start)
320
+ return focusedOffset;
321
+ if (focusedOffset + focusedLines > start + viewport)
322
+ return focusedOffset + focusedLines - viewport;
323
+ return start;
324
+ });
325
+ }, [focusedOffset, focusedLines, viewport]);
326
+ let entryOffset = 0;
327
+ const visible = entries.filter(entry => {
328
+ const start = entryOffset;
329
+ entryOffset += entry.lines;
330
+ return start >= windowStart && start + entry.lines <= windowStart + viewport;
331
+ });
332
+ return (_jsxs(Box, { flexDirection: "column", width: columns, height: rows, children: [_jsxs(Box, { children: [_jsx(Text, { bold: true, children: t('settings-title') }), _jsx(Box, { flexGrow: 1 }), host === undefined && _jsx(Text, { color: "warning", children: t('settings-unavailable') })] }), _jsx(Divider, {}), visible.map(entry => (_jsx(React.Fragment, { children: entry.node }, entry.key))), _jsx(Box, { flexGrow: 1 }), notice !== undefined && (_jsx(Text, { color: notice.tone === 'error' ? 'error' : 'success', children: notice.text })), _jsx(Divider, {}), _jsx(Text, { dimColor: true, italic: true, children: _jsx(HintLine, { text: mode === 'edit' ? t('settings-hint-edit') : t('settings-hint-list') }) })] }));
333
+ }
@@ -105,7 +105,7 @@ export function StatusLine({ channel, selectionActive = false, helpOpen = false,
105
105
  if (channel.contextBarEnabled && barWidth >= 14 && channel.contextWindow !== undefined) {
106
106
  bar = renderContextBar(channel.contextSegments, usage !== undefined ? usage.input + usage.cacheRead + usage.cacheWrite : 0, channel.contextWindow, barWidth, barColors);
107
107
  }
108
- return (_jsx(Box, { paddingX: 2, width: columns, flexShrink: 0, children: _jsxs(Box, { flexDirection: "column", width: "100%", children: [bar ? _jsx(Text, { children: bar }) : null, _jsxs(Box, { flexDirection: "row", justifyContent: "space-between", gap: 2, children: [_jsx(Text, { wrap: "truncate", children: _jsx(Byline, { children: leftParts }) }), _jsx(Box, { justifyContent: "flex-end", flexShrink: 2, children: _jsx(Text, { wrap: "truncate", children: _jsx(Byline, { children: rightParts }) }) })] }), _jsxs(Box, { height: 1, overflow: "hidden", flexDirection: "row", justifyContent: "space-between", gap: 2, children: [showActivity && activity !== undefined ? (_jsx(ActivityLine, { activity: activity, activityFrames: channel.activityFrames, warnPct: contextPressurePct(usage, channel.contextWindow), warnDanger: (contextPressurePct(usage, channel.contextWindow) ?? 0) >= 95 })) : hint ? (_jsx(Text, { color: "inactiveShimmer", children: hint })) : null, showActivity && hint ? (_jsx(Text, { color: "inactiveShimmer", wrap: "truncate", children: hint })) : null, wake !== undefined ? (_jsx(MiniWake, { band: wake.band, hint: wake.hint, tick: wake.tick })) : null] })] }) }));
108
+ return (_jsx(Box, { paddingX: 1, width: columns, flexShrink: 0, children: _jsxs(Box, { flexDirection: "column", width: "100%", children: [bar ? _jsx(Text, { children: bar }) : null, _jsxs(Box, { flexDirection: "row", justifyContent: "space-between", gap: 2, children: [_jsx(Text, { wrap: "truncate", children: _jsx(Byline, { children: leftParts }) }), _jsx(Box, { justifyContent: "flex-end", flexShrink: 2, children: _jsx(Text, { wrap: "truncate", children: _jsx(Byline, { children: rightParts }) }) })] }), _jsxs(Box, { height: 1, overflow: "hidden", flexDirection: "row", justifyContent: "space-between", gap: 2, children: [showActivity && activity !== undefined ? (_jsx(ActivityLine, { activity: activity, activityFrames: channel.activityFrames, warnPct: contextPressurePct(usage, channel.contextWindow), warnDanger: (contextPressurePct(usage, channel.contextWindow) ?? 0) >= 95 })) : hint ? (_jsx(Text, { color: "inactiveShimmer", children: hint })) : null, showActivity && hint ? (_jsx(Text, { color: "inactiveShimmer", wrap: "truncate", children: hint })) : null, wake !== undefined ? (_jsx(MiniWake, { band: wake.band, hint: wake.hint, tick: wake.tick })) : null] })] }) }));
109
109
  }
110
110
  function basename(path) {
111
111
  const parts = path.split(/[\\/]/);
@@ -1 +1 @@
1
- {"version":3,"file":"view.d.ts","sourceRoot":"","sources":["../../../src/sessions/view.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AACH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,kCAAkC,CAAA;AAEtE,6CAA6C;AAC7C,MAAM,WAAW,cAAc;IAC7B,mDAAmD;IACnD,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,wEAAwE;IACxE,QAAQ,CAAC,WAAW,EAAE,OAAO,CAAA;IAC7B,8DAA8D;IAC9D,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAA;IAC5B,qEAAqE;IACrE,QAAQ,CAAC,aAAa,EAAE,OAAO,CAAA;CAChC;AAED,oEAAoE;AACpE,eAAO,MAAM,eAAe,EAAE,cAK7B,CAAA;AAED,sCAAsC;AACtC,MAAM,MAAM,UAAU,GAClB;IAAE,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC;IAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAC9E;IAAE,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC;IAAC,QAAQ,CAAC,OAAO,EAAE,cAAc,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAA;AAE1F,uDAAuD;AACvD,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,IAAI,EAAE,SAAS,UAAU,EAAE,CAAA;IACpC,+CAA+C;IAC/C,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,yDAAyD;IACzD,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAA;IAChC,8EAA8E;IAC9E,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAA;IAC3B,2DAA2D;IAC3D,QAAQ,CAAC,QAAQ,EAAE,SAAS,MAAM,EAAE,CAAA;CACrC;AAgBD;;;;;;;;;;GAUG;AACH,wBAAgB,SAAS,CACvB,QAAQ,EAAE,SAAS,cAAc,EAAE,EACnC,OAAO,EAAE,cAAc,EACvB,OAAO,EAAE;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,KAAK,OAAO,CAAA;CAAE,GACtH,WAAW,CAuFb;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,SAAS,UAAU,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,GAAG,CAAC,CAAC,GAAG,MAAM,CAK9F;AAED;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,SAAS,UAAU,EAAE,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,GAAG,CAAC,CAAC,GAAG,MAAM,CAKhG;AAED,+DAA+D;AAC/D,wBAAgB,SAAS,CAAC,IAAI,EAAE,SAAS,UAAU,EAAE,EAAE,KAAK,EAAE,MAAM,GAAG,cAAc,GAAG,SAAS,CAGhG;AAED,2EAA2E;AAC3E,wBAAgB,SAAS,CAAC,GAAG,EAAE,UAAU,GAAG,MAAM,CAEjD;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,SAAS,CACvB,IAAI,EAAE,SAAS,UAAU,EAAE,EAC3B,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,MAAM,GACf,MAAM,CAoBR;AAED;;;;;;;GAOG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,SAAS,UAAU,EAAE,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAU1F"}
1
+ {"version":3,"file":"view.d.ts","sourceRoot":"","sources":["../../../src/sessions/view.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AACH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,kCAAkC,CAAA;AAEtE,6CAA6C;AAC7C,MAAM,WAAW,cAAc;IAC7B,mDAAmD;IACnD,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,wEAAwE;IACxE,QAAQ,CAAC,WAAW,EAAE,OAAO,CAAA;IAC7B,8DAA8D;IAC9D,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAA;IAC5B,qEAAqE;IACrE,QAAQ,CAAC,aAAa,EAAE,OAAO,CAAA;CAChC;AAED,oEAAoE;AACpE,eAAO,MAAM,eAAe,EAAE,cAK7B,CAAA;AAED,sCAAsC;AACtC,MAAM,MAAM,UAAU,GAClB;IAAE,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC;IAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAC9E;IAAE,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC;IAAC,QAAQ,CAAC,OAAO,EAAE,cAAc,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAA;AAE1F,uDAAuD;AACvD,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,IAAI,EAAE,SAAS,UAAU,EAAE,CAAA;IACpC,+CAA+C;IAC/C,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,yDAAyD;IACzD,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAA;IAChC,8EAA8E;IAC9E,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAA;IAC3B,2DAA2D;IAC3D,QAAQ,CAAC,QAAQ,EAAE,SAAS,MAAM,EAAE,CAAA;CACrC;AAgBD;;;;;;;;;;GAUG;AACH,wBAAgB,SAAS,CACvB,QAAQ,EAAE,SAAS,cAAc,EAAE,EACnC,OAAO,EAAE,cAAc,EACvB,OAAO,EAAE;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,KAAK,OAAO,CAAA;CAAE,GACtH,WAAW,CAmGb;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,SAAS,UAAU,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,GAAG,CAAC,CAAC,GAAG,MAAM,CAK9F;AAED;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,SAAS,UAAU,EAAE,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,GAAG,CAAC,CAAC,GAAG,MAAM,CAKhG;AAED,+DAA+D;AAC/D,wBAAgB,SAAS,CAAC,IAAI,EAAE,SAAS,UAAU,EAAE,EAAE,KAAK,EAAE,MAAM,GAAG,cAAc,GAAG,SAAS,CAGhG;AAED,2EAA2E;AAC3E,wBAAgB,SAAS,CAAC,GAAG,EAAE,UAAU,GAAG,MAAM,CAEjD;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,SAAS,CACvB,IAAI,EAAE,SAAS,UAAU,EAAE,EAC3B,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,MAAM,GACf,MAAM,CAoBR;AAED;;;;;;;GAOG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,SAAS,UAAU,EAAE,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAU1F"}
@@ -85,7 +85,19 @@ export function buildView(sessions, filters, context) {
85
85
  const rows = [];
86
86
  let shown = 0;
87
87
  let lastProject;
88
- for (const session of visible.sort((left, right) => right.updatedAt - left.updatedAt)) {
88
+ const ordered = visible.sort((left, right) => right.updatedAt - left.updatedAt);
89
+ if (filters.allProjects) {
90
+ // Order project groups by their newest visible session, then keep every
91
+ // session from that project together in MRU order. A global MRU sort alone
92
+ // can interleave A/B/A and emit the same project header more than once.
93
+ const projectOrder = new Map();
94
+ for (const session of ordered) {
95
+ if (!projectOrder.has(session.cwd))
96
+ projectOrder.set(session.cwd, projectOrder.size);
97
+ }
98
+ ordered.sort((left, right) => projectOrder.get(left.cwd) - projectOrder.get(right.cwd) || right.updatedAt - left.updatedAt);
99
+ }
100
+ for (const session of ordered) {
89
101
  // Group headers only earn their line when more than one project is in
90
102
  // play; inside a single project they would repeat the same path forever.
91
103
  if (filters.allProjects && session.cwd !== lastProject) {
@@ -0,0 +1,3 @@
1
+ export * from './dsh-adapter/settings-sections.js';
2
+ export { default } from './dsh-adapter/settings-sections.js';
3
+ //# sourceMappingURL=settings-sections.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"settings-sections.d.ts","sourceRoot":"","sources":["../../src/settings-sections.ts"],"names":[],"mappings":"AAEA,cAAc,oCAAoC,CAAA;AAClD,OAAO,EAAE,OAAO,EAAE,MAAM,oCAAoC,CAAA"}
@@ -0,0 +1,4 @@
1
+ // Re-export shim: the Cordis-backed implementation lives behind the adapter
2
+ // boundary so UI consumers never import official @deepseek-ai/* packages.
3
+ export * from './dsh-adapter/settings-sections.js';
4
+ export { default } from './dsh-adapter/settings-sections.js';
@@ -47,6 +47,18 @@ export type Theme = {
47
47
  diffRemovedDimmed: string;
48
48
  diffAddedWord: string;
49
49
  diffRemovedWord: string;
50
+ toolCardBackground: string;
51
+ toolCardBackgroundDim: string;
52
+ syntaxKeyword: string;
53
+ syntaxString: string;
54
+ syntaxComment: string;
55
+ syntaxNumber: string;
56
+ syntaxFunction: string;
57
+ syntaxType: string;
58
+ syntaxVariable: string;
59
+ syntaxOperator: string;
60
+ syntaxPunctuation: string;
61
+ syntaxConstant: string;
50
62
  red_FOR_SUBAGENTS_ONLY: string;
51
63
  blue_FOR_SUBAGENTS_ONLY: string;
52
64
  green_FOR_SUBAGENTS_ONLY: string;
@@ -1 +1 @@
1
- {"version":3,"file":"theme.d.ts","sourceRoot":"","sources":["../../src/theme.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"theme.d.ts","sourceRoot":"","sources":["../../src/theme.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,MAAM,MAAM,KAAK,GAAG;IAClB,UAAU,EAAE,MAAM,CAAA;IAClB,UAAU,EAAE,MAAM,CAAA;IAClB,MAAM,EAAE,MAAM,CAAA;IACd,aAAa,EAAE,MAAM,CAAA;IACrB,6BAA6B,EAAE,MAAM,CAAA;IACrC,oCAAoC,EAAE,MAAM,CAAA;IAC5C,UAAU,EAAE,MAAM,CAAA;IAClB,iBAAiB,EAAE,MAAM,CAAA;IACzB,QAAQ,EAAE,MAAM,CAAA;IAChB,GAAG,EAAE,MAAM,CAAA;IACX,YAAY,EAAE,MAAM,CAAA;IACpB,mBAAmB,EAAE,MAAM,CAAA;IAC3B,IAAI,EAAE,MAAM,CAAA;IACZ,WAAW,EAAE,MAAM,CAAA;IACnB,QAAQ,EAAE,MAAM,CAAA;IAChB,eAAe,EAAE,MAAM,CAAA;IACvB,MAAM,EAAE,MAAM,CAAA;IACd,UAAU,EAAE,MAAM,CAAA;IAClB,QAAQ,EAAE,MAAM,CAAA;IAChB,UAAU,EAAE,MAAM,CAAA;IAElB,OAAO,EAAE,MAAM,CAAA;IACf,KAAK,EAAE,MAAM,CAAA;IACb,OAAO,EAAE,MAAM,CAAA;IACf,MAAM,EAAE,MAAM,CAAA;IACd,cAAc,EAAE,MAAM,CAAA;IAEtB,SAAS,EAAE,MAAM,CAAA;IACjB,WAAW,EAAE,MAAM,CAAA;IACnB,eAAe,EAAE,MAAM,CAAA;IACvB,iBAAiB,EAAE,MAAM,CAAA;IACzB,aAAa,EAAE,MAAM,CAAA;IACrB,eAAe,EAAE,MAAM,CAAA;IAGvB,kBAAkB,EAAE,MAAM,CAAA;IAC1B,qBAAqB,EAAE,MAAM,CAAA;IAE7B,aAAa,EAAE,MAAM,CAAA;IACrB,YAAY,EAAE,MAAM,CAAA;IACpB,aAAa,EAAE,MAAM,CAAA;IACrB,YAAY,EAAE,MAAM,CAAA;IACpB,cAAc,EAAE,MAAM,CAAA;IACtB,UAAU,EAAE,MAAM,CAAA;IAClB,cAAc,EAAE,MAAM,CAAA;IACtB,cAAc,EAAE,MAAM,CAAA;IACtB,iBAAiB,EAAE,MAAM,CAAA;IACzB,cAAc,EAAE,MAAM,CAAA;IAEtB,sBAAsB,EAAE,MAAM,CAAA;IAC9B,uBAAuB,EAAE,MAAM,CAAA;IAC/B,wBAAwB,EAAE,MAAM,CAAA;IAChC,yBAAyB,EAAE,MAAM,CAAA;IACjC,yBAAyB,EAAE,MAAM,CAAA;IACjC,yBAAyB,EAAE,MAAM,CAAA;IACjC,uBAAuB,EAAE,MAAM,CAAA;IAC/B,uBAAuB,EAAE,MAAM,CAAA;IAE/B,gBAAgB,EAAE,MAAM,CAAA;IAExB,YAAY,EAAE,MAAM,CAAA;IAEpB,UAAU,EAAE,MAAM,CAAA;IAClB,gBAAgB,EAAE,MAAM,CAAA;IACxB,qBAAqB,EAAE,MAAM,CAAA;IAC7B,0BAA0B,EAAE,MAAM,CAAA;IAClC,wBAAwB,EAAE,MAAM,CAAA;IAChC,WAAW,EAAE,MAAM,CAAA;IACnB,0BAA0B,EAAE,MAAM,CAAA;IAClC,qBAAqB,EAAE,MAAM,CAAA;IAC7B,eAAe,EAAE,MAAM,CAAA;IACvB,gBAAgB,EAAE,MAAM,CAAA;IACxB,QAAQ,EAAE,MAAM,CAAA;IAChB,eAAe,EAAE,MAAM,CAAA;IACvB,aAAa,EAAE,MAAM,CAAA;IACrB,gBAAgB,EAAE,MAAM,CAAA;IACxB,WAAW,EAAE,MAAM,CAAA;IACnB,cAAc,EAAE,MAAM,CAAA;IACtB,cAAc,EAAE,MAAM,CAAA;IACtB,aAAa,EAAE,MAAM,CAAA;IACrB,YAAY,EAAE,MAAM,CAAA;IACpB,cAAc,EAAE,MAAM,CAAA;IACtB,cAAc,EAAE,MAAM,CAAA;IACtB,mBAAmB,EAAE,MAAM,CAAA;IAC3B,sBAAsB,EAAE,MAAM,CAAA;IAC9B,sBAAsB,EAAE,MAAM,CAAA;IAC9B,qBAAqB,EAAE,MAAM,CAAA;IAC7B,oBAAoB,EAAE,MAAM,CAAA;IAC5B,sBAAsB,EAAE,MAAM,CAAA;IAC9B,sBAAsB,EAAE,MAAM,CAAA;CAC/B,CAAA;AAED,kDAAkD;AAClD,eAAO,MAAM,WAAW,yCAA0C,CAAA;AAElE;;;;;;GAMG;AACH,eAAO,MAAM,eAAe,SAAS,CAAA;AAWrC;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI,CAE7D;AAED,oEAAoE;AACpE,wBAAgB,gBAAgB,IAAI,OAAO,GAAG,MAAM,CAEnD;AAED;;;;GAIG;AACH,MAAM,MAAM,SAAS,GAAG,MAAM,CAAA;AAsR9B;;;;;;GAMG;AACH,wBAAgB,QAAQ,CAAC,SAAS,EAAE,SAAS,GAAG,KAAK,CAiBpD;AASD;;;;;GAKG;AACH,wBAAgB,2BAA2B,CACzC,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,KAAK,GAAG,SAAS,GAC5C,IAAI,CAEN;AASD;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,SAAS,GAAG,IAAI,CAExD;AAED;;;GAGG;AACH,wBAAgB,cAAc,IAAI,KAAK,CAEtC"}
@@ -86,6 +86,18 @@ const darkTheme = {
86
86
  diffRemovedDimmed: rgb('#362B2C'),
87
87
  diffAddedWord: rgb('#57956B'),
88
88
  diffRemovedWord: rgb('#B26671'),
89
+ toolCardBackground: rgb('#242B3A'), // lighter blue-grey card surface
90
+ toolCardBackgroundDim: rgb('#1C2330'), // deeper blue substrate
91
+ syntaxKeyword: rgb('#8FA8E8'), // mist blue
92
+ syntaxString: rgb('#9FBF8F'), // soft sage
93
+ syntaxComment: rgb('#6B7280'), // neutral grey
94
+ syntaxNumber: rgb('#D9A97E'), // warm tan
95
+ syntaxFunction: rgb('#82B8C7'), // cyan blue
96
+ syntaxType: rgb('#B39DDB'), // soft violet
97
+ syntaxVariable: rgb('#C9D1D9'), // near-text
98
+ syntaxOperator: rgb('#93A1B0'), // blue grey
99
+ syntaxPunctuation: rgb('#7A8694'), // dim blue grey
100
+ syntaxConstant: rgb('#D98C9B'), // soft rose
89
101
  red_FOR_SUBAGENTS_ONLY: rgb('#D4685E'),
90
102
  blue_FOR_SUBAGENTS_ONLY: rgb('#7496D6'),
91
103
  green_FOR_SUBAGENTS_ONLY: rgb('#66B285'),
@@ -163,6 +175,18 @@ const lightTheme = {
163
175
  diffRemovedDimmed: rgb('#F5E6E4'),
164
176
  diffAddedWord: rgb('#A9D3B4'),
165
177
  diffRemovedWord: rgb('#E5B3AE'),
178
+ toolCardBackground: rgb('#E9EFF9'), // cool light blue card
179
+ toolCardBackgroundDim: rgb('#DEE7F4'), // deeper blue-tinted substrate
180
+ syntaxKeyword: rgb('#4A63A8'),
181
+ syntaxString: rgb('#4E7A4E'),
182
+ syntaxComment: rgb('#8A8F98'),
183
+ syntaxNumber: rgb('#A96B32'),
184
+ syntaxFunction: rgb('#3F7E8F'),
185
+ syntaxType: rgb('#7A5CA8'),
186
+ syntaxVariable: rgb('#343945'),
187
+ syntaxOperator: rgb('#5B6672'),
188
+ syntaxPunctuation: rgb('#9AA0A8'),
189
+ syntaxConstant: rgb('#B04A5A'),
166
190
  red_FOR_SUBAGENTS_ONLY: rgb('#BE5A52'),
167
191
  blue_FOR_SUBAGENTS_ONLY: rgb('#3F6CC4'),
168
192
  green_FOR_SUBAGENTS_ONLY: rgb('#4E9675'),
@@ -242,6 +266,18 @@ const darkAnsiTheme = {
242
266
  diffRemovedDimmed: 'ansi:red',
243
267
  diffAddedWord: 'ansi:greenBright',
244
268
  diffRemovedWord: 'ansi:redBright',
269
+ toolCardBackground: 'ansi:blackBright',
270
+ toolCardBackgroundDim: 'ansi:black',
271
+ syntaxKeyword: 'ansi:blueBright',
272
+ syntaxString: 'ansi:greenBright',
273
+ syntaxComment: 'ansi:blackBright',
274
+ syntaxNumber: 'ansi:yellowBright',
275
+ syntaxFunction: 'ansi:cyanBright',
276
+ syntaxType: 'ansi:magentaBright',
277
+ syntaxVariable: 'ansi:white',
278
+ syntaxOperator: 'ansi:white',
279
+ syntaxPunctuation: 'ansi:blackBright',
280
+ syntaxConstant: 'ansi:redBright',
245
281
  red_FOR_SUBAGENTS_ONLY: 'ansi:redBright',
246
282
  blue_FOR_SUBAGENTS_ONLY: 'ansi:blueBright',
247
283
  green_FOR_SUBAGENTS_ONLY: 'ansi:greenBright',
@@ -55,22 +55,36 @@ export declare function resolveTuiUpdateTarget(): Promise<TuiUpdateTarget>;
55
55
  * or blocks the interactive TUI.
56
56
  */
57
57
  export declare function checkForTuiUpdate(): Promise<TuiUpdateInfo | undefined>;
58
+ /** Build the profile-manager command, preferring a preflight-pinned version. */
59
+ export declare function tuiUpdatePluginArgs(profile: string, targetVersion?: string): string[];
60
+ /**
61
+ * The pnpm Windows tmp-rename race signature (issue #225): pnpm swaps a
62
+ * package directory via a `<name>_tmp_<pid>` staging dir, and a file lock or
63
+ * AV scan makes the scandir/rename fail with ENOENT/EPERM/EBUSY. The failure
64
+ * is transient — the identical command succeeds on retry — but the crashed
65
+ * run leaves a half-updated profile (manifest pins the old version while the
66
+ * lockfile already carries the new snapshot), which presents as "update did
67
+ * nothing" (#209). Genuine resolution errors never carry the `_tmp_<pid>`
68
+ * token, so matching both keeps the retry from masking real failures.
69
+ */
70
+ export declare function isTransientUpdateFailure(stderr: string): boolean;
58
71
  /**
59
72
  * Update the installed dsh-tui package and restart the same launcher while
60
73
  * preserving the active session. The TUI must already be unmounted before
61
74
  * this is called so pnpm output cannot corrupt the rendered terminal frame.
62
75
  *
63
- * `--latest` is required: `pnpm add` writes a caret range into the profile
64
- * manifest, and a plain `pnpm update` stays inside that range — with this
65
- * project's minor-per-release cadence the TUI would restart unchanged while
66
- * reporting success. The restart carries `DSH_TUI_UPDATED_FROM` so the new
67
- * process can warn when the version did not actually move (e.g. a mirror
68
- * registry still serving the old `latest`).
76
+ * When the preflight registry check resolved an exact target, pass that
77
+ * version to pnpm instead of resolving `latest` a second time. This avoids a
78
+ * stale mirror/dist-tag response between the check and install. If preflight
79
+ * failed, retain the `--latest` fallback: a plain `pnpm update` stays inside
80
+ * the manifest range and can restart unchanged across minor releases.
69
81
  *
70
82
  * @param sessionId - Session to resume in the replacement process.
71
83
  * @param profile - The dsh profile this TUI was launched with; updating any
72
84
  * other profile would leave the running install untouched.
85
+ * @param targetVersion - Exact version returned by the preflight registry
86
+ * check, or undefined when that check failed and pnpm should resolve latest.
73
87
  * @returns Exit codes for the update run and the replacement process.
74
88
  */
75
- export declare function updateTuiAndRestart(sessionId: string, profile: string): Promise<TuiUpdateResult>;
89
+ export declare function updateTuiAndRestart(sessionId: string, profile: string, targetVersion?: string): Promise<TuiUpdateResult>;
76
90
  //# sourceMappingURL=update.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"update.d.ts","sourceRoot":"","sources":["../../src/update.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAA;AAIlD,OAAO,EAAE,UAAU,EAAE,CAAA;AAQrB,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,MAAM,CAAA;IACf,MAAM,EAAE,MAAM,CAAA;CACf;AAED,4DAA4D;AAC5D,MAAM,MAAM,eAAe,GACvB;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GACnD;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GACnC;IAAE,IAAI,EAAE,SAAS,CAAA;CAAE,CAAA;AAEvB,MAAM,WAAW,eAAe;IAC9B,8EAA8E;IAC9E,UAAU,EAAE,MAAM,CAAA;IAClB;;;OAGG;IACH,WAAW,EAAE,MAAM,CAAA;CACpB;AAED,gFAAgF;AAChF,wBAAgB,mBAAmB,IAAI,MAAM,GAAG,SAAS,CAiBxD;AAED;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,GAAE,SAAS,MAAM,EAAiB,GAAG,MAAM,GAAG,SAAS,CAahG;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,IAAI,MAAM,CAW5C;AAED,6EAA6E;AAC7E,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAIzE;AA4BD;;;;GAIG;AACH,wBAAsB,sBAAsB,IAAI,OAAO,CAAC,eAAe,CAAC,CASvE;AAED;;;;GAIG;AACH,wBAAsB,iBAAiB,IAAI,OAAO,CAAC,aAAa,GAAG,SAAS,CAAC,CAG5E;AAgDD;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAsB,mBAAmB,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,CAAC,CAuBtG"}
1
+ {"version":3,"file":"update.d.ts","sourceRoot":"","sources":["../../src/update.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAA;AAIlD,OAAO,EAAE,UAAU,EAAE,CAAA;AAQrB,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,MAAM,CAAA;IACf,MAAM,EAAE,MAAM,CAAA;CACf;AAED,4DAA4D;AAC5D,MAAM,MAAM,eAAe,GACvB;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GACnD;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GACnC;IAAE,IAAI,EAAE,SAAS,CAAA;CAAE,CAAA;AAEvB,MAAM,WAAW,eAAe;IAC9B,8EAA8E;IAC9E,UAAU,EAAE,MAAM,CAAA;IAClB;;;OAGG;IACH,WAAW,EAAE,MAAM,CAAA;CACpB;AAED,gFAAgF;AAChF,wBAAgB,mBAAmB,IAAI,MAAM,GAAG,SAAS,CAiBxD;AAED;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,GAAE,SAAS,MAAM,EAAiB,GAAG,MAAM,GAAG,SAAS,CAahG;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,IAAI,MAAM,CAW5C;AAED,6EAA6E;AAC7E,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAIzE;AA4BD;;;;GAIG;AACH,wBAAsB,sBAAsB,IAAI,OAAO,CAAC,eAAe,CAAC,CASvE;AAED;;;;GAIG;AACH,wBAAsB,iBAAiB,IAAI,OAAO,CAAC,aAAa,GAAG,SAAS,CAAC,CAG5E;AA6DD,gFAAgF;AAChF,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,MAAM,EAAE,aAAa,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAIrF;AAED;;;;;;;;;GASG;AACH,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAEhE;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAsB,mBAAmB,CACvC,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,MAAM,EACf,aAAa,CAAC,EAAE,MAAM,GACrB,OAAO,CAAC,eAAe,CAAC,CAgD1B"}