@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,244 @@
1
+ /**
2
+ * React-free form model behind the `/settings` screen (issue #165), mirroring
3
+ * the web front door's card-form.ts semantics: a section stages what the user
4
+ * types and writes it only on save — every settings write is a durable,
5
+ * revision-fenced `settings.mutate`, so a control that committed as it
6
+ * settled turned one edit into a write the user never asked for and could
7
+ * not preview; staged text makes what is on screen exactly what a save would
8
+ * store.
9
+ *
10
+ * A field shows its effective value — the user layer over the composition
11
+ * layer over the schema default — and whether the user layer carries it. That
12
+ * presence, not a value comparison, is what marks a field overridden: an
13
+ * override equal to the composition default is still an override.
14
+ *
15
+ * The kernel side (storage, schema validation, layering, revision fencing)
16
+ * stays with the dsh settings / credentials services; this module only
17
+ * translates between drafts and `mutate` path ops.
18
+ */
19
+ /** Read a nested value by path (array indexes as strings). */
20
+ export function getPath(value, path) {
21
+ let current = value;
22
+ for (const key of path) {
23
+ if (Array.isArray(current)) {
24
+ current = current[Number(key)];
25
+ continue;
26
+ }
27
+ if (typeof current !== 'object' || current === null)
28
+ return undefined;
29
+ current = current[key];
30
+ }
31
+ return current;
32
+ }
33
+ /**
34
+ * Whether a value explicitly carries the path — its presence marks a user
35
+ * override, independent of the value stored there.
36
+ */
37
+ export function hasPath(value, path) {
38
+ if (path.length === 0)
39
+ return value !== undefined;
40
+ const parent = getPath(value, path.slice(0, -1));
41
+ const key = path[path.length - 1];
42
+ if (Array.isArray(parent))
43
+ return Number(key) < parent.length;
44
+ if (typeof parent !== 'object' || parent === null)
45
+ return false;
46
+ return key in parent;
47
+ }
48
+ /** Default draft text for a kind; the empty string when the section carries none. */
49
+ function defaultFormat(field, value) {
50
+ if (field.format)
51
+ return field.format(value);
52
+ if (value === undefined || value === null)
53
+ return '';
54
+ switch (field.kind) {
55
+ case 'number':
56
+ return typeof value === 'number' ? String(value) : '';
57
+ case 'boolean':
58
+ return value === true ? 'true' : 'false';
59
+ default:
60
+ return typeof value === 'string' ? value : String(value);
61
+ }
62
+ }
63
+ /** Default write for a kind: an empty text/number draft stages a clear. */
64
+ function defaultParse(field, text) {
65
+ if (field.parse)
66
+ return field.parse(text);
67
+ const trimmed = text.trim();
68
+ switch (field.kind) {
69
+ case 'number': {
70
+ if (trimmed === '')
71
+ return { kind: 'clear' };
72
+ const value = Number(trimmed);
73
+ return Number.isFinite(value) ? { kind: 'set', value } : undefined;
74
+ }
75
+ case 'boolean':
76
+ return { kind: 'set', value: trimmed === 'true' };
77
+ case 'select':
78
+ return field.options?.some(option => option.value === trimmed)
79
+ ? { kind: 'set', value: trimmed }
80
+ : undefined;
81
+ default:
82
+ return trimmed === '' ? { kind: 'clear' } : { kind: 'set', value: text };
83
+ }
84
+ }
85
+ /** Key addressing one field inside a form: its path joined. */
86
+ function fieldKey(field) {
87
+ return field.path.join('.');
88
+ }
89
+ /**
90
+ * The staged form over one settings namespace's declared fields. The screen
91
+ * re-seeds it from a fresh namespace view after every save and whenever the
92
+ * underlying document changes.
93
+ */
94
+ export class SettingsForm {
95
+ host;
96
+ view;
97
+ fields;
98
+ edits = new Map();
99
+ saving = false;
100
+ failed = false;
101
+ constructor(host, view, fields) {
102
+ this.host = host;
103
+ this.view = view;
104
+ this.fields = fields;
105
+ }
106
+ get available() {
107
+ return this.view !== undefined;
108
+ }
109
+ /** The namespace view the form was seeded from (undefined = not served). */
110
+ get namespace() {
111
+ return this.view;
112
+ }
113
+ field(field) {
114
+ const key = fieldKey(field);
115
+ const staged = this.edits.get(key);
116
+ if (field.secret !== undefined) {
117
+ // A credential literal never seeds a draft: blank until typed, and a
118
+ // blank draft writes nothing.
119
+ return { text: staged?.text ?? '', overridden: false, invalid: false };
120
+ }
121
+ if (staged !== undefined) {
122
+ const write = defaultParse(field, staged.text);
123
+ return {
124
+ text: staged.text,
125
+ overridden: write?.kind === 'set',
126
+ invalid: write === undefined,
127
+ };
128
+ }
129
+ return {
130
+ text: defaultFormat(field, this.view ? getPath(this.view.value, field.path) : undefined),
131
+ overridden: this.view !== undefined && hasPath(this.view.user, field.path),
132
+ invalid: false,
133
+ };
134
+ }
135
+ shell() {
136
+ let invalid = false;
137
+ for (const field of this.fields) {
138
+ if (this.field(field).invalid)
139
+ invalid = true;
140
+ }
141
+ return {
142
+ available: this.available,
143
+ dirty: this.edits.size > 0,
144
+ invalid,
145
+ saving: this.saving,
146
+ failed: this.failed,
147
+ };
148
+ }
149
+ /** Whether the field holds a staged (unsaved) edit. */
150
+ isStaged(field) {
151
+ return this.edits.has(fieldKey(field));
152
+ }
153
+ /** Stage draft text for one field. */
154
+ edit(field, text) {
155
+ this.edits.set(fieldKey(field), { text, clear: false });
156
+ this.failed = false;
157
+ }
158
+ /** Stage a clear, so saving lets the field re-inherit the composition layer. */
159
+ resetField(field) {
160
+ this.edits.set(fieldKey(field), { text: '', clear: true });
161
+ this.failed = false;
162
+ }
163
+ /** Drop every staged edit. */
164
+ discard() {
165
+ this.edits.clear();
166
+ this.failed = false;
167
+ }
168
+ /** Whether any staged draft is invalid, which blocks the save. */
169
+ get invalid() {
170
+ return this.shell().invalid;
171
+ }
172
+ /**
173
+ * Write every staged edit. Settings fields land as one revision-fenced
174
+ * `mutate` (one retry on a stale-revision conflict); credential fields
175
+ * write through the credentials seam. Resolves to whether every write
176
+ * landed — the caller re-seeds from a fresh namespace view afterwards.
177
+ *
178
+ * Only the edits THIS save snapshotted are cleared on success, and only
179
+ * when they are still the same edit: a draft typed while the write was in
180
+ * flight is a newer object and survives — it was never written, so it must
181
+ * not be silently dropped.
182
+ */
183
+ async save() {
184
+ if (this.view === undefined || this.invalid || this.saving)
185
+ return false;
186
+ const ns = this.view.ns;
187
+ const snapshot = new Map(this.edits);
188
+ const ops = [];
189
+ const secrets = [];
190
+ for (const field of this.fields) {
191
+ const staged = snapshot.get(fieldKey(field));
192
+ if (staged === undefined)
193
+ continue;
194
+ if (field.secret !== undefined) {
195
+ // Blank drafts write nothing; typed drafts replace the credential.
196
+ if (staged.text !== '')
197
+ secrets.push({ ref: field.secret.ref, text: staged.text });
198
+ continue;
199
+ }
200
+ const write = staged.clear ? { kind: 'clear' } : defaultParse(field, staged.text);
201
+ if (write === undefined)
202
+ return false;
203
+ ops.push(write.kind === 'clear'
204
+ ? { op: 'unset', path: field.path }
205
+ : { op: 'set', path: field.path, value: write.value });
206
+ }
207
+ this.saving = true;
208
+ try {
209
+ if (ops.length > 0) {
210
+ try {
211
+ await this.host.write(ns, ops, this.view.revision);
212
+ }
213
+ catch (error) {
214
+ // One retry on a stale-revision conflict (a concurrent write landed
215
+ // between seed and save); anything else propagates.
216
+ if (error?.code !== 'SETTINGS_CONFLICT')
217
+ throw error;
218
+ const fresh = this.host.listNamespaces().find(entry => entry.ns === ns);
219
+ await this.host.write(ns, ops, fresh?.revision);
220
+ }
221
+ }
222
+ for (const secret of secrets) {
223
+ await this.host.writeCredential(secret.ref, secret.text);
224
+ }
225
+ for (const [key, edit] of snapshot) {
226
+ if (this.edits.get(key) === edit)
227
+ this.edits.delete(key);
228
+ }
229
+ this.failed = false;
230
+ return true;
231
+ }
232
+ catch {
233
+ this.failed = true;
234
+ return false;
235
+ }
236
+ finally {
237
+ this.saving = false;
238
+ }
239
+ }
240
+ /** Whether any layer supplies the credential a secret field addresses. */
241
+ credentialConfigured(field) {
242
+ return field.secret === undefined ? Promise.resolve(false) : this.host.credentialConfigured(field.secret.ref);
243
+ }
244
+ }
@@ -700,13 +700,41 @@ declare const dict: {
700
700
  readonly en: "Created {{result}}";
701
701
  };
702
702
  readonly 'login-api-key': {
703
- readonly zh: "API key: {{key}}";
704
- readonly en: "API key: {{key}}";
703
+ readonly zh: "API key: {{status}}";
704
+ readonly en: "API key: {{status}}";
705
+ };
706
+ readonly 'login-key-configured': {
707
+ readonly zh: "已配置({{ref}})";
708
+ readonly en: "configured ({{ref}})";
705
709
  };
706
710
  readonly 'login-key-missing': {
707
711
  readonly zh: "未配置(DEEPSEEK_API_KEY)";
708
712
  readonly en: "not configured (DEEPSEEK_API_KEY)";
709
713
  };
714
+ readonly 'login-credentials-unavailable': {
715
+ readonly zh: "无法检查(credentials service 不可用)";
716
+ readonly en: "unavailable (credentials service unavailable)";
717
+ };
718
+ readonly 'login-credential-source': {
719
+ readonly zh: "凭据来源: {{source}}";
720
+ readonly en: "Credential source: {{source}}";
721
+ };
722
+ readonly 'login-source-none': {
723
+ readonly zh: "无";
724
+ readonly en: "none";
725
+ };
726
+ readonly 'login-credential-storage': {
727
+ readonly zh: "凭据存储: {{mode}}";
728
+ readonly en: "Credential storage: {{mode}}";
729
+ };
730
+ readonly 'login-storage-writable': {
731
+ readonly zh: "可写";
732
+ readonly en: "writable";
733
+ };
734
+ readonly 'login-storage-read-only': {
735
+ readonly zh: "只读";
736
+ readonly en: "read-only";
737
+ };
710
738
  readonly 'login-base-url': {
711
739
  readonly zh: "Base URL: {{url}}";
712
740
  readonly en: "Base URL: {{url}}";
@@ -715,13 +743,9 @@ declare const dict: {
715
743
  readonly zh: "官方端点";
716
744
  readonly en: "official endpoint";
717
745
  };
718
- readonly 'login-source-hint': {
719
- readonly zh: "来源:环境变量 → 工作区 .env(run.ts 兜底读取)";
720
- readonly en: "Source: env var → workspace .env (run.ts fallback)";
721
- };
722
746
  readonly 'login-logout-hint': {
723
- readonly zh: "DSH 凭证来自环境变量 DEEPSEEK_API_KEY — 删除该环境变量后重启 dsh-tui 即登出";
724
- readonly en: "DSH credentials come from the DEEPSEEK_API_KEY env var — remove it and restart dsh-tui to log out";
747
+ readonly zh: "使用 /provider 管理 DSH 凭据;若来源为 env,请删除对应环境变量并重启 dsh-tui";
748
+ readonly en: "Manage DSH credentials with /provider; for env sources, remove the corresponding environment variable and restart dsh-tui";
725
749
  };
726
750
  readonly 'permissions-policy-hint': {
727
751
  readonly zh: "DSH 权限策略由 fs-policy / bash-sandbox 配置决定(当前 leaf:workspace 内读写、写入需已读文件)。";
@@ -751,14 +775,6 @@ declare const dict: {
751
775
  readonly zh: "需要时可在 cordis.yml 挂载对应 hooks 插件。";
752
776
  readonly en: "Mount the matching hooks plugin in cordis.yml when needed.";
753
777
  };
754
- readonly 'memory-none': {
755
- readonly zh: "DSH 暂无持久记忆服务。";
756
- readonly en: "DSH has no persistent memory service yet.";
757
- };
758
- readonly 'memory-hint': {
759
- readonly zh: "长期约定可写入 AGENTS.md(工作区上下文)或技能(~/.dsh/skills)。";
760
- readonly en: "Long-term conventions can go into AGENTS.md (workspace context) or skills (~/.dsh/skills).";
761
- };
762
778
  readonly 'update-unavailable': {
763
779
  readonly zh: "当前运行方式不支持自动更新(需经 dsh --profile 启动),请在终端执行 dsh plugin --profile <name> update @deepseek-harness-tui/dsh-tui";
764
780
  readonly en: "Automatic update is unavailable in this launch mode (needs dsh --profile). Run dsh plugin --profile <name> update @deepseek-harness-tui/dsh-tui in a terminal.";
@@ -1203,6 +1219,90 @@ declare const dict: {
1203
1219
  readonly zh: "恢复会话";
1204
1220
  readonly en: "Resume session";
1205
1221
  };
1222
+ readonly 'settings-title': {
1223
+ readonly zh: "插件设置";
1224
+ readonly en: "Plugin settings";
1225
+ };
1226
+ readonly 'settings-unavailable': {
1227
+ readonly zh: "设置服务未挂载——只读";
1228
+ readonly en: "settings service absent — read-only";
1229
+ };
1230
+ readonly 'settings-empty': {
1231
+ readonly zh: "没有可配置的插件设置(尚无插件注册设置区块)";
1232
+ readonly en: "No configurable plugin settings (no plugin has registered a section)";
1233
+ };
1234
+ readonly 'settings-section-unavailable': {
1235
+ readonly zh: "命名空间未注册";
1236
+ readonly en: "namespace not served";
1237
+ };
1238
+ readonly 'settings-readonly-heading': {
1239
+ readonly zh: "其他设置命名空间(只读)";
1240
+ readonly en: "Other settings namespaces (read-only)";
1241
+ };
1242
+ readonly 'settings-readonly-hint': {
1243
+ readonly zh: "以上命名空间尚无 TUI 设置区块,可手工编辑 {{path}}";
1244
+ readonly en: "No TUI section for these namespaces yet — edit {{path}} by hand";
1245
+ };
1246
+ readonly 'settings-badge-override': {
1247
+ readonly zh: "已覆盖";
1248
+ readonly en: "overridden";
1249
+ };
1250
+ readonly 'settings-badge-restart': {
1251
+ readonly zh: "重启生效";
1252
+ readonly en: "applies on restart";
1253
+ };
1254
+ readonly 'settings-badge-dirty': {
1255
+ readonly zh: "未保存";
1256
+ readonly en: "unsaved";
1257
+ };
1258
+ readonly 'settings-badge-saving': {
1259
+ readonly zh: "保存中";
1260
+ readonly en: "saving";
1261
+ };
1262
+ readonly 'settings-badge-failed': {
1263
+ readonly zh: "保存失败";
1264
+ readonly en: "save failed";
1265
+ };
1266
+ readonly 'settings-field-empty': {
1267
+ readonly zh: "(未设置)";
1268
+ readonly en: "(unset)";
1269
+ };
1270
+ readonly 'settings-field-invalid': {
1271
+ readonly zh: "无效输入";
1272
+ readonly en: "invalid";
1273
+ };
1274
+ readonly 'settings-secret-set': {
1275
+ readonly zh: "●●●●●●(已配置)";
1276
+ readonly en: "●●●●●● (configured)";
1277
+ };
1278
+ readonly 'settings-secret-unset': {
1279
+ readonly zh: "(未配置)";
1280
+ readonly en: "(not configured)";
1281
+ };
1282
+ readonly 'settings-secret-staged': {
1283
+ readonly zh: "(待保存)";
1284
+ readonly en: "(pending save)";
1285
+ };
1286
+ readonly 'settings-saved': {
1287
+ readonly zh: "已保存 {{ns}}";
1288
+ readonly en: "Saved {{ns}}";
1289
+ };
1290
+ readonly 'settings-save-failed': {
1291
+ readonly zh: "保存 {{ns}} 失败——请重试";
1292
+ readonly en: "Saving {{ns}} failed — please retry";
1293
+ };
1294
+ readonly 'settings-discarded': {
1295
+ readonly zh: "已放弃所有未保存的修改";
1296
+ readonly en: "Discarded all unsaved edits";
1297
+ };
1298
+ readonly 'settings-hint-list': {
1299
+ readonly zh: "**Enter** 编辑/切换 · s 保存 · d 放弃 · Esc 放弃/退出";
1300
+ readonly en: "**Enter** edit/toggle · s save · d discard · Esc discard/exit";
1301
+ };
1302
+ readonly 'settings-hint-edit': {
1303
+ readonly zh: "**Enter** 确认 · Esc 取消";
1304
+ readonly en: "**Enter** to confirm · Esc to cancel";
1305
+ };
1206
1306
  readonly 'session-loading': {
1207
1307
  readonly zh: "正在读取会话…";
1208
1308
  readonly en: "Reading sessions…";
@@ -1331,6 +1431,10 @@ declare const dict: {
1331
1431
  readonly zh: "**Enter** 选择 · Esc 退出";
1332
1432
  readonly en: "**Enter** to select · Esc to exit";
1333
1433
  };
1434
+ readonly 'hint-fill-exit': {
1435
+ readonly zh: "**Enter** 填入命令 · Esc 退出";
1436
+ readonly en: "**Enter** to insert · Esc to exit";
1437
+ };
1334
1438
  readonly 'hint-rewind-back': {
1335
1439
  readonly zh: "**Enter** 回退 · Esc 返回";
1336
1440
  readonly en: "**Enter** to rewind · Esc to back";
@@ -1351,6 +1455,50 @@ declare const dict: {
1351
1455
  readonly zh: "模型";
1352
1456
  readonly en: "Model";
1353
1457
  };
1458
+ readonly 'picker-title-skills': {
1459
+ readonly zh: "技能";
1460
+ readonly en: "Skills";
1461
+ };
1462
+ readonly 'skills-loading': {
1463
+ readonly zh: "正在加载技能";
1464
+ readonly en: "Loading skills";
1465
+ };
1466
+ readonly 'skills-loading-subtitle': {
1467
+ readonly zh: "正在查询技能注册表…";
1468
+ readonly en: "Querying the skill registry…";
1469
+ };
1470
+ readonly 'skills-empty': {
1471
+ readonly zh: "当前会话没有可用技能";
1472
+ readonly en: "No skills available in this session";
1473
+ };
1474
+ readonly 'skills-load-failed': {
1475
+ readonly zh: "技能列表加载失败";
1476
+ readonly en: "Failed to load the skill list";
1477
+ };
1478
+ readonly 'plugin-scene-crashed': {
1479
+ readonly zh: "插件场景「{{id}}」渲染崩溃:{{err}}(已自动关闭)";
1480
+ readonly en: "Plugin scene \"{{id}}\" crashed while rendering: {{err}} (closed)";
1481
+ };
1482
+ readonly 'skills-source-bundled': {
1483
+ readonly zh: "内置";
1484
+ readonly en: "built-in";
1485
+ };
1486
+ readonly 'skills-source-user': {
1487
+ readonly zh: "用户";
1488
+ readonly en: "user";
1489
+ };
1490
+ readonly 'skills-source-project': {
1491
+ readonly zh: "项目";
1492
+ readonly en: "project";
1493
+ };
1494
+ readonly 'skills-source-runtime': {
1495
+ readonly zh: "运行时";
1496
+ readonly en: "runtime";
1497
+ };
1498
+ readonly 'skills-source-custom': {
1499
+ readonly zh: "自定义";
1500
+ readonly en: "custom";
1501
+ };
1354
1502
  readonly 'picker-title-theme': {
1355
1503
  readonly zh: "颜色主题";
1356
1504
  readonly en: "Color theme";
@@ -1867,6 +2015,9 @@ declare const dict: {
1867
2015
  readonly 'cmd-desc-config': {
1868
2016
  readonly zh: "查看 dsh-tui 配置来源";
1869
2017
  };
2018
+ readonly 'cmd-desc-settings': {
2019
+ readonly zh: "查看和编辑插件设置";
2020
+ };
1870
2021
  readonly 'cmd-desc-doctor': {
1871
2022
  readonly zh: "运行环境检查";
1872
2023
  };
@@ -1918,8 +2069,8 @@ declare const dict: {
1918
2069
  readonly 'cmd-desc-mcp': {
1919
2070
  readonly zh: "查看 MCP 状态";
1920
2071
  };
1921
- readonly 'cmd-desc-memory': {
1922
- readonly zh: "查看记忆状态";
2072
+ readonly 'cmd-desc-skills': {
2073
+ readonly zh: "列出所有可用技能";
1923
2074
  };
1924
2075
  readonly 'cmd-desc-update': {
1925
2076
  readonly zh: "更新 dsh-tui 并重启";
@@ -1 +1 @@
1
- {"version":3,"file":"i18n.d.ts","sourceRoot":"","sources":["../../src/i18n.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"i18n.d.ts","sourceRoot":"","sources":["../../src/i18n.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAMH,MAAM,MAAM,IAAI,GAAG,IAAI,GAAG,IAAI,CAAA;AAI9B,+DAA+D;AAC/D,eAAO,MAAM,KAAK,uBAAwB,CAAA;AAE1C,QAAA,MAAM,IAAI;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAkqBA,CAAA;AAEV,MAAM,MAAM,OAAO,GAAG,MAAM,OAAO,IAAI,CAAA;AACvC,MAAM,MAAM,UAAU,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,CAAA;AAUxD,uEAAuE;AACvE,KAAK,QAAQ,GAAG,MAAM,IAAI,CAAA;AAG1B,8EAA8E;AAC9E,wBAAgB,aAAa,CAAC,QAAQ,EAAE,QAAQ,GAAG,MAAM,IAAI,CAG5D;AAED,qCAAqC;AACrC,wBAAgB,OAAO,IAAI,IAAI,CAE9B;AAED,yDAAyD;AACzD,wBAAgB,OAAO,CAAC,IAAI,EAAE,IAAI,GAAG,IAAI,CAGxC;AAED,iDAAiD;AACjD,wBAAgB,MAAM,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,IAAI,CAEpD;AAED;;;;;;GAMG;AACH,wBAAgB,CAAC,CAAC,GAAG,EAAE,OAAO,EAAE,MAAM,GAAE,UAAe,GAAG,MAAM,CAM/D;AAED;;;;;;;;GAQG;AACH,wBAAgB,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAGzD;AAID;;;GAGG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAS5D;AAED,wEAAwE;AACxE,wBAAgB,YAAY,CAAC,GAAG,GAAE,MAAkB,GAAG,IAAI,GAAG,SAAS,CAMtE;AAED,iDAAiD;AACjD,wBAAgB,aAAa,CAAC,IAAI,EAAE,IAAI,EAAE,GAAG,GAAE,MAAkB,GAAG,OAAO,CAQ1E;AAED;;;;;;;;GAQG;AACH,wBAAgB,gBAAgB,IAAI,IAAI,CAavC;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,IAAI,IAAI,CAIzC"}
package/lib/types/i18n.js CHANGED
@@ -197,12 +197,18 @@ const dict = {
197
197
  'agentsmd-create-failed': { zh: '创建 AGENTS.md 失败', en: 'Failed to create AGENTS.md' },
198
198
  'agentsmd-exists': { zh: 'AGENTS.md 已存在,未覆盖', en: 'AGENTS.md already exists, not overwritten' },
199
199
  'agentsmd-created': { zh: '已创建 {{result}}', en: 'Created {{result}}' },
200
- 'login-api-key': { zh: 'API key: {{key}}', en: 'API key: {{key}}' },
200
+ 'login-api-key': { zh: 'API key: {{status}}', en: 'API key: {{status}}' },
201
+ 'login-key-configured': { zh: '已配置({{ref}})', en: 'configured ({{ref}})' },
201
202
  'login-key-missing': { zh: '未配置(DEEPSEEK_API_KEY)', en: 'not configured (DEEPSEEK_API_KEY)' },
203
+ 'login-credentials-unavailable': { zh: '无法检查(credentials service 不可用)', en: 'unavailable (credentials service unavailable)' },
204
+ 'login-credential-source': { zh: '凭据来源: {{source}}', en: 'Credential source: {{source}}' },
205
+ 'login-source-none': { zh: '无', en: 'none' },
206
+ 'login-credential-storage': { zh: '凭据存储: {{mode}}', en: 'Credential storage: {{mode}}' },
207
+ 'login-storage-writable': { zh: '可写', en: 'writable' },
208
+ 'login-storage-read-only': { zh: '只读', en: 'read-only' },
202
209
  'login-base-url': { zh: 'Base URL: {{url}}', en: 'Base URL: {{url}}' },
203
210
  'login-official-endpoint': { zh: '官方端点', en: 'official endpoint' },
204
- 'login-source-hint': { zh: '来源:环境变量 → 工作区 .env(run.ts 兜底读取)', en: 'Source: env var → workspace .env (run.ts fallback)' },
205
- 'login-logout-hint': { zh: 'DSH 凭证来自环境变量 DEEPSEEK_API_KEY — 删除该环境变量后重启 dsh-tui 即登出', en: 'DSH credentials come from the DEEPSEEK_API_KEY env var — remove it and restart dsh-tui to log out' },
211
+ 'login-logout-hint': { zh: '使用 /provider 管理 DSH 凭据;若来源为 env,请删除对应环境变量并重启 dsh-tui', en: 'Manage DSH credentials with /provider; for env sources, remove the corresponding environment variable and restart dsh-tui' },
206
212
  'permissions-policy-hint': { zh: 'DSH 权限策略由 fs-policy / bash-sandbox 配置决定(当前 leaf:workspace 内读写、写入需已读文件)。', en: 'DSH permission policy is set by fs-policy / bash-sandbox config (current leaf: read/write in workspace, writes need a prior read).' },
207
213
  'permissions-approval-hint': { zh: '审批通道已挂载:命令申请权限提升(sandbox_permissions)时弹出审批条,Yes 放行一次、No / Esc 拒绝。', en: 'The approval channel is mounted: sandbox escalations (sandbox_permissions) raise an approval bar — Yes allows once, No / Esc rejects.' },
208
214
  'permissions-preset-hint': { zh: '/permission 可查看与切换权限预设(read-only / workspace-write / danger-full-access)。', en: '/permission shows and switches permission presets (read-only / workspace-write / danger-full-access).' },
@@ -210,8 +216,6 @@ const dict = {
210
216
  'permissions-path-hint': { zh: '模型工具相对路径均解析自该目录;跨目录访问由 fs-policy 拦截。', en: 'Relative paths of model tools resolve from this directory; cross-directory access is blocked by fs-policy.' },
211
217
  'hooks-not-mounted': { zh: 'DSH hooks(dsh-hooks-claude / dsh-hooks-codex)未在本 leaf 挂载。', en: 'DSH hooks (dsh-hooks-claude / dsh-hooks-codex) are not mounted in this leaf.' },
212
218
  'hooks-mount-hint': { zh: '需要时可在 cordis.yml 挂载对应 hooks 插件。', en: 'Mount the matching hooks plugin in cordis.yml when needed.' },
213
- 'memory-none': { zh: 'DSH 暂无持久记忆服务。', en: 'DSH has no persistent memory service yet.' },
214
- 'memory-hint': { zh: '长期约定可写入 AGENTS.md(工作区上下文)或技能(~/.dsh/skills)。', en: 'Long-term conventions can go into AGENTS.md (workspace context) or skills (~/.dsh/skills).' },
215
219
  'update-unavailable': { zh: '当前运行方式不支持自动更新(需经 dsh --profile 启动),请在终端执行 dsh plugin --profile <name> update @deepseek-harness-tui/dsh-tui', en: 'Automatic update is unavailable in this launch mode (needs dsh --profile). Run dsh plugin --profile <name> update @deepseek-harness-tui/dsh-tui in a terminal.' },
216
220
  'update-working': { zh: '当前回合仍在运行,请等待完成后再更新 TUI。', en: 'The current turn is still running. Wait for it to finish before updating the TUI.' },
217
221
  'update-starting': { zh: '正在更新 @deepseek-harness-tui/dsh-tui,完成后会自动重启并恢复当前会话……', en: 'Updating @deepseek-harness-tui/dsh-tui. The TUI will restart and resume this session when finished…' },
@@ -337,6 +341,28 @@ const dict = {
337
341
  'resume-hint-delete': { zh: '**Enter** 删除 · Esc 取消', en: '**Enter** to delete · Esc to cancel' },
338
342
  'resume-hint-rename': { zh: '**Enter** 保存 · Esc 取消', en: '**Enter** to save · Esc to cancel' },
339
343
  'resume-title': { zh: '恢复会话', en: 'Resume session' },
344
+ // ── screens/Settings.tsx (/settings, issue #165) ───────────────────
345
+ 'settings-title': { zh: '插件设置', en: 'Plugin settings' },
346
+ 'settings-unavailable': { zh: '设置服务未挂载——只读', en: 'settings service absent — read-only' },
347
+ 'settings-empty': { zh: '没有可配置的插件设置(尚无插件注册设置区块)', en: 'No configurable plugin settings (no plugin has registered a section)' },
348
+ 'settings-section-unavailable': { zh: '命名空间未注册', en: 'namespace not served' },
349
+ 'settings-readonly-heading': { zh: '其他设置命名空间(只读)', en: 'Other settings namespaces (read-only)' },
350
+ 'settings-readonly-hint': { zh: '以上命名空间尚无 TUI 设置区块,可手工编辑 {{path}}', en: 'No TUI section for these namespaces yet — edit {{path}} by hand' },
351
+ 'settings-badge-override': { zh: '已覆盖', en: 'overridden' },
352
+ 'settings-badge-restart': { zh: '重启生效', en: 'applies on restart' },
353
+ 'settings-badge-dirty': { zh: '未保存', en: 'unsaved' },
354
+ 'settings-badge-saving': { zh: '保存中', en: 'saving' },
355
+ 'settings-badge-failed': { zh: '保存失败', en: 'save failed' },
356
+ 'settings-field-empty': { zh: '(未设置)', en: '(unset)' },
357
+ 'settings-field-invalid': { zh: '无效输入', en: 'invalid' },
358
+ 'settings-secret-set': { zh: '●●●●●●(已配置)', en: '●●●●●● (configured)' },
359
+ 'settings-secret-unset': { zh: '(未配置)', en: '(not configured)' },
360
+ 'settings-secret-staged': { zh: '(待保存)', en: '(pending save)' },
361
+ 'settings-saved': { zh: '已保存 {{ns}}', en: 'Saved {{ns}}' },
362
+ 'settings-save-failed': { zh: '保存 {{ns}} 失败——请重试', en: 'Saving {{ns}} failed — please retry' },
363
+ 'settings-discarded': { zh: '已放弃所有未保存的修改', en: 'Discarded all unsaved edits' },
364
+ 'settings-hint-list': { zh: '**Enter** 编辑/切换 · s 保存 · d 放弃 · Esc 放弃/退出', en: '**Enter** edit/toggle · s save · d discard · Esc discard/exit' },
365
+ 'settings-hint-edit': { zh: '**Enter** 确认 · Esc 取消', en: '**Enter** to confirm · Esc to cancel' },
340
366
  // ── 会话浏览器:行、计数、筛选、预览 ───────────────────────────────
341
367
  'session-loading': { zh: '正在读取会话…', en: 'Reading sessions…' },
342
368
  'session-list-failed': { zh: '无法读取会话列表 · {{err}}', en: 'Could not read the session list · {{err}}' },
@@ -374,12 +400,24 @@ const dict = {
374
400
  'hint-confirm-exit': { zh: '**Enter** 确认 · Esc 退出', en: '**Enter** to confirm · Esc to exit' },
375
401
  'hint-confirm-cancel': { zh: '**Enter** 确认 · Esc 取消', en: '**Enter** to confirm · Esc to cancel' },
376
402
  'hint-select-exit': { zh: '**Enter** 选择 · Esc 退出', en: '**Enter** to select · Esc to exit' },
403
+ 'hint-fill-exit': { zh: '**Enter** 填入命令 · Esc 退出', en: '**Enter** to insert · Esc to exit' },
377
404
  'hint-rewind-back': { zh: '**Enter** 回退 · Esc 返回', en: '**Enter** to rewind · Esc to back' },
378
405
  'hint-adjust-done': { zh: '**←/→** 调整 · Enter/Esc 完成', en: '**←/→** to adjust · Enter/Esc to done' },
379
406
  'hint-history-search': { zh: '↑/↓ 选择 · **Enter** 确认 · Esc 取消', en: '↑/↓ to navigate · **Enter** to select · Esc to cancel' },
380
407
  'hint-expand-ctrl-o': { zh: '(ctrl+o 展开)', en: '(ctrl+o to expand)' },
381
408
  // ── components/ModelPicker.tsx / ThemePicker.tsx / ActivityPicker.tsx / EffortSlider.tsx ──
382
409
  'picker-title-model': { zh: '模型', en: 'Model' },
410
+ 'picker-title-skills': { zh: '技能', en: 'Skills' },
411
+ 'skills-loading': { zh: '正在加载技能', en: 'Loading skills' },
412
+ 'skills-loading-subtitle': { zh: '正在查询技能注册表…', en: 'Querying the skill registry…' },
413
+ 'skills-empty': { zh: '当前会话没有可用技能', en: 'No skills available in this session' },
414
+ 'skills-load-failed': { zh: '技能列表加载失败', en: 'Failed to load the skill list' },
415
+ 'plugin-scene-crashed': { zh: '插件场景「{{id}}」渲染崩溃:{{err}}(已自动关闭)', en: 'Plugin scene "{{id}}" crashed while rendering: {{err}} (closed)' },
416
+ 'skills-source-bundled': { zh: '内置', en: 'built-in' },
417
+ 'skills-source-user': { zh: '用户', en: 'user' },
418
+ 'skills-source-project': { zh: '项目', en: 'project' },
419
+ 'skills-source-runtime': { zh: '运行时', en: 'runtime' },
420
+ 'skills-source-custom': { zh: '自定义', en: 'custom' },
383
421
  'picker-title-theme': { zh: '颜色主题', en: 'Color theme' },
384
422
  'picker-title-activity': { zh: '指示器预设', en: 'Indicator preset' },
385
423
  'picker-title-effort': { zh: '推理强度', en: 'Reasoning effort' },
@@ -530,6 +568,7 @@ const dict = {
530
568
  'cmd-desc-status': { zh: '查看会话状态' },
531
569
  'cmd-desc-cost': { zh: '查看会话 token 用量' },
532
570
  'cmd-desc-config': { zh: '查看 dsh-tui 配置来源' },
571
+ 'cmd-desc-settings': { zh: '查看和编辑插件设置' },
533
572
  'cmd-desc-doctor': { zh: '运行环境检查' },
534
573
  'cmd-desc-init': { zh: '在工作目录创建 AGENTS.md' },
535
574
  'cmd-desc-agents': { zh: '查看本会话的子代理' },
@@ -549,7 +588,7 @@ const dict = {
549
588
  'cmd-desc-add-dir': { zh: '查看文件系统策略范围' },
550
589
  'cmd-desc-hooks': { zh: '查看 hooks 状态' },
551
590
  'cmd-desc-mcp': { zh: '查看 MCP 状态' },
552
- 'cmd-desc-memory': { zh: '查看记忆状态' },
591
+ 'cmd-desc-skills': { zh: '列出所有可用技能' },
553
592
  'cmd-desc-update': { zh: '更新 dsh-tui 并重启' },
554
593
  // Built-in skills
555
594
  'cmd-desc-audit': { zh: '对当前项目做全面代码审计' },
@@ -1,10 +1,18 @@
1
- import type { ReactNode } from 'react';
1
+ import type { ReactNode, Ref } from 'react';
2
2
  import type { Color, Styles } from '../styles.js';
3
+ import type { DOMElement } from '../dom.js';
3
4
  type BaseProps = {
4
5
  /**
5
6
  * Change text color. Accepts a raw color value (rgb, hex, ansi).
6
7
  */
7
8
  readonly color?: Color;
9
+ /**
10
+ * Ref to the underlying ink-text DOMElement. dsh-tui addition: lets
11
+ * useDeclaredCursor park the native cursor on an inline caret cell without
12
+ * wrapping it in a Box (a wrapper Box changes flex shrink behaviour and
13
+ * breaks wrapped-line layouts).
14
+ */
15
+ readonly ref?: Ref<DOMElement>;
8
16
  /**
9
17
  * Same as `color`, but for background.
10
18
  */
@@ -1 +1 @@
1
- {"version":3,"file":"Text.d.ts","sourceRoot":"","sources":["../../../../src/ink/components/Text.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAEvC,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,EAAc,MAAM,cAAc,CAAC;AAC9D,KAAK,SAAS,GAAG;IACf;;OAEG;IACH,QAAQ,CAAC,KAAK,CAAC,EAAE,KAAK,CAAC;IAEvB;;OAEG;IACH,QAAQ,CAAC,eAAe,CAAC,EAAE,KAAK,CAAC;IAEjC;;OAEG;IACH,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC;IAE1B;;OAEG;IACH,QAAQ,CAAC,SAAS,CAAC,EAAE,OAAO,CAAC;IAE7B;;OAEG;IACH,QAAQ,CAAC,aAAa,CAAC,EAAE,OAAO,CAAC;IAEjC;;OAEG;IACH,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,CAAC;IAE3B;;;;OAIG;IACH,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC,CAAC;IACnC,QAAQ,CAAC,QAAQ,CAAC,EAAE,SAAS,CAAC;CAC/B,CAAC;AAEF;;;GAGG;AACH,KAAK,WAAW,GAAG;IACjB,IAAI,CAAC,EAAE,KAAK,CAAC;IACb,GAAG,CAAC,EAAE,KAAK,CAAC;CACb,GAAG;IACF,IAAI,EAAE,OAAO,CAAC;IACd,GAAG,CAAC,EAAE,KAAK,CAAC;CACb,GAAG;IACF,GAAG,EAAE,OAAO,CAAC;IACb,IAAI,CAAC,EAAE,KAAK,CAAC;CACd,CAAC;AACF,MAAM,MAAM,KAAK,GAAG,SAAS,GAAG,WAAW,CAAC;AAoD5C;;GAEG;AACH,MAAM,CAAC,OAAO,UAAU,IAAI,CAAC,EAAE,KAAA,OA2I9B"}
1
+ {"version":3,"file":"Text.d.ts","sourceRoot":"","sources":["../../../../src/ink/components/Text.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,GAAG,EAAE,MAAM,OAAO,CAAC;AAE5C,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,EAAc,MAAM,cAAc,CAAC;AAC9D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAC5C,KAAK,SAAS,GAAG;IACf;;OAEG;IACH,QAAQ,CAAC,KAAK,CAAC,EAAE,KAAK,CAAC;IACvB;;;;;OAKG;IACH,QAAQ,CAAC,GAAG,CAAC,EAAE,GAAG,CAAC,UAAU,CAAC,CAAC;IAE/B;;OAEG;IACH,QAAQ,CAAC,eAAe,CAAC,EAAE,KAAK,CAAC;IAEjC;;OAEG;IACH,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC;IAE1B;;OAEG;IACH,QAAQ,CAAC,SAAS,CAAC,EAAE,OAAO,CAAC;IAE7B;;OAEG;IACH,QAAQ,CAAC,aAAa,CAAC,EAAE,OAAO,CAAC;IAEjC;;OAEG;IACH,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,CAAC;IAE3B;;;;OAIG;IACH,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC,CAAC;IACnC,QAAQ,CAAC,QAAQ,CAAC,EAAE,SAAS,CAAC;CAC/B,CAAC;AAEF;;;GAGG;AACH,KAAK,WAAW,GAAG;IACjB,IAAI,CAAC,EAAE,KAAK,CAAC;IACb,GAAG,CAAC,EAAE,KAAK,CAAC;CACb,GAAG;IACF,IAAI,EAAE,OAAO,CAAC;IACd,GAAG,CAAC,EAAE,KAAK,CAAC;CACb,GAAG;IACF,GAAG,EAAE,OAAO,CAAC;IACb,IAAI,CAAC,EAAE,KAAK,CAAC;CACd,CAAC;AACF,MAAM,MAAM,KAAK,GAAG,SAAS,GAAG,WAAW,CAAC;AAoD5C;;GAEG;AACH,MAAM,CAAC,OAAO,UAAU,IAAI,CAAC,EAAE,KAAA,OA6I9B"}