@deepseek-harness-tui/dsh-tui 0.8.7 → 0.9.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 (229) hide show
  1. package/README.md +29 -10
  2. package/bin/dsh-tui.js +1 -1
  3. package/lib/types/commands.d.ts.map +1 -1
  4. package/lib/types/commands.js +4 -2
  5. package/lib/types/components/ActivityPicker.d.ts +4 -1
  6. package/lib/types/components/ActivityPicker.d.ts.map +1 -1
  7. package/lib/types/components/ActivityPicker.js +2 -2
  8. package/lib/types/components/BtwPanel.d.ts.map +1 -1
  9. package/lib/types/components/BtwPanel.js +1 -1
  10. package/lib/types/components/Chat/SubagentMessage.d.ts +2 -1
  11. package/lib/types/components/Chat/SubagentMessage.d.ts.map +1 -1
  12. package/lib/types/components/Chat/SubagentMessage.js +11 -3
  13. package/lib/types/components/CommandSuggestions.d.ts +21 -4
  14. package/lib/types/components/CommandSuggestions.d.ts.map +1 -1
  15. package/lib/types/components/CommandSuggestions.js +41 -14
  16. package/lib/types/components/EffortSlider.d.ts +4 -1
  17. package/lib/types/components/EffortSlider.d.ts.map +1 -1
  18. package/lib/types/components/EffortSlider.js +4 -2
  19. package/lib/types/components/ExtensionDialog.js +6 -2
  20. package/lib/types/components/FileSuggestions.d.ts +26 -5
  21. package/lib/types/components/FileSuggestions.d.ts.map +1 -1
  22. package/lib/types/components/FileSuggestions.js +53 -16
  23. package/lib/types/components/GoalTodoPanel.d.ts +26 -2
  24. package/lib/types/components/GoalTodoPanel.d.ts.map +1 -1
  25. package/lib/types/components/GoalTodoPanel.js +86 -17
  26. package/lib/types/components/HelpMenu.d.ts +7 -1
  27. package/lib/types/components/HelpMenu.d.ts.map +1 -1
  28. package/lib/types/components/HelpMenu.js +12 -4
  29. package/lib/types/components/HistorySearchDialog.d.ts +4 -1
  30. package/lib/types/components/HistorySearchDialog.d.ts.map +1 -1
  31. package/lib/types/components/HistorySearchDialog.js +2 -2
  32. package/lib/types/components/LangPicker.d.ts +14 -0
  33. package/lib/types/components/LangPicker.d.ts.map +1 -0
  34. package/lib/types/components/LangPicker.js +17 -0
  35. package/lib/types/components/LoadedContextPanel.d.ts.map +1 -1
  36. package/lib/types/components/LoadedContextPanel.js +3 -1
  37. package/lib/types/components/LogoV2.d.ts +5 -1
  38. package/lib/types/components/LogoV2.d.ts.map +1 -1
  39. package/lib/types/components/LogoV2.js +12 -3
  40. package/lib/types/components/Markdown.d.ts.map +1 -1
  41. package/lib/types/components/Markdown.js +12 -3
  42. package/lib/types/components/MessageList.d.ts +43 -2
  43. package/lib/types/components/MessageList.d.ts.map +1 -1
  44. package/lib/types/components/MessageList.js +553 -41
  45. package/lib/types/components/ModelPicker.d.ts +4 -1
  46. package/lib/types/components/ModelPicker.d.ts.map +1 -1
  47. package/lib/types/components/ModelPicker.js +2 -2
  48. package/lib/types/components/PermissionsPicker.d.ts +21 -0
  49. package/lib/types/components/PermissionsPicker.d.ts.map +1 -0
  50. package/lib/types/components/PermissionsPicker.js +27 -0
  51. package/lib/types/components/PlanPicker.d.ts +15 -0
  52. package/lib/types/components/PlanPicker.d.ts.map +1 -0
  53. package/lib/types/components/PlanPicker.js +18 -0
  54. package/lib/types/components/PresetPicker.d.ts +4 -1
  55. package/lib/types/components/PresetPicker.d.ts.map +1 -1
  56. package/lib/types/components/PresetPicker.js +2 -2
  57. package/lib/types/components/PromptInput.d.ts +4 -3
  58. package/lib/types/components/PromptInput.d.ts.map +1 -1
  59. package/lib/types/components/PromptInput.js +273 -50
  60. package/lib/types/components/RewindPicker.d.ts +12 -1
  61. package/lib/types/components/RewindPicker.d.ts.map +1 -1
  62. package/lib/types/components/RewindPicker.js +6 -4
  63. package/lib/types/components/ScrollbarGutter.d.ts +28 -0
  64. package/lib/types/components/ScrollbarGutter.d.ts.map +1 -0
  65. package/lib/types/components/ScrollbarGutter.js +68 -0
  66. package/lib/types/components/Select.d.ts +8 -1
  67. package/lib/types/components/Select.d.ts.map +1 -1
  68. package/lib/types/components/Select.js +4 -2
  69. package/lib/types/components/SkillsPicker.d.ts +4 -1
  70. package/lib/types/components/SkillsPicker.d.ts.map +1 -1
  71. package/lib/types/components/SkillsPicker.js +2 -2
  72. package/lib/types/components/SubagentCard.d.ts +2 -1
  73. package/lib/types/components/SubagentCard.d.ts.map +1 -1
  74. package/lib/types/components/SubagentCard.js +6 -1
  75. package/lib/types/components/SubagentDashboard.d.ts +4 -0
  76. package/lib/types/components/SubagentDashboard.d.ts.map +1 -1
  77. package/lib/types/components/SubagentDashboard.js +9 -1
  78. package/lib/types/components/SubagentDetailScene.d.ts.map +1 -1
  79. package/lib/types/components/SubagentDetailScene.js +5 -4
  80. package/lib/types/components/SuggestionCard.d.ts +63 -0
  81. package/lib/types/components/SuggestionCard.d.ts.map +1 -0
  82. package/lib/types/components/SuggestionCard.js +77 -0
  83. package/lib/types/components/ThemePicker.d.ts +4 -1
  84. package/lib/types/components/ThemePicker.d.ts.map +1 -1
  85. package/lib/types/components/ThemePicker.js +2 -2
  86. package/lib/types/components/ThinkingToggle.d.ts +4 -1
  87. package/lib/types/components/ThinkingToggle.d.ts.map +1 -1
  88. package/lib/types/components/ThinkingToggle.js +2 -2
  89. package/lib/types/components/TimelineRail.d.ts +53 -0
  90. package/lib/types/components/TimelineRail.d.ts.map +1 -0
  91. package/lib/types/components/TimelineRail.js +190 -0
  92. package/lib/types/components/WorkspaceFlowPicker.d.ts +6 -1
  93. package/lib/types/components/WorkspaceFlowPicker.d.ts.map +1 -1
  94. package/lib/types/components/WorkspaceFlowPicker.js +4 -2
  95. package/lib/types/components/WorkspaceMenuPicker.d.ts +19 -0
  96. package/lib/types/components/WorkspaceMenuPicker.d.ts.map +1 -0
  97. package/lib/types/components/WorkspaceMenuPicker.js +19 -0
  98. package/lib/types/components/WorkspacePicker.d.ts +4 -1
  99. package/lib/types/components/WorkspacePicker.d.ts.map +1 -1
  100. package/lib/types/components/WorkspacePicker.js +2 -2
  101. package/lib/types/components/approvals/ApprovalPanel.d.ts.map +1 -1
  102. package/lib/types/components/approvals/ApprovalPanel.js +5 -1
  103. package/lib/types/components/design-system/ListItem.d.ts +8 -1
  104. package/lib/types/components/design-system/ListItem.d.ts.map +1 -1
  105. package/lib/types/components/design-system/ListItem.js +8 -3
  106. package/lib/types/components/messages/AssistantTextMessage.d.ts +5 -2
  107. package/lib/types/components/messages/AssistantTextMessage.d.ts.map +1 -1
  108. package/lib/types/components/messages/AssistantTextMessage.js +6 -2
  109. package/lib/types/components/messages/AssistantThinkingMessage.d.ts +2 -1
  110. package/lib/types/components/messages/AssistantThinkingMessage.d.ts.map +1 -1
  111. package/lib/types/components/messages/AssistantThinkingMessage.js +10 -4
  112. package/lib/types/components/messages/AssistantToolUseMessage.d.ts +8 -1
  113. package/lib/types/components/messages/AssistantToolUseMessage.d.ts.map +1 -1
  114. package/lib/types/components/messages/AssistantToolUseMessage.js +4 -2
  115. package/lib/types/components/messages/UserPromptMessage.d.ts +2 -1
  116. package/lib/types/components/messages/UserPromptMessage.d.ts.map +1 -1
  117. package/lib/types/components/questions/AskUserQuestionPanel.d.ts.map +1 -1
  118. package/lib/types/components/questions/AskUserQuestionPanel.js +34 -2
  119. package/lib/types/components/questions/PlanReviewPanel.d.ts.map +1 -1
  120. package/lib/types/components/questions/PlanReviewPanel.js +13 -2
  121. package/lib/types/components/sessions/SessionListRow.d.ts +4 -1
  122. package/lib/types/components/sessions/SessionListRow.d.ts.map +1 -1
  123. package/lib/types/components/sessions/SessionListRow.js +4 -2
  124. package/lib/types/components/trajectory/HotspotView.d.ts +7 -1
  125. package/lib/types/components/trajectory/HotspotView.d.ts.map +1 -1
  126. package/lib/types/components/trajectory/HotspotView.js +8 -2
  127. package/lib/types/components/trajectory/Ledger.d.ts +9 -1
  128. package/lib/types/components/trajectory/Ledger.d.ts.map +1 -1
  129. package/lib/types/components/trajectory/Ledger.js +23 -5
  130. package/lib/types/components/trajectory/WaveBand.d.ts +10 -1
  131. package/lib/types/components/trajectory/WaveBand.d.ts.map +1 -1
  132. package/lib/types/components/trajectory/WaveBand.js +7 -2
  133. package/lib/types/dsh-adapter/channel.d.ts +27 -6
  134. package/lib/types/dsh-adapter/channel.d.ts.map +1 -1
  135. package/lib/types/dsh-adapter/channel.js +432 -66
  136. package/lib/types/dsh-adapter/contract.d.ts +58 -18
  137. package/lib/types/dsh-adapter/contract.d.ts.map +1 -1
  138. package/lib/types/dsh-adapter/contract.js +124 -36
  139. package/lib/types/dsh-adapter/index.d.ts +10 -2
  140. package/lib/types/dsh-adapter/index.d.ts.map +1 -1
  141. package/lib/types/dsh-adapter/index.js +10 -13
  142. package/lib/types/dsh-adapter/plugin.d.ts +0 -9
  143. package/lib/types/dsh-adapter/plugin.d.ts.map +1 -1
  144. package/lib/types/dsh-adapter/plugin.js +136 -6
  145. package/lib/types/i18n.d.ts +194 -16
  146. package/lib/types/i18n.d.ts.map +1 -1
  147. package/lib/types/i18n.js +73 -10
  148. package/lib/types/ink/components/AlternateScreen.d.ts.map +1 -1
  149. package/lib/types/ink/components/AlternateScreen.js +13 -1
  150. package/lib/types/ink/components/App.d.ts +12 -1
  151. package/lib/types/ink/components/App.d.ts.map +1 -1
  152. package/lib/types/ink/components/App.js +101 -4
  153. package/lib/types/ink/components/ClockContext.d.ts.map +1 -1
  154. package/lib/types/ink/components/ClockContext.js +2 -0
  155. package/lib/types/ink/components/ScrollBox.d.ts.map +1 -1
  156. package/lib/types/ink/components/ScrollBox.js +170 -91
  157. package/lib/types/ink/constants.d.ts +9 -0
  158. package/lib/types/ink/constants.d.ts.map +1 -1
  159. package/lib/types/ink/constants.js +9 -0
  160. package/lib/types/ink/events/click-event.d.ts +7 -16
  161. package/lib/types/ink/events/click-event.d.ts.map +1 -1
  162. package/lib/types/ink/events/click-event.js +16 -19
  163. package/lib/types/ink/events/event-handlers.d.ts +11 -1
  164. package/lib/types/ink/events/event-handlers.d.ts.map +1 -1
  165. package/lib/types/ink/events/event-handlers.js +2 -0
  166. package/lib/types/ink/events/input-event.d.ts +9 -0
  167. package/lib/types/ink/events/input-event.d.ts.map +1 -1
  168. package/lib/types/ink/events/input-event.js +4 -0
  169. package/lib/types/ink/events/pointer-event.d.ts +61 -0
  170. package/lib/types/ink/events/pointer-event.d.ts.map +1 -0
  171. package/lib/types/ink/events/pointer-event.js +61 -0
  172. package/lib/types/ink/events/wheel-event.d.ts +17 -0
  173. package/lib/types/ink/events/wheel-event.d.ts.map +1 -0
  174. package/lib/types/ink/events/wheel-event.js +20 -0
  175. package/lib/types/ink/geometry-trace.d.ts +51 -0
  176. package/lib/types/ink/geometry-trace.d.ts.map +1 -0
  177. package/lib/types/ink/geometry-trace.js +83 -0
  178. package/lib/types/ink/hit-test.d.ts +56 -1
  179. package/lib/types/ink/hit-test.d.ts.map +1 -1
  180. package/lib/types/ink/hit-test.js +146 -8
  181. package/lib/types/ink/ink.d.ts +58 -1
  182. package/lib/types/ink/ink.d.ts.map +1 -1
  183. package/lib/types/ink/ink.js +268 -52
  184. package/lib/types/ink/node-cache.d.ts +5 -0
  185. package/lib/types/ink/node-cache.d.ts.map +1 -1
  186. package/lib/types/ink/parse-keypress.d.ts +8 -0
  187. package/lib/types/ink/parse-keypress.d.ts.map +1 -1
  188. package/lib/types/ink/parse-keypress.js +101 -12
  189. package/lib/types/ink/render-node-to-output.d.ts +16 -3
  190. package/lib/types/ink/render-node-to-output.d.ts.map +1 -1
  191. package/lib/types/ink/render-node-to-output.js +144 -25
  192. package/lib/types/ink/selection.d.ts +29 -4
  193. package/lib/types/ink/selection.d.ts.map +1 -1
  194. package/lib/types/ink/selection.js +41 -5
  195. package/lib/types/ink/timeline-rail.d.ts +138 -0
  196. package/lib/types/ink/timeline-rail.d.ts.map +1 -0
  197. package/lib/types/ink/timeline-rail.js +179 -0
  198. package/lib/types/ink/wrap-text.d.ts.map +1 -1
  199. package/lib/types/ink/wrap-text.js +47 -5
  200. package/lib/types/screens/Chat.d.ts.map +1 -1
  201. package/lib/types/screens/Chat.js +819 -337
  202. package/lib/types/screens/SessionBrowser.d.ts.map +1 -1
  203. package/lib/types/screens/SessionBrowser.js +56 -29
  204. package/lib/types/screens/Settings.d.ts.map +1 -1
  205. package/lib/types/screens/Settings.js +64 -22
  206. package/lib/types/screens/StatusLine.d.ts +2 -1
  207. package/lib/types/screens/StatusLine.d.ts.map +1 -1
  208. package/lib/types/screens/StatusLine.js +17 -1
  209. package/lib/types/screens/TrajectoryScene.d.ts.map +1 -1
  210. package/lib/types/screens/TrajectoryScene.js +81 -14
  211. package/lib/types/screens/chatOverlay.d.ts +221 -0
  212. package/lib/types/screens/chatOverlay.d.ts.map +1 -0
  213. package/lib/types/screens/chatOverlay.js +114 -0
  214. package/lib/types/tips.d.ts.map +1 -1
  215. package/lib/types/tips.js +64 -10
  216. package/lib/types/tuiDisplayPrefs.d.ts +15 -0
  217. package/lib/types/tuiDisplayPrefs.d.ts.map +1 -1
  218. package/lib/types/tuiDisplayPrefs.js +9 -0
  219. package/lib/types/utils/debug.d.ts +10 -0
  220. package/lib/types/utils/debug.d.ts.map +1 -1
  221. package/lib/types/utils/debug.js +24 -0
  222. package/lib/types/utils/fileSuggestions.d.ts +19 -0
  223. package/lib/types/utils/fileSuggestions.d.ts.map +1 -0
  224. package/lib/types/utils/fileSuggestions.js +40 -0
  225. package/lib/types/utils/mentions.d.ts.map +1 -1
  226. package/lib/types/utils/mentions.js +3 -1
  227. package/lib/types/utils/paths.d.ts.map +1 -1
  228. package/lib/types/utils/paths.js +1 -0
  229. package/package.json +69 -66
@@ -23,9 +23,10 @@ import { clearResumeTarget, writeResumeTarget } from '../sessionHistory.js';
23
23
  import { resolveSessionCwd } from '../utils/workspaceRoot.js';
24
24
  import { checkForTuiUpdate, installedTuiVersion, isBootDeadlockTarget, isVersionNewer, resolveDshProfileName, resolveTuiUpdateTarget, updateTuiAndRestart } from '../update.js';
25
25
  import { getLang, isLang, resolveStartupLang, setLang, t, writeLangPref } from '../i18n.js';
26
- import { DEFAULT_STATUS_BAR, normalizeStatusBar, normalizeToolBackground } from '../tuiDisplayPrefs.js';
26
+ import { DEFAULT_STATUS_BAR, normalizeScrollGutter, normalizeStatusBar, normalizeToolBackground } from '../tuiDisplayPrefs.js';
27
27
  import { detectLegacyEnv, migrateLegacyDataDir, RENAMED_ENV } from '../utils/paths.js';
28
28
  import { attachHerdrIntegration } from '../herdr.js';
29
+ import { logMouseDebug } from '../utils/debug.js';
29
30
  import { Chat } from '../screens/Chat.js';
30
31
  import { getHostDialogStore } from './dialogs.js';
31
32
  import { getHostStatusStore } from './status.js';
@@ -48,6 +49,19 @@ import { CLEAR_ITERM2_PROGRESS, CLEAR_TAB_STATUS, supportsTabStatus, wrapForMult
48
49
  * `dsh-jsonrpc`: the surrounding `cordis.yml` supplies the agent spine, the
49
50
  * LLM adapter, and the tool plugins.
50
51
  */
52
+ /**
53
+ * Fullscreen decision latched across host recomposes. The launcher disposes
54
+ * and re-mounts the plugin tree (teardown → apply() runs again) — a fresh
55
+ * apply() re-resolves `bootedFullscreen` from cordis config, and the
56
+ * settings user layer (settings.yaml) can arrive after the 300ms
57
+ * `settingsReady` bound when the recompose is also re-mounting the settings
58
+ * service. The tree would then mount INLINE and `fullscreenFrozen` would
59
+ * swallow the late application — the app lands on the main screen
60
+ * ("exited fullscreen", dead mouse, unpinned input) until restart. A
61
+ * session that already mounted fullscreen must never regress on a
62
+ * recompose: latch the decision.
63
+ */
64
+ let lastBootedFullscreen;
51
65
  export async function apply(ctx, config) {
52
66
  if (!process.stdout.isTTY) {
53
67
  throw new Error('dsh-tui requires an interactive terminal (stdout must be a TTY).');
@@ -329,9 +343,31 @@ export async function apply(ctx, config) {
329
343
  diffLayout: config.diffLayout,
330
344
  thinkingFold: config.thinkingFold,
331
345
  toolBackground: config.toolBackground,
346
+ scrollGutter: config.scrollGutter,
332
347
  statusBar: config.statusBar,
333
348
  handle,
334
349
  });
350
+ // Fullscreen layout decision: the settings user layer (edited through the
351
+ // /settings screen) overrides cordis.yml when set. The settings injection
352
+ // below resolves it synchronously when the host settings service is up —
353
+ // i.e. before the tree mounts. `fullscreenFrozen` latches at mount: the
354
+ // exit funnel and the AlternateScreen wrap must keep reading the mode this
355
+ // session ACTUALLY runs, never a mid-session edit meant for the next boot
356
+ // (swapping layouts requires re-mounting the whole tree).
357
+ let bootedFullscreen = config.fullscreen === true;
358
+ let fullscreenFrozen = false;
359
+ // The settings service may come up AFTER this plugin's apply: the cordis
360
+ // inject callback defers until the service registers, so the first
361
+ // `apply(scope.get())` below can land after the mount (field report: the
362
+ // /settings fullscreen toggle never took effect — the frozen latch below
363
+ // swallowed the late callback and bootedFullscreen stayed false). The
364
+ // mount must therefore WAIT for the first settings application (bounded —
365
+ // a bare embedder without a settings service must not deadlock).
366
+ let resolveSettingsReady;
367
+ const settingsReady = new Promise(resolve => {
368
+ resolveSettingsReady = () => resolve();
369
+ setTimeout(resolve, 300);
370
+ });
335
371
  // Register the dsh-tui settings namespace so the /settings screen can
336
372
  // edit it (the section below was '命名空间未注册' without this): the
337
373
  // user layer in settings.yaml wins over cordis.yml's diffLayout, and
@@ -341,6 +377,7 @@ export async function apply(ctx, config) {
341
377
  diffLayout: Schema.union(['auto', 'split', 'unified']).default('auto'),
342
378
  thinkingFold: Schema.union(['preview', 'full']).default('preview'),
343
379
  toolBackground: Schema.union(['none', 'subtle', 'strong']).default('none'),
380
+ scrollGutter: Schema.union(['timeline', 'scrollbar', 'hidden']).default('timeline'),
344
381
  statusBar: Schema.object({
345
382
  compact: Schema.boolean().default(DEFAULT_STATUS_BAR.compact),
346
383
  model: Schema.boolean().default(DEFAULT_STATUS_BAR.model),
@@ -352,6 +389,8 @@ export async function apply(ctx, config) {
352
389
  tps: Schema.boolean().default(DEFAULT_STATUS_BAR.tps),
353
390
  gitBranch: Schema.boolean().default(DEFAULT_STATUS_BAR.gitBranch),
354
391
  sessionTitle: Schema.boolean().default(DEFAULT_STATUS_BAR.sessionTitle),
392
+ sessionId: Schema.boolean().default(DEFAULT_STATUS_BAR.sessionId),
393
+ goal: Schema.boolean().default(DEFAULT_STATUS_BAR.goal),
355
394
  mode: Schema.boolean().default(DEFAULT_STATUS_BAR.mode),
356
395
  contextBar: Schema.boolean().default(DEFAULT_STATUS_BAR.contextBar),
357
396
  activity: Schema.boolean().default(DEFAULT_STATUS_BAR.activity),
@@ -367,6 +406,9 @@ export async function apply(ctx, config) {
367
406
  // the effective language (see the section's format below) and lets
368
407
  // cordis.yml / lang.json keep their precedence.
369
408
  lang: Schema.union(['zh', 'en']),
409
+ // Same no-default rule: unset keeps cordis.yml's `fullscreen`
410
+ // decisive; set overrides it from the next boot on.
411
+ fullscreen: Schema.boolean(),
370
412
  }));
371
413
  const applyLayout = (value) => {
372
414
  channel.setDiffLayout(value.diffLayout ?? config.diffLayout ?? 'auto');
@@ -377,6 +419,15 @@ export async function apply(ctx, config) {
377
419
  const applyMinimal = (value) => {
378
420
  channel.setMinimal(value.minimal ?? false);
379
421
  };
422
+ // Fullscreen: only meaningful before the tree mounts (the freeze latch
423
+ // above). A later doc change (mid-session /settings edit) is persisted
424
+ // by the service and picked up on the next boot; the watch below says
425
+ // so with a notify.
426
+ const applyFullscreen = (value) => {
427
+ if (!fullscreenFrozen && typeof value.fullscreen === 'boolean') {
428
+ bootedFullscreen = value.fullscreen;
429
+ }
430
+ };
380
431
  // The /settings language field writes `lang` through the settings
381
432
  // service (user layer): apply it live and mirror it to lang.json so
382
433
  // the /lang command and next-boot resolution agree. DSH_TUI_LANG
@@ -393,6 +444,7 @@ export async function apply(ctx, config) {
393
444
  const applyDisplay = (value) => {
394
445
  channel.setThinkingFold(value.thinkingFold ?? config.thinkingFold ?? 'preview');
395
446
  channel.setToolBackground(normalizeToolBackground(value.toolBackground ?? config.toolBackground));
447
+ channel.setScrollGutter(normalizeScrollGutter(value.scrollGutter ?? config.scrollGutter));
396
448
  channel.setStatusBar(normalizeStatusBar(value.statusBar ?? config.statusBar));
397
449
  };
398
450
  const apply = (next) => {
@@ -401,11 +453,16 @@ export async function apply(ctx, config) {
401
453
  applyMinimal(next);
402
454
  applyLang(next);
403
455
  applyDisplay(next);
456
+ applyFullscreen(next);
404
457
  };
405
458
  apply(scope.get());
406
459
  scope.watch(next => {
407
460
  apply(next);
461
+ if (typeof next.fullscreen === 'boolean' && next.fullscreen !== bootedFullscreen) {
462
+ channel.notify(t('settings-fullscreen-restart'), { color: 'warning' });
463
+ }
408
464
  });
465
+ resolveSettingsReady?.();
409
466
  });
410
467
  // The /settings screen's own section: the dsh-tui namespace comes from
411
468
  // the settings registration above, and the declared selects write `lang`
@@ -436,6 +493,22 @@ export async function apply(ctx, config) {
436
493
  return value === undefined || value === null ? getLang() : String(value);
437
494
  },
438
495
  },
496
+ {
497
+ path: ['fullscreen'],
498
+ label: 'Fullscreen mode',
499
+ descriptions: { zh: '全屏模式' },
500
+ // 故意不用 "alt-screen" 这类终端术语:读者要的是行为差异。鼠标
501
+ // 两种模式都可用(整屏页面自带鼠标跟踪),别让描述暗示关掉就
502
+ // 没有鼠标——最常见的误解。长度对齐既有最长 hint(单行假设)。
503
+ hint: 'On: app takes the whole screen (vim/less style), in-app mouse. Off: native scrollback; full-page screens keep the mouse. Restart to apply.',
504
+ hintDescriptions: { zh: '开启:接管整个终端(同 vim/less),应用内鼠标;关闭:终端原生滚动选择;整屏页两种模式都有鼠标。重启生效。' },
505
+ kind: 'boolean',
506
+ format(value) {
507
+ // Unset in settings.yaml: show what THIS session booted with
508
+ // (the cordis.yml resolution) instead of a misleading false.
509
+ return value === undefined || value === null ? String(bootedFullscreen) : String(value);
510
+ },
511
+ },
439
512
  {
440
513
  path: ['diffLayout'],
441
514
  label: 'Diff layout',
@@ -474,6 +547,19 @@ export async function apply(ctx, config) {
474
547
  { value: 'strong', label: 'Strong', descriptions: { zh: '明显' } },
475
548
  ],
476
549
  },
550
+ {
551
+ path: ['scrollGutter'],
552
+ label: 'Transcript gutter',
553
+ descriptions: { zh: '转录边栏' },
554
+ hint: 'Right gutter of the fullscreen transcript: per-turn timeline ticks, a proportional scrollbar, or nothing.',
555
+ hintDescriptions: { zh: '全屏转录区右侧边栏:按轮次的时间线节点、比例滚动条,或留空。' },
556
+ kind: 'select',
557
+ options: [
558
+ { value: 'timeline', label: 'Turn timeline', descriptions: { zh: '轮次时间线' } },
559
+ { value: 'scrollbar', label: 'Scrollbar', descriptions: { zh: '滚动条' } },
560
+ { value: 'hidden', label: 'Hidden', descriptions: { zh: '隐藏' } },
561
+ ],
562
+ },
477
563
  {
478
564
  path: ['statusBar', 'compact'],
479
565
  label: 'Compact status bar',
@@ -564,6 +650,24 @@ export async function apply(ctx, config) {
564
650
  group: 'status-bar',
565
651
  kind: 'boolean',
566
652
  },
653
+ {
654
+ path: ['statusBar', 'sessionId'],
655
+ label: 'Show session id',
656
+ descriptions: { zh: '显示会话 ID' },
657
+ hint: 'Show the short session id (# + first 8 chars) — it matches the session log filename for --resume.',
658
+ hintDescriptions: { zh: '显示短会话 ID(# + 前 8 位)——与日志文件名对应,方便 --resume 定位。' },
659
+ group: 'status-bar',
660
+ kind: 'boolean',
661
+ },
662
+ {
663
+ path: ['statusBar', 'goal'],
664
+ label: 'Show goal status',
665
+ descriptions: { zh: '显示 Goal 状态' },
666
+ hint: 'Show a compact goal chip (phase glyph + rounds) in the status footer while a goal exists.',
667
+ hintDescriptions: { zh: '存在 Goal 时,在底部状态栏显示紧凑的 Goal 状态(阶段符号与轮次)。' },
668
+ group: 'status-bar',
669
+ kind: 'boolean',
670
+ },
567
671
  {
568
672
  path: ['statusBar', 'mode'],
569
673
  label: 'Show session mode',
@@ -695,7 +799,7 @@ export async function apply(ctx, config) {
695
799
  if (error !== undefined) {
696
800
  const message = error instanceof Error ? error.message : String(error);
697
801
  ctx.logger.error(`dsh-tui: exit after error: ${message}`);
698
- void finishExit(ctx, instance, config.fullscreen === true, undefined, `dsh-tui crashed: ${message}`, () => disposeRootAndExit(ctx, 1));
802
+ void finishExit(ctx, instance, bootedFullscreen, undefined, `dsh-tui crashed: ${message}`, () => disposeRootAndExit(ctx, 1));
699
803
  return;
700
804
  }
701
805
  if (updateRequested) {
@@ -705,7 +809,7 @@ export async function apply(ctx, config) {
705
809
  catch {
706
810
  // Resume persistence is best effort and must never block an update.
707
811
  }
708
- void finishExit(ctx, instance, config.fullscreen === true, 'Updating @deepseek-harness-tui/dsh-tui and restarting…', undefined, () => runUpdate(ctx, profile, channel.agentId, updateTargetVersion));
812
+ void finishExit(ctx, instance, bootedFullscreen, 'Updating @deepseek-harness-tui/dsh-tui and restarting…', undefined, () => runUpdate(ctx, profile, channel.agentId, updateTargetVersion));
709
813
  return;
710
814
  }
711
815
  // Judge against the live session behind the channel (channel.agentId),
@@ -729,10 +833,19 @@ export async function apply(ctx, config) {
729
833
  const hint = resumable
730
834
  ? `Resume with the command below:\n${resumeCommand(profile, channel.agentId)}`
731
835
  : undefined;
732
- void finishExit(ctx, instance, config.fullscreen === true, hint, undefined, () => disposeRootAndExit(ctx, 0));
836
+ void finishExit(ctx, instance, bootedFullscreen, hint, undefined, () => disposeRootAndExit(ctx, 0));
733
837
  },
734
838
  });
735
839
  const handleExit = funnel.handleExit;
840
+ // Chat's `fullscreen` prop must match the root wrap below, or the
841
+ // full-screen surfaces inside Chat (session browser, settings, trajectory,
842
+ // subagent pages) would nest a SECOND <AlternateScreen> — whose unmount
843
+ // writes DEC 1049 exit and drops the whole app back to the main screen
844
+ // (the stable /resume→Esc "exited fullscreen" repro). The prop is captured
845
+ // when the element is created, so wait for the settings first-application
846
+ // BEFORE creating Chat: the element must see the same bootedFullscreen the
847
+ // root tree resolves after settingsReady below.
848
+ await settingsReady;
736
849
  const chat = React.createElement(Chat, {
737
850
  channel,
738
851
  questionStore,
@@ -746,7 +859,7 @@ export async function apply(ctx, config) {
746
859
  // Full-screen surfaces inside Chat — the trajectory scene and the session
747
860
  // browser — enter the alt screen themselves in inline mode; in fullscreen
748
861
  // the tree is already wrapped below, so they must not nest.
749
- fullscreen: config.fullscreen === true,
862
+ fullscreen: bootedFullscreen,
750
863
  onExit: () => handleExit(),
751
864
  // Only a `dsh --profile <name>` launch has a profile installation for
752
865
  // `/update` to act on; source checkouts and `--config` overlays get the
@@ -791,12 +904,28 @@ export async function apply(ctx, config) {
791
904
  });
792
905
  },
793
906
  });
907
+ // Freeze the fullscreen decision only NOW, right before the tree mounts:
908
+ // Chat above was created after the same settingsReady await, so the root
909
+ // wrap and the `fullscreen` prop share one value; a mid-session /settings
910
+ // edit from here on is persisted for the next boot (the watch notifies),
911
+ // never applied live (swapping layouts requires re-mounting the tree).
912
+ // Host recompose hardening: never regress a fullscreen session to inline
913
+ // on a re-mount whose settings application arrived late (see the module
914
+ // latch note). A fresh process still resolves from config + settings
915
+ // normally — the latch is undefined there.
916
+ if (bootedFullscreen === false && lastBootedFullscreen === true) {
917
+ bootedFullscreen = true;
918
+ }
919
+ fullscreenFrozen = true;
794
920
  // fullscreen: wrap the tree in <AlternateScreen> (DEC 1049 + SGR mouse
795
921
  // tracking), which turns on in-app text selection (copy-on-select via
796
922
  // useCopyOnSelect), wheel scroll, and click/hover hit-testing. Inline
797
923
  // mode leaves the mouse to the terminal emulator's native selection.
798
- const tree = React.createElement(ThemeProvider, null, config.fullscreen ? React.createElement(AlternateScreen, null, chat) : chat);
924
+ const tree = React.createElement(ThemeProvider, null, bootedFullscreen ? React.createElement(AlternateScreen, null, chat) : chat);
799
925
  instance = await render(tree, { exitOnCtrlC: false });
926
+ const isRecompose = lastBootedFullscreen !== undefined;
927
+ lastBootedFullscreen = bootedFullscreen;
928
+ logMouseDebug('apply mount', { bootedFullscreen, isRecompose });
800
929
  // Check in the background so registry latency never delays the first frame.
801
930
  // A failed/offline check is intentionally silent; the manual `/update`
802
931
  // command remains available regardless of network access.
@@ -814,6 +943,7 @@ export async function apply(ctx, config) {
814
943
  // skill commands (issue #86) would survive this exact recompose and the
815
944
  // re-mounted channel would find the names taken, freezing its menu.
816
945
  ctx.effect(() => () => {
946
+ logMouseDebug('apply teardown');
817
947
  funnel.markTeardown();
818
948
  channel.releaseContributions();
819
949
  instance?.unmount();
@@ -363,6 +363,14 @@ declare const dict: {
363
363
  readonly zh: "模型已切换,但工作区挂载失败 · {{err}}";
364
364
  readonly en: "Model switched, but workspace attachment failed · {{err}}";
365
365
  };
366
+ readonly 'model-usage': {
367
+ readonly zh: "用法:/model <provider/model>(如 deepseek/deepseek-v4-flash)";
368
+ readonly en: "Usage: /model <provider/model> (e.g. deepseek/deepseek-v4-flash)";
369
+ };
370
+ readonly 'model-unknown': {
371
+ readonly zh: "未知模型「{{spec}}」· /model 查看全部";
372
+ readonly en: "Unknown model \"{{spec}}\" · /model to view all";
373
+ };
366
374
  readonly 'compact-unavailable': {
367
375
  readonly zh: "压缩不可用——当前 leaf 没有压缩服务";
368
376
  readonly en: "Compaction unavailable · no compaction service in this leaf";
@@ -643,6 +651,22 @@ declare const dict: {
643
651
  readonly zh: "用法:/workspace resume | rename <名称> | open <路径或 URI>{{commands}}";
644
652
  readonly en: "Usage: /workspace resume | rename <name> | open <path-or-URI>{{commands}}";
645
653
  };
654
+ readonly 'workspace-menu-title': {
655
+ readonly zh: "Workspace 操作";
656
+ readonly en: "Workspace actions";
657
+ };
658
+ readonly 'workspace-menu-resume-desc': {
659
+ readonly zh: "切换到另一个工作区";
660
+ readonly en: "Switch to another workspace";
661
+ };
662
+ readonly 'workspace-menu-rename-desc': {
663
+ readonly zh: "重命名当前工作区(需输入名称)";
664
+ readonly en: "Rename the current workspace (needs a name)";
665
+ };
666
+ readonly 'workspace-menu-open-desc': {
667
+ readonly zh: "打开路径或工作区 URI(需输入路径)";
668
+ readonly en: "Open a path or workspace URI (needs a path)";
669
+ };
646
670
  readonly 'workspace-open-usage': {
647
671
  readonly zh: "用法:/workspace open <路径或 URI>";
648
672
  readonly en: "Usage: /workspace open <path-or-URI>";
@@ -775,26 +799,74 @@ declare const dict: {
775
799
  readonly zh: "使用 /provider 管理 DSH 凭据;若来源为 env,请删除对应环境变量并重启 dsh-tui";
776
800
  readonly en: "Manage DSH credentials with /provider; for env sources, remove the corresponding environment variable and restart dsh-tui";
777
801
  };
778
- readonly 'permissions-policy-hint': {
802
+ readonly 'permission-policy-hint': {
779
803
  readonly zh: "DSH 权限策略由 fs-policy / bash-sandbox 配置决定(当前 leaf:workspace 内读写、写入需已读文件)。";
780
804
  readonly en: "DSH permission policy is set by fs-policy / bash-sandbox config (current leaf: read/write in workspace, writes need a prior read).";
781
805
  };
782
- readonly 'permissions-approval-hint': {
806
+ readonly 'permission-approval-hint': {
783
807
  readonly zh: "审批通道已挂载:命令申请权限提升(sandbox_permissions)时弹出审批条,Yes 放行一次、No / Esc 拒绝。";
784
808
  readonly en: "The approval channel is mounted: sandbox escalations (sandbox_permissions) raise an approval bar — Yes allows once, No / Esc rejects.";
785
809
  };
786
- readonly 'permissions-preset-hint': {
787
- readonly zh: "/permission 可查看与切换权限预设(read-only / workspace-write / danger-full-access)。";
788
- readonly en: "/permission shows and switches permission presets (read-only / workspace-write / danger-full-access).";
789
- };
790
- readonly 'permissions-root-hint': {
810
+ readonly 'permission-root-hint': {
791
811
  readonly zh: "当前文件系统策略以工作目录为根:{{cwd}}";
792
812
  readonly en: "Current filesystem policy is rooted at the working directory: {{cwd}}";
793
813
  };
794
- readonly 'permissions-path-hint': {
814
+ readonly 'permission-path-hint': {
795
815
  readonly zh: "模型工具相对路径均解析自该目录;跨目录访问由 fs-policy 拦截。";
796
816
  readonly en: "Relative paths of model tools resolve from this directory; cross-directory access is blocked by fs-policy.";
797
817
  };
818
+ readonly 'permission-current': {
819
+ readonly zh: "当前预设 {{name}}";
820
+ readonly en: "Current preset {{name}}";
821
+ };
822
+ readonly 'permission-picker-title': {
823
+ readonly zh: "权限预设";
824
+ readonly en: "Permission preset";
825
+ };
826
+ readonly 'permission-preset-readonly': {
827
+ readonly zh: "只读";
828
+ readonly en: "Read-only";
829
+ };
830
+ readonly 'permission-preset-readonly-desc': {
831
+ readonly zh: "会话只读:不写文件、不执行命令";
832
+ readonly en: "Read-only session: no file writes, no commands";
833
+ };
834
+ readonly 'permission-preset-workspace-write': {
835
+ readonly zh: "工作区读写";
836
+ readonly en: "Workspace read/write";
837
+ };
838
+ readonly 'permission-preset-workspace-write-desc': {
839
+ readonly zh: "工作区内读写;写入需先读该文件";
840
+ readonly en: "Read/write inside the workspace; writes need a prior read";
841
+ };
842
+ readonly 'permission-preset-full-access': {
843
+ readonly zh: "完全访问";
844
+ readonly en: "Full access";
845
+ };
846
+ readonly 'permission-preset-full-access-desc': {
847
+ readonly zh: "不受限读写,无需审批";
848
+ readonly en: "Unrestricted access, no approvals";
849
+ };
850
+ readonly 'plan-picker-title': {
851
+ readonly zh: "计划模式";
852
+ readonly en: "Plan mode";
853
+ };
854
+ readonly 'plan-mode-on': {
855
+ readonly zh: "开启";
856
+ readonly en: "On";
857
+ };
858
+ readonly 'plan-mode-on-desc': {
859
+ readonly zh: "进入计划模式:只读,先规划后动手";
860
+ readonly en: "Enter plan mode: read-only, plan before acting";
861
+ };
862
+ readonly 'plan-mode-off': {
863
+ readonly zh: "关闭";
864
+ readonly en: "Off";
865
+ };
866
+ readonly 'plan-mode-off-desc': {
867
+ readonly zh: "退出计划模式,恢复正常执行";
868
+ readonly en: "Exit plan mode, back to normal execution";
869
+ };
798
870
  readonly 'hooks-not-mounted': {
799
871
  readonly zh: "DSH hooks(dsh-hooks-claude / dsh-hooks-codex)未在本 leaf 挂载。";
800
872
  readonly en: "DSH hooks (dsh-hooks-claude / dsh-hooks-codex) are not mounted in this leaf.";
@@ -1023,6 +1095,22 @@ declare const dict: {
1023
1095
  readonly zh: "更多技巧";
1024
1096
  readonly en: "more tips";
1025
1097
  };
1098
+ readonly 'logo-drift-newer': {
1099
+ readonly zh: "dsh 引擎为 {{installed}},比本界面验证过的 {{validated}} 新,可能出现兼容问题;求稳可执行 npm i -g @deepseek-ai/dsh@{{primary}} 降级,或等待 dsh-tui 适配新版。";
1100
+ readonly en: "The dsh engine ({{installed}}) is newer than the {{validated}} this UI is validated against, so issues are possible; downgrade via npm i -g @deepseek-ai/dsh@{{primary}} for stability, or wait for a dsh-tui update.";
1101
+ };
1102
+ readonly 'logo-drift-older': {
1103
+ readonly zh: "dsh 引擎为 {{installed}},低于本界面验证过的 {{validated}},部分功能可能不可用;建议执行 npm i -g @deepseek-ai/dsh@{{primary}} 升级。";
1104
+ readonly en: "The dsh engine ({{installed}}) is older than the {{validated}} this UI is validated against; some features may be missing. Upgrade via npm i -g @deepseek-ai/dsh@{{primary}}.";
1105
+ };
1106
+ readonly 'logo-drift-mixed': {
1107
+ readonly zh: "检测到 dsh 引擎多版本混装({{installed}}),容易出现奇怪问题;建议执行 npm i -g @deepseek-ai/dsh@{{primary}} 统一版本。";
1108
+ readonly en: "Mixed dsh engine versions detected ({{installed}}), which can cause odd behavior; unify them via npm i -g @deepseek-ai/dsh@{{primary}}.";
1109
+ };
1110
+ readonly 'logo-drift-broken': {
1111
+ readonly zh: "dsh 引擎版本异常({{installed}}),本界面验证过 {{validated}};建议执行 npm i -g @deepseek-ai/dsh@{{primary}} 重装。";
1112
+ readonly en: "Unexpected dsh engine versions ({{installed}}); this UI is validated against {{validated}}. Reinstall via npm i -g @deepseek-ai/dsh@{{primary}}.";
1113
+ };
1026
1114
  readonly 'input-sent-after-turn': {
1027
1115
  readonly zh: "已发送,当前回合结束后处理";
1028
1116
  readonly en: "Sent, processed after the current turn";
@@ -1143,6 +1231,66 @@ declare const dict: {
1143
1231
  readonly zh: "摆尾巴3";
1144
1232
  readonly en: "tail3";
1145
1233
  };
1234
+ readonly 'sugg-commands-title': {
1235
+ readonly zh: "命令";
1236
+ readonly en: "commands";
1237
+ };
1238
+ readonly 'sugg-files-title': {
1239
+ readonly zh: "文件";
1240
+ readonly en: "files";
1241
+ };
1242
+ readonly 'sugg-count': {
1243
+ readonly zh: "共 {{n}} 项";
1244
+ readonly en: "{{n}} items";
1245
+ };
1246
+ readonly 'sugg-more-above': {
1247
+ readonly zh: "↑{{n}}";
1248
+ readonly en: "↑{{n}}";
1249
+ };
1250
+ readonly 'sugg-more-below': {
1251
+ readonly zh: "↓{{n}}";
1252
+ readonly en: "↓{{n}}";
1253
+ };
1254
+ readonly 'sugg-status-desc': {
1255
+ readonly zh: "显示当前选择";
1256
+ readonly en: "Show the current choice";
1257
+ };
1258
+ readonly 'sugg-lang-zh-desc': {
1259
+ readonly zh: "切换界面语言到中文";
1260
+ readonly en: "Switch the UI language to Chinese";
1261
+ };
1262
+ readonly 'sugg-lang-en-desc': {
1263
+ readonly zh: "切换界面语言到英文";
1264
+ readonly en: "Switch the UI language to English";
1265
+ };
1266
+ readonly 'sugg-theme-auto-desc': {
1267
+ readonly zh: "跟随终端背景自动切换";
1268
+ readonly en: "Follow the terminal background";
1269
+ };
1270
+ readonly 'sugg-theme-builtin-desc': {
1271
+ readonly zh: "内置主题";
1272
+ readonly en: "Built-in theme";
1273
+ };
1274
+ readonly 'sugg-theme-user-desc': {
1275
+ readonly zh: "用户主题({{base}} 基底)";
1276
+ readonly en: "User theme ({{base}} base)";
1277
+ };
1278
+ readonly 'sugg-effort-level-desc': {
1279
+ readonly zh: "思考强度档位";
1280
+ readonly en: "Reasoning effort level";
1281
+ };
1282
+ readonly 'sugg-activity-frames-desc': {
1283
+ readonly zh: "列出或切换动画帧预设";
1284
+ readonly en: "List or switch frame presets";
1285
+ };
1286
+ readonly 'sugg-activity-frame-desc': {
1287
+ readonly zh: "动画帧预设";
1288
+ readonly en: "Animation frame preset";
1289
+ };
1290
+ readonly 'settings-fullscreen-restart': {
1291
+ readonly zh: "全屏设置已保存,重启 dsh-tui 后生效";
1292
+ readonly en: "Fullscreen preference saved — restart dsh-tui to apply";
1293
+ };
1146
1294
  readonly 'help-for-commands': {
1147
1295
  readonly zh: "/ 查看命令";
1148
1296
  readonly en: "/ for commands";
@@ -1203,6 +1351,14 @@ declare const dict: {
1203
1351
  readonly zh: "ctrl+g 打开编辑器";
1204
1352
  readonly en: "ctrl+g to open editor";
1205
1353
  };
1354
+ readonly 'help-fold-todos': {
1355
+ readonly zh: "{{mod}}q 折叠待办";
1356
+ readonly en: "{{mod}}q to fold todos";
1357
+ };
1358
+ readonly 'goal-todo-fold-hint': {
1359
+ readonly zh: "{{mod}}q 折叠";
1360
+ readonly en: "{{mod}}q to fold";
1361
+ };
1206
1362
  readonly 'help-commands-title': {
1207
1363
  readonly zh: "命令:";
1208
1364
  readonly en: "commands:";
@@ -1551,6 +1707,14 @@ declare const dict: {
1551
1707
  readonly zh: "技能列表加载失败";
1552
1708
  readonly en: "Failed to load the skill list";
1553
1709
  };
1710
+ readonly 'skills-unknown': {
1711
+ readonly zh: "未知技能「{{name}}」";
1712
+ readonly en: "Unknown skill \"{{name}}\"";
1713
+ };
1714
+ readonly 'skills-not-invocable': {
1715
+ readonly zh: "技能「{{name}}」不可直接调用";
1716
+ readonly en: "Skill \"{{name}}\" is not directly invocable";
1717
+ };
1554
1718
  readonly 'plugin-scene-crashed': {
1555
1719
  readonly zh: "插件场景「{{id}}」渲染崩溃:{{err}}(已自动关闭)";
1556
1720
  readonly en: "Plugin scene \"{{id}}\" crashed while rendering: {{err}} (closed)";
@@ -1848,12 +2012,16 @@ declare const dict: {
1848
2012
  readonly en: "Summary folded";
1849
2013
  };
1850
2014
  readonly 'new-message': {
1851
- readonly zh: "{{n}} 条新消息";
1852
- readonly en: "1 new message";
2015
+ readonly zh: "↓ {{n}} 条新消息";
2016
+ readonly en: "↓ 1 new message";
1853
2017
  };
1854
2018
  readonly 'new-messages': {
1855
- readonly zh: "{{n}} 条新消息";
1856
- readonly en: "{{n}} new messages";
2019
+ readonly zh: "↓ {{n}} 条新消息";
2020
+ readonly en: "↓ {{n}} new messages";
2021
+ };
2022
+ readonly 'back-to-bottom': {
2023
+ readonly zh: "↓ 回到底部(Enter/End)";
2024
+ readonly en: "↓ back to bottom (Enter/End)";
1857
2025
  };
1858
2026
  readonly 'theme-builtin-base': {
1859
2027
  readonly zh: "内置 · {{name}} 基底";
@@ -2368,9 +2536,6 @@ declare const dict: {
2368
2536
  readonly 'cmd-desc-logout': {
2369
2537
  readonly zh: "清除 API 凭证";
2370
2538
  };
2371
- readonly 'cmd-desc-permissions': {
2372
- readonly zh: "查看权限策略状态";
2373
- };
2374
2539
  readonly 'cmd-desc-add-dir': {
2375
2540
  readonly zh: "查看文件系统策略范围";
2376
2541
  };
@@ -2473,6 +2638,18 @@ declare const dict: {
2473
2638
  readonly zh: "语言「{{lang}}」切换失败(无法写入 ~/.dsh-tui/lang.json)";
2474
2639
  readonly en: "Language \"{{lang}}\" switch failed (cannot write ~/.dsh-tui/lang.json)";
2475
2640
  };
2641
+ readonly 'lang-picker-title': {
2642
+ readonly zh: "界面语言";
2643
+ readonly en: "UI language";
2644
+ };
2645
+ readonly 'lang-zh-desc': {
2646
+ readonly zh: "简体中文(默认)";
2647
+ readonly en: "Simplified Chinese (default)";
2648
+ };
2649
+ readonly 'lang-en-desc': {
2650
+ readonly zh: "English(英文)";
2651
+ readonly en: "English";
2652
+ };
2476
2653
  readonly 'status-cache-label': {
2477
2654
  readonly zh: "缓存 ";
2478
2655
  readonly en: "cache ";
@@ -2654,8 +2831,9 @@ export declare function t(key: I18nKey, params?: I18nParams): string;
2654
2831
  * lives in `LOCAL_COMMANDS` / the DSH registry, the dict carries zh only).
2655
2832
  * @param key - Dictionary key, computed at runtime so it is not type-checked.
2656
2833
  * @param fallback - Text used when no translation exists.
2834
+ * @param params - Placeholder values substituted into whichever text wins.
2657
2835
  */
2658
- export declare function tOr(key: string, fallback: string): string;
2836
+ export declare function tOr(key: string, fallback: string, params?: I18nParams): string;
2659
2837
  /**
2660
2838
  * Parse a persisted `{ lang }` value; anything else yields undefined.
2661
2839
  * @param text - Raw file contents.
@@ -1 +1 @@
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;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA2xBA,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"}
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;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA01BA,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;;;;;;;;;GASG;AACH,wBAAgB,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,GAAE,UAAe,GAAG,MAAM,CAMlF;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"}