@deepseek-harness-tui/dsh-tui 0.8.1 → 0.8.3

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 (241) hide show
  1. package/README.md +30 -15
  2. package/bin/dsh-tui.js +43 -7
  3. package/dsh-ecosystem-spec/protocols/profile-definitions.d.ts +13 -0
  4. package/dsh-ecosystem-spec/protocols/profile-definitions.js +59 -0
  5. package/dsh-ecosystem-spec/protocols/tui-channel-http.d.ts +15 -0
  6. package/dsh-ecosystem-spec/protocols/tui-channel-http.js +32 -0
  7. package/dsh-ecosystem-spec/protocols/tui-channel.d.ts +59 -0
  8. package/dsh-ecosystem-spec/protocols/tui-channel.js +166 -0
  9. package/dsh-ecosystem-spec/protocols/tui-contributions.d.ts +42 -0
  10. package/dsh-ecosystem-spec/protocols/tui-contributions.js +143 -0
  11. package/dsh-ecosystem-spec/registry/README.md +14 -0
  12. package/dsh-ecosystem-spec/registry/contracts/scene-v1alpha1.json +13 -0
  13. package/dsh-ecosystem-spec/registry/contracts/settings-section-v1alpha1.json +13 -0
  14. package/dsh-ecosystem-spec/registry/contracts/tui-channel-v1alpha1.json +30 -0
  15. package/dsh-ecosystem-spec/registry/contracts/workspace-provider-v1alpha1.json +6 -0
  16. package/{ecosystem-spec → dsh-ecosystem-spec}/registry/permissions-0.1.json +1 -1
  17. package/dsh-ecosystem-spec/registry/registry-0.15.json +149 -0
  18. package/lib/types/command-trees.d.ts +2 -1
  19. package/lib/types/command-trees.d.ts.map +1 -1
  20. package/lib/types/command-trees.js +1 -1
  21. package/lib/types/components/LoadedContextPanel.d.ts +10 -5
  22. package/lib/types/components/LoadedContextPanel.d.ts.map +1 -1
  23. package/lib/types/components/LoadedContextPanel.js +18 -7
  24. package/lib/types/components/LogoV2.d.ts.map +1 -1
  25. package/lib/types/components/LogoV2.js +16 -7
  26. package/lib/types/components/Markdown.d.ts +13 -1
  27. package/lib/types/components/Markdown.d.ts.map +1 -1
  28. package/lib/types/components/Markdown.js +15 -1
  29. package/lib/types/components/MessageList.d.ts +3 -1
  30. package/lib/types/components/MessageList.d.ts.map +1 -1
  31. package/lib/types/components/MessageList.js +90 -9
  32. package/lib/types/components/PromptInput.d.ts.map +1 -1
  33. package/lib/types/components/PromptInput.js +26 -12
  34. package/lib/types/components/StreamingMarkdown.d.ts +2 -8
  35. package/lib/types/components/StreamingMarkdown.d.ts.map +1 -1
  36. package/lib/types/components/StreamingMarkdown.js +51 -9
  37. package/lib/types/components/activityFrames.d.ts +1 -1
  38. package/lib/types/components/activityFrames.d.ts.map +1 -1
  39. package/lib/types/components/activityFrames.js +4 -1
  40. package/lib/types/components/messages/AssistantThinkingMessage.d.ts +5 -1
  41. package/lib/types/components/messages/AssistantThinkingMessage.d.ts.map +1 -1
  42. package/lib/types/components/messages/AssistantThinkingMessage.js +26 -3
  43. package/lib/types/components/messages/AssistantToolUseMessage.d.ts.map +1 -1
  44. package/lib/types/components/messages/AssistantToolUseMessage.js +12 -1
  45. package/lib/types/dsh-adapter/channel.d.ts +31 -4
  46. package/lib/types/dsh-adapter/channel.d.ts.map +1 -1
  47. package/lib/types/dsh-adapter/channel.js +267 -102
  48. package/lib/types/dsh-adapter/childStderr.d.ts.map +1 -1
  49. package/lib/types/dsh-adapter/childStderr.js +10 -0
  50. package/lib/types/dsh-adapter/command-attribution.d.ts.map +1 -1
  51. package/lib/types/dsh-adapter/command-attribution.js +2 -2
  52. package/lib/types/dsh-adapter/command-trees.d.ts +7 -1
  53. package/lib/types/dsh-adapter/command-trees.d.ts.map +1 -1
  54. package/lib/types/dsh-adapter/command-trees.js +70 -20
  55. package/lib/types/dsh-adapter/component-identity.d.ts +5 -0
  56. package/lib/types/dsh-adapter/component-identity.d.ts.map +1 -1
  57. package/lib/types/dsh-adapter/component-identity.js +14 -14
  58. package/lib/types/dsh-adapter/decision-guard.d.ts.map +1 -1
  59. package/lib/types/dsh-adapter/decision-guard.js +10 -16
  60. package/lib/types/dsh-adapter/dialogs.d.ts +2 -3
  61. package/lib/types/dsh-adapter/dialogs.d.ts.map +1 -1
  62. package/lib/types/dsh-adapter/dialogs.js +114 -60
  63. package/lib/types/dsh-adapter/effect-ledger.d.ts +0 -8
  64. package/lib/types/dsh-adapter/effect-ledger.d.ts.map +1 -1
  65. package/lib/types/dsh-adapter/effect-ledger.js +73 -53
  66. package/lib/types/dsh-adapter/extension-events.d.ts.map +1 -1
  67. package/lib/types/dsh-adapter/extension-events.js +39 -1
  68. package/lib/types/dsh-adapter/grants.d.ts.map +1 -1
  69. package/lib/types/dsh-adapter/grants.js +43 -14
  70. package/lib/types/dsh-adapter/host-access.d.ts +37 -0
  71. package/lib/types/dsh-adapter/host-access.d.ts.map +1 -0
  72. package/lib/types/dsh-adapter/host-access.js +652 -0
  73. package/lib/types/dsh-adapter/host-descriptor.d.ts +2 -2
  74. package/lib/types/dsh-adapter/host-descriptor.d.ts.map +1 -1
  75. package/lib/types/dsh-adapter/host-descriptor.js +31 -8
  76. package/lib/types/dsh-adapter/index.d.ts +4 -0
  77. package/lib/types/dsh-adapter/index.d.ts.map +1 -1
  78. package/lib/types/dsh-adapter/index.js +1 -0
  79. package/lib/types/dsh-adapter/message-observer.d.ts +10 -15
  80. package/lib/types/dsh-adapter/message-observer.d.ts.map +1 -1
  81. package/lib/types/dsh-adapter/message-observer.js +114 -70
  82. package/lib/types/dsh-adapter/plugin-host.d.ts +39 -6
  83. package/lib/types/dsh-adapter/plugin-host.d.ts.map +1 -1
  84. package/lib/types/dsh-adapter/plugin-host.js +139 -44
  85. package/lib/types/dsh-adapter/plugin-storage.d.ts +0 -5
  86. package/lib/types/dsh-adapter/plugin-storage.d.ts.map +1 -1
  87. package/lib/types/dsh-adapter/plugin-storage.js +48 -36
  88. package/lib/types/dsh-adapter/plugin.d.ts.map +1 -1
  89. package/lib/types/dsh-adapter/plugin.js +97 -16
  90. package/lib/types/dsh-adapter/promptDebug.d.ts +0 -6
  91. package/lib/types/dsh-adapter/promptDebug.d.ts.map +1 -1
  92. package/lib/types/dsh-adapter/promptDebug.js +13 -4
  93. package/lib/types/dsh-adapter/renderers.d.ts +6 -14
  94. package/lib/types/dsh-adapter/renderers.d.ts.map +1 -1
  95. package/lib/types/dsh-adapter/renderers.js +81 -46
  96. package/lib/types/dsh-adapter/scenes.d.ts +8 -4
  97. package/lib/types/dsh-adapter/scenes.d.ts.map +1 -1
  98. package/lib/types/dsh-adapter/scenes.js +136 -37
  99. package/lib/types/dsh-adapter/settings-sections.d.ts +7 -3
  100. package/lib/types/dsh-adapter/settings-sections.d.ts.map +1 -1
  101. package/lib/types/dsh-adapter/settings-sections.js +106 -26
  102. package/lib/types/dsh-adapter/shortcuts.d.ts +8 -10
  103. package/lib/types/dsh-adapter/shortcuts.d.ts.map +1 -1
  104. package/lib/types/dsh-adapter/shortcuts.js +108 -36
  105. package/lib/types/dsh-adapter/status.d.ts +6 -7
  106. package/lib/types/dsh-adapter/status.d.ts.map +1 -1
  107. package/lib/types/dsh-adapter/status.js +88 -22
  108. package/lib/types/dsh-adapter/workspaces.d.ts +16 -5
  109. package/lib/types/dsh-adapter/workspaces.d.ts.map +1 -1
  110. package/lib/types/dsh-adapter/workspaces.js +287 -170
  111. package/lib/types/extensions.d.ts +10 -5
  112. package/lib/types/extensions.d.ts.map +1 -1
  113. package/lib/types/extensions.js +11 -5
  114. package/lib/types/force-production-react.d.ts +1 -0
  115. package/lib/types/force-production-react.d.ts.map +1 -0
  116. package/lib/types/force-production-react.js +12 -0
  117. package/lib/types/i18n.d.ts +22 -6
  118. package/lib/types/i18n.d.ts.map +1 -1
  119. package/lib/types/i18n.js +14 -2
  120. package/lib/types/index.d.ts +1 -0
  121. package/lib/types/index.d.ts.map +1 -1
  122. package/lib/types/index.js +1 -0
  123. package/lib/types/ink/components/App.d.ts +2 -0
  124. package/lib/types/ink/components/App.d.ts.map +1 -1
  125. package/lib/types/ink/components/App.js +4 -0
  126. package/lib/types/ink/ink.d.ts +20 -3
  127. package/lib/types/ink/ink.d.ts.map +1 -1
  128. package/lib/types/ink/ink.js +57 -5
  129. package/lib/types/ink/log-update.d.ts.map +1 -1
  130. package/lib/types/ink/log-update.js +128 -9
  131. package/lib/types/ink/output.d.ts +28 -2
  132. package/lib/types/ink/output.d.ts.map +1 -1
  133. package/lib/types/ink/output.js +158 -12
  134. package/lib/types/ink/screen.d.ts +39 -0
  135. package/lib/types/ink/screen.d.ts.map +1 -1
  136. package/lib/types/ink/screen.js +117 -0
  137. package/lib/types/ink/terminal.d.ts.map +1 -1
  138. package/lib/types/ink/terminal.js +10 -2
  139. package/lib/types/ink/wrapAnsi.d.ts +1 -1
  140. package/lib/types/ink/wrapAnsi.d.ts.map +1 -1
  141. package/lib/types/ink/wrapAnsi.js +29 -2
  142. package/lib/types/plugin-host.d.ts +13 -11
  143. package/lib/types/plugin-host.d.ts.map +1 -1
  144. package/lib/types/plugin-host.js +16 -16
  145. package/lib/types/plugin-spec/registry.d.ts +2 -2
  146. package/lib/types/plugin-spec/registry.d.ts.map +1 -1
  147. package/lib/types/plugin-spec/registry.js +113 -18
  148. package/lib/types/plugin-spec/tui-extension.d.ts.map +1 -1
  149. package/lib/types/plugin-spec/tui-extension.js +2 -0
  150. package/lib/types/plugin-spec/validate.d.ts.map +1 -1
  151. package/lib/types/plugin-spec/validate.js +20 -1
  152. package/lib/types/scenes.d.ts +2 -1
  153. package/lib/types/scenes.d.ts.map +1 -1
  154. package/lib/types/scenes.js +1 -1
  155. package/lib/types/screens/Chat.d.ts +3 -3
  156. package/lib/types/screens/Chat.d.ts.map +1 -1
  157. package/lib/types/screens/Chat.js +38 -17
  158. package/lib/types/screens/SessionBrowser.d.ts.map +1 -1
  159. package/lib/types/screens/SessionBrowser.js +10 -3
  160. package/lib/types/sessions/view.d.ts +3 -2
  161. package/lib/types/sessions/view.d.ts.map +1 -1
  162. package/lib/types/sessions/view.js +18 -0
  163. package/lib/types/settings-sections.d.ts +2 -1
  164. package/lib/types/settings-sections.d.ts.map +1 -1
  165. package/lib/types/settings-sections.js +1 -1
  166. package/lib/types/utils/execFileNoThrow.d.ts.map +1 -1
  167. package/lib/types/utils/execFileNoThrow.js +3 -0
  168. package/lib/types/workspaces.d.ts +2 -1
  169. package/lib/types/workspaces.d.ts.map +1 -1
  170. package/lib/types/workspaces.js +1 -1
  171. package/node_modules/@dsh-std/command/CHANGELOG.md +8 -0
  172. package/node_modules/@dsh-std/command/package.json +1 -1
  173. package/node_modules/@dsh-std/connection/CHANGELOG.md +8 -0
  174. package/node_modules/@dsh-std/connection/lib/memory.d.ts +1 -1
  175. package/node_modules/@dsh-std/connection/lib/memory.d.ts.map +1 -1
  176. package/node_modules/@dsh-std/connection/lib/memory.js +1 -0
  177. package/node_modules/@dsh-std/connection/lib/memory.js.map +1 -1
  178. package/node_modules/@dsh-std/connection/package.json +1 -1
  179. package/node_modules/@dsh-std/core/CHANGELOG.md +8 -0
  180. package/node_modules/@dsh-std/core/package.json +1 -1
  181. package/node_modules/@dsh-std/manifest/CHANGELOG.md +9 -0
  182. package/node_modules/@dsh-std/manifest/package.json +1 -1
  183. package/node_modules/@dsh-std/messages/CHANGELOG.md +8 -0
  184. package/node_modules/@dsh-std/messages/package.json +1 -1
  185. package/node_modules/@dsh-std/presentation/CHANGELOG.md +9 -0
  186. package/node_modules/@dsh-std/presentation/README.md +2 -1
  187. package/node_modules/@dsh-std/presentation/README.zh.md +2 -1
  188. package/node_modules/@dsh-std/presentation/lib/callback.d.ts +2 -0
  189. package/node_modules/@dsh-std/presentation/lib/callback.js +179 -0
  190. package/node_modules/@dsh-std/presentation/lib/callback.js.map +1 -0
  191. package/node_modules/@dsh-std/presentation/lib/index.d.ts +44 -1
  192. package/node_modules/@dsh-std/presentation/lib/index.d.ts.map +1 -1
  193. package/node_modules/@dsh-std/presentation/lib/index.js +6 -3
  194. package/node_modules/@dsh-std/presentation/lib/index.js.map +1 -1
  195. package/node_modules/@dsh-std/presentation/package.json +7 -3
  196. package/node_modules/@dsh-std/storage/CHANGELOG.md +8 -0
  197. package/node_modules/@dsh-std/storage/package.json +1 -1
  198. package/package.json +10 -5
  199. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/connection/CHANGELOG.md +8 -0
  200. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/connection/lib/memory.d.ts +1 -1
  201. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/connection/lib/memory.d.ts.map +1 -1
  202. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/connection/lib/memory.js +1 -0
  203. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/connection/lib/memory.js.map +1 -1
  204. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/connection/package.json +1 -1
  205. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/core/CHANGELOG.md +8 -0
  206. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/core/package.json +1 -1
  207. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/manifest/CHANGELOG.md +9 -0
  208. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/manifest/package.json +1 -1
  209. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/presentation/CHANGELOG.md +9 -0
  210. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/presentation/README.md +2 -1
  211. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/presentation/README.zh.md +2 -1
  212. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/presentation/lib/callback.d.ts +2 -0
  213. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/presentation/lib/callback.js +179 -0
  214. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/presentation/lib/callback.js.map +1 -0
  215. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/presentation/lib/index.d.ts +44 -1
  216. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/presentation/lib/index.d.ts.map +1 -1
  217. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/presentation/lib/index.js +6 -3
  218. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/presentation/lib/index.js.map +1 -1
  219. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/presentation/package.json +7 -3
  220. package/vendor/dsh-std/packages/connection/node_modules/@dsh-std/core/CHANGELOG.md +8 -0
  221. package/vendor/dsh-std/packages/connection/node_modules/@dsh-std/core/package.json +1 -1
  222. package/vendor/dsh-std/packages/manifest/node_modules/@dsh-std/core/CHANGELOG.md +8 -0
  223. package/vendor/dsh-std/packages/manifest/node_modules/@dsh-std/core/package.json +1 -1
  224. package/vendor/dsh-std/packages/messages/node_modules/@dsh-std/core/CHANGELOG.md +8 -0
  225. package/vendor/dsh-std/packages/messages/node_modules/@dsh-std/core/package.json +1 -1
  226. package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/connection/CHANGELOG.md +8 -0
  227. package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/connection/lib/memory.d.ts +1 -1
  228. package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/connection/lib/memory.d.ts.map +1 -1
  229. package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/connection/lib/memory.js +1 -0
  230. package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/connection/lib/memory.js.map +1 -1
  231. package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/connection/package.json +1 -1
  232. package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/core/CHANGELOG.md +8 -0
  233. package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/core/package.json +1 -1
  234. package/vendor/dsh-std/packages/storage/node_modules/@dsh-std/core/CHANGELOG.md +8 -0
  235. package/vendor/dsh-std/packages/storage/node_modules/@dsh-std/core/package.json +1 -1
  236. package/ecosystem-spec/registry/registry-0.15.json +0 -57
  237. /package/{ecosystem-spec → dsh-ecosystem-spec}/registry/contracts/decision-events-v1alpha1.json +0 -0
  238. /package/{ecosystem-spec → dsh-ecosystem-spec}/registry/host-descriptor.tui.example.json +0 -0
  239. /package/{ecosystem-spec → dsh-ecosystem-spec}/schemas/conformance-claim.schema.json +0 -0
  240. /package/{ecosystem-spec → dsh-ecosystem-spec}/schemas/effect-ledger-record.schema.json +0 -0
  241. /package/{ecosystem-spec → dsh-ecosystem-spec}/schemas/host-descriptor.schema.json +0 -0
@@ -0,0 +1,143 @@
1
+ /** dsh-TUI private, lifecycle-owned manifest contribution definitions. */
2
+
3
+ export const API_VERSION = 'tui.dsh/v1alpha1'
4
+
5
+ export const SETTINGS_SECTION = Object.freeze({
6
+ apiVersion: API_VERSION,
7
+ kind: 'SettingsSection',
8
+ })
9
+
10
+ export const SCENE = Object.freeze({
11
+ apiVersion: API_VERSION,
12
+ kind: 'Scene',
13
+ })
14
+
15
+ export const settingsSectionExtensionDefinition = Object.freeze({
16
+ ...SETTINGS_SECTION,
17
+ validateMetadata(metadata) {
18
+ contributionName(metadata?.name, 'SettingsSection metadata.name')
19
+ },
20
+ validateSpec: validateSettingsSectionSpec,
21
+ })
22
+
23
+ export function validateSettingsSectionSpec(value) {
24
+ const spec = exactRecord(value, ['namespace', 'title', 'titles', 'fields'], 'SettingsSection content')
25
+ contributionName(spec.namespace, 'SettingsSection content.namespace')
26
+ nonEmpty(spec.title, 'SettingsSection content.title')
27
+ localized(spec.titles, 'SettingsSection content.titles')
28
+ if (!Array.isArray(spec.fields)) throw new TypeError('SettingsSection content.fields must be an array')
29
+ return Object.freeze({
30
+ namespace: spec.namespace,
31
+ title: spec.title,
32
+ ...(spec.titles === undefined ? {} : { titles: freezeRecord(spec.titles) }),
33
+ fields: Object.freeze(spec.fields.map((field, index) => validateSettingsField(field, index))),
34
+ })
35
+ }
36
+
37
+ export const sceneExtensionDefinition = Object.freeze({
38
+ ...SCENE,
39
+ validateMetadata(metadata) {
40
+ contributionName(metadata?.name, 'Scene metadata.name')
41
+ },
42
+ validateSpec: validateSceneSpec,
43
+ })
44
+
45
+ export function validateSceneSpec(value) {
46
+ const spec = exactRecord(value, ['title', 'titles'], 'Scene content')
47
+ if (spec.title !== undefined) nonEmpty(spec.title, 'Scene content.title')
48
+ localized(spec.titles, 'Scene content.titles')
49
+ return Object.freeze({
50
+ ...(spec.title === undefined ? {} : { title: spec.title }),
51
+ ...(spec.titles === undefined ? {} : { titles: freezeRecord(spec.titles) }),
52
+ })
53
+ }
54
+
55
+ export const contributionExtensionDefinitions = Object.freeze([
56
+ settingsSectionExtensionDefinition,
57
+ sceneExtensionDefinition,
58
+ ])
59
+
60
+ export function registerTuiContributionExtensions(catalog) {
61
+ const disposers = contributionExtensionDefinitions.map(definition => catalog.registerExtension(definition))
62
+ return () => { for (const dispose of disposers.reverse()) dispose() }
63
+ }
64
+
65
+ export function assertSettingsSectionHandler(value) {
66
+ runtimeObject(value, 'SettingsSection handler')
67
+ }
68
+
69
+ export function assertSceneHandler(value) {
70
+ const handler = runtimeObject(value, 'Scene handler')
71
+ if (typeof handler.component !== 'function') throw new TypeError('Scene handler.component must be a function')
72
+ }
73
+
74
+ function validateSettingsField(value, index) {
75
+ const label = `SettingsSection spec.fields[${index}]`
76
+ const field = exactRecord(value, [
77
+ 'path', 'label', 'titles', 'hint', 'hintTitles', 'kind', 'options', 'placeholder', 'secretRef',
78
+ ], label)
79
+ if (!Array.isArray(field.path) || field.path.length === 0
80
+ || field.path.some(segment => typeof segment !== 'string' || segment.length === 0)) {
81
+ throw new TypeError(`${label}.path must be a non-empty string array`)
82
+ }
83
+ nonEmpty(field.label, `${label}.label`)
84
+ localized(field.titles, `${label}.titles`)
85
+ if (field.hint !== undefined) nonEmpty(field.hint, `${label}.hint`)
86
+ localized(field.hintTitles, `${label}.hintTitles`)
87
+ if (!['text', 'number', 'boolean', 'select'].includes(field.kind)) {
88
+ throw new TypeError(`${label}.kind is invalid`)
89
+ }
90
+ if (field.placeholder !== undefined && typeof field.placeholder !== 'string') {
91
+ throw new TypeError(`${label}.placeholder must be a string`)
92
+ }
93
+ if (field.secretRef !== undefined) nonEmpty(field.secretRef, `${label}.secretRef`)
94
+ if (field.options !== undefined) {
95
+ if (!Array.isArray(field.options)) throw new TypeError(`${label}.options must be an array`)
96
+ for (const [optionIndex, valueOption] of field.options.entries()) {
97
+ const option = exactRecord(valueOption, ['value', 'label', 'titles'], `${label}.options[${optionIndex}]`)
98
+ nonEmpty(option.value, `${label}.options[${optionIndex}].value`)
99
+ nonEmpty(option.label, `${label}.options[${optionIndex}].label`)
100
+ localized(option.titles, `${label}.options[${optionIndex}].titles`)
101
+ }
102
+ }
103
+ return Object.freeze(structuredClone(field))
104
+ }
105
+
106
+ function exactRecord(value, allowed, label) {
107
+ if (typeof value !== 'object' || value === null || Array.isArray(value)) {
108
+ throw new TypeError(`${label} must be an object`)
109
+ }
110
+ const unknown = Object.keys(value).filter(key => !allowed.includes(key) && !key.startsWith('x-'))
111
+ if (unknown.length > 0) throw new TypeError(`${label} contains unknown field ${JSON.stringify(unknown[0])}`)
112
+ return value
113
+ }
114
+
115
+ function runtimeObject(value, label) {
116
+ if ((typeof value !== 'object' && typeof value !== 'function') || value === null || Array.isArray(value)) {
117
+ throw new TypeError(`${label} must be an object`)
118
+ }
119
+ return value
120
+ }
121
+
122
+ function contributionName(value, label) {
123
+ if (typeof value !== 'string' || !/^[a-z][a-z0-9_-]*$/u.test(value)) {
124
+ throw new TypeError(`${label} is invalid`)
125
+ }
126
+ }
127
+
128
+ function nonEmpty(value, label) {
129
+ if (typeof value !== 'string' || value.trim() === '') throw new TypeError(`${label} must be a non-empty string`)
130
+ }
131
+
132
+ function localized(value, label) {
133
+ if (value === undefined) return
134
+ const record = exactRecord(value, Object.keys(value), label)
135
+ for (const [locale, text] of Object.entries(record)) {
136
+ if (locale.trim() === '') throw new TypeError(`${label} contains an empty locale`)
137
+ nonEmpty(text, `${label}.${locale}`)
138
+ }
139
+ }
140
+
141
+ function freezeRecord(value) {
142
+ return Object.freeze({ ...value })
143
+ }
@@ -0,0 +1,14 @@
1
+ # dsh-TUI Admission Protocol Profile
2
+
3
+ [`registry-0.15.json`](registry-0.15.json) 不是全局协议注册表。它描述本 TUI admission profile 使用的 definition 集合:
4
+
5
+ - `imports` 引用固定 dsh-std revision 已提供的 definitions;
6
+ - `definitions` 只收录 dsh-TUI 私有 definitions。
7
+
8
+ 导入条目不复制 dsh-std 的 schema 或 contract 正文。Community v0.15 的 `Command`、`LocalStorage`、`MessageObserver`、Presentation 与 Workspace 定义分别由 `@dsh-std/command`、`@dsh-std/storage`、`@dsh-std/messages`、`@dsh-std/presentation` 与 `@dsh-std/workspace` 注册。
9
+
10
+ 本地 definition 的 contract profile 使用 SHA-256 固定。修改 profile 内容必须更新 digest,并按兼容性决定保留坐标或发布新 `apiVersion`。digest 只证明字节一致,不证明发布者身份。
11
+
12
+ `tui.dsh/*` 是 dsh-TUI 私有 namespace。私有 definition 与公共 definition 一样注册到 dsh-std `ProtocolCatalog`;目录收录本身不产生 live support。
13
+
14
+ 权限目录仍是 TUI authorization policy 的输入。permission grant 与 protocol support 分开判断,安装 definition 不自动授权操作。
@@ -0,0 +1,13 @@
1
+ {
2
+ "name":"tui.scene","version":"v1alpha1","kind":"extension",
3
+ "coordinates":{"apiVersion":"tui.dsh/v1alpha1","kind":"Scene"},
4
+ "caller":"trusted in-process dsh-TUI scene shell",
5
+ "permissions":[],
6
+ "operations":["open","close","render"],
7
+ "errors":["SCENE_INVALID","SCENE_CONFLICT","RENDERER_INCOMPATIBLE"],
8
+ "concurrency":"one scene may own the full-screen surface at a time",
9
+ "timeout":"scene operations are local; domain calls made by a scene retain their own deadlines",
10
+ "cleanup":"an open scene closes when its owning facet deactivates",
11
+ "privacyClass":"sensitive",
12
+ "securityBoundary":false
13
+ }
@@ -0,0 +1,13 @@
1
+ {
2
+ "name":"tui.settings-section","version":"v1alpha1","kind":"extension",
3
+ "coordinates":{"apiVersion":"tui.dsh/v1alpha1","kind":"SettingsSection"},
4
+ "caller":"dsh-TUI settings contribution binder",
5
+ "permissions":[],
6
+ "operations":["render"],
7
+ "errors":["SECTION_INVALID","NAMESPACE_CONFLICT","NAMESPACE_UNAVAILABLE"],
8
+ "concurrency":"one active section owns each settings namespace",
9
+ "timeout":"not applicable to declarative rendering; writes use the settings protocol deadline",
10
+ "cleanup":"removing a section removes only UI metadata and preserves settings and credentials",
11
+ "privacyClass":"public",
12
+ "securityBoundary":false
13
+ }
@@ -0,0 +1,30 @@
1
+ {
2
+ "name": "tui.channel",
3
+ "version": "v1alpha1",
4
+ "coordinates": {
5
+ "apiVersion": "tui.dsh/v1alpha1",
6
+ "kind": "Channel"
7
+ },
8
+ "kind": "capability",
9
+ "caller": "a negotiated TUI endpoint",
10
+ "permissions": [],
11
+ "operations": [
12
+ {"name": "open", "input": "workspace/session selector", "output": "complete channel snapshot"},
13
+ {"name": "subscribe", "input": "channel id and last observed version", "output": "snapshot progress stream"},
14
+ {"name": "invoke", "input": "channel method and JSON arguments", "output": "method progress, JSON result and optional snapshot"},
15
+ {"name": "close", "input": "channel id", "output": "closed acknowledgement"}
16
+ ],
17
+ "wireRevision": 6,
18
+ "features": [
19
+ "commands", "credentials", "diagnostics", "files", "models", "modes",
20
+ "presets", "presentation", "provider-setup", "scenes", "session-history", "session-input",
21
+ "session-lifecycle", "session-state", "settings", "skills", "subagents",
22
+ "trace", "workspaces"
23
+ ],
24
+ "errors": ["CHANNEL_NOT_FOUND", "FEATURE_UNAVAILABLE", "INVALID_ARGUMENT", "PERMISSION_DENIED", "REVISION_MISMATCH", "PROVIDER_FAILURE"],
25
+ "concurrency": "state mutations are serialized per channel; independent read operations may run concurrently",
26
+ "timeout": "the caller supplies connection invocation cancellation; subscriptions remain open until cancelled or closed",
27
+ "cleanup": "connection close or facet deactivate releases subscriptions, uploads and channel attachments",
28
+ "privacyClass": "sensitive",
29
+ "securityBoundary": false
30
+ }
@@ -0,0 +1,6 @@
1
+ {
2
+ "status": "superseded",
3
+ "coordinates": {"apiVersion": "workspace.dsh/v1alpha1", "kind": "WorkspaceProvider"},
4
+ "package": "@dsh-std/workspace",
5
+ "proposal": "vendor/dsh-std/docs/proposals/workspace.zh.md"
6
+ }
@@ -5,7 +5,7 @@
5
5
  {"name":"storage.local.write","default":"deny","revocable":true,"scope":"plugin namespace"},
6
6
  {"name":"commands.invoke","default":"allow","revocable":true,"scope":"declared command id","rationale":"调用由宿主或用户通过一次 invocation 主动发起,不同于数据访问类权限——插件无法仅凭此权限被动读取任何数据,故默认允许;撤销只关闭该 command ID 的调用入口"},
7
7
  {"name":"messages.observe.read","default":"deny","revocable":true,"scope":"message observation scope"},
8
- {"name":"session.input.intercept","default":"deny","revocable":true,"scope":"decision event subscription","rationale":"拦截类订阅(RFC 0005 D-7):能否决/改写用户输入,严格强于观察,故默认拒绝;TUI 实验实现见 tui.dsh/v1alpha1#DecisionEvents"},
8
+ {"name":"session.input.intercept","default":"deny","revocable":true,"scope":"decision event subscription","rationale":"拦截类订阅(RFC 0005 D-7):能否决/改写用户输入,严格强于观察,故默认拒绝;TUI 实验实现见 tui.dsh.decision-events"},
9
9
  {"name":"session.rewind.intercept","default":"deny","revocable":true,"scope":"decision event subscription","rationale":"同 session.input.intercept——可否决会话回退或注入回退模式"},
10
10
  {"name":"session.switch.intercept","default":"deny","revocable":true,"scope":"decision event subscription","rationale":"同 session.input.intercept——可否决会话切换"},
11
11
  {"name":"session.compact.intercept","default":"deny","revocable":true,"scope":"decision event subscription","rationale":"同 session.input.intercept——可否决手动压缩"}
@@ -0,0 +1,149 @@
1
+ {
2
+ "profileVersion": "tui-admission/0.15",
3
+ "std": {
4
+ "repository": "https://github.com/Yan-Zero/dsh-std",
5
+ "submodule": "vendor/dsh-std",
6
+ "manifestVersion": "0.15"
7
+ },
8
+ "imports": [
9
+ {
10
+ "name": "commands",
11
+ "coordinates": {
12
+ "apiVersion": "commands.dsh/v1alpha1",
13
+ "kind": "Command"
14
+ },
15
+ "package": "@dsh-std/command",
16
+ "kind": "capability",
17
+ "permissions": [
18
+ "commands.invoke"
19
+ ]
20
+ },
21
+ {
22
+ "name": "storage.local",
23
+ "coordinates": {
24
+ "apiVersion": "storage.dsh/v1alpha1",
25
+ "kind": "LocalStorage"
26
+ },
27
+ "package": "@dsh-std/storage",
28
+ "kind": "capability",
29
+ "permissions": [
30
+ "storage.local.read",
31
+ "storage.local.write"
32
+ ]
33
+ },
34
+ {
35
+ "name": "messages.observe",
36
+ "coordinates": {
37
+ "apiVersion": "messages.dsh/v1alpha1",
38
+ "kind": "MessageObserver"
39
+ },
40
+ "package": "@dsh-std/messages",
41
+ "kind": "event",
42
+ "permissions": [
43
+ "messages.observe.read"
44
+ ],
45
+ "eventEnvelope": "@dsh-std/messages/schema/message-observer-envelope-0.15.json"
46
+ },
47
+ {
48
+ "name": "presentation.open-external",
49
+ "coordinates": {
50
+ "apiVersion": "presentation.dsh/v1alpha1",
51
+ "kind": "OpenExternal"
52
+ },
53
+ "package": "@dsh-std/presentation",
54
+ "kind": "capability",
55
+ "permissions": []
56
+ },
57
+ {
58
+ "name": "presentation.user-interaction",
59
+ "coordinates": {
60
+ "apiVersion": "presentation.dsh/v1alpha1",
61
+ "kind": "UserInteraction"
62
+ },
63
+ "package": "@dsh-std/presentation",
64
+ "kind": "capability",
65
+ "permissions": []
66
+ },
67
+ {
68
+ "name": "presentation.external-redirect",
69
+ "coordinates": {
70
+ "apiVersion": "presentation.dsh/v1alpha1",
71
+ "kind": "ExternalRedirect"
72
+ },
73
+ "package": "@dsh-std/presentation",
74
+ "kind": "capability",
75
+ "permissions": []
76
+ }
77
+ ],
78
+ "definitions": [
79
+ {
80
+ "name": "tui.decision-events",
81
+ "authority": "dsh-tui",
82
+ "coordinates": {
83
+ "apiVersion": "tui.dsh/v1alpha1",
84
+ "kind": "DecisionEvents"
85
+ },
86
+ "kind": "capability",
87
+ "profile": "registry/contracts/decision-events-v1alpha1.json",
88
+ "profileHash": "sha256:56440dde1b00c210b22761a0cba139ce0a20207330567777cebe1e48b494ceb0",
89
+ "permissions": [
90
+ "session.input.intercept",
91
+ "session.rewind.intercept",
92
+ "session.switch.intercept",
93
+ "session.compact.intercept"
94
+ ]
95
+ },
96
+ {
97
+ "name": "tui.channel",
98
+ "authority": "dsh-tui",
99
+ "coordinates": {
100
+ "apiVersion": "tui.dsh/v1alpha1",
101
+ "kind": "Channel"
102
+ },
103
+ "kind": "capability",
104
+ "profile": "registry/contracts/tui-channel-v1alpha1.json",
105
+ "profileHash": "sha256:2d0668656d999a8cacd363cd3c76207f9392118be8a83e6dd5f81b54ae063fe6",
106
+ "permissions": []
107
+ }
108
+ ],
109
+ "extensions": [
110
+ {
111
+ "name": "workspace.provider",
112
+ "authority": "dsh-std",
113
+ "coordinates": {
114
+ "apiVersion": "workspace.dsh/v1alpha1",
115
+ "kind": "WorkspaceProvider"
116
+ },
117
+ "kind": "extension",
118
+ "package": "@dsh-std/workspace",
119
+ "permissions": []
120
+ },
121
+ {
122
+ "name": "tui.settings-section",
123
+ "authority": "dsh-tui",
124
+ "coordinates": {
125
+ "apiVersion": "tui.dsh/v1alpha1",
126
+ "kind": "SettingsSection"
127
+ },
128
+ "kind": "extension",
129
+ "profile": "registry/contracts/settings-section-v1alpha1.json",
130
+ "profileHash": "sha256:685b0f17fe489f458e9b799811f61c42b858330584e95ecb97e7216b5be100ac",
131
+ "permissions": []
132
+ },
133
+ {
134
+ "name": "tui.scene",
135
+ "authority": "dsh-tui",
136
+ "coordinates": {
137
+ "apiVersion": "tui.dsh/v1alpha1",
138
+ "kind": "Scene"
139
+ },
140
+ "kind": "extension",
141
+ "profile": "registry/contracts/scene-v1alpha1.json",
142
+ "profileHash": "sha256:69ce3ae26d60dea2ed170376db8a1fb5860c94bd192c62681423c1d848ec2b26",
143
+ "permissions": []
144
+ }
145
+ ],
146
+ "facetApiVersions": [
147
+ "v1alpha1"
148
+ ]
149
+ }
@@ -1,3 +1,4 @@
1
- export * from './dsh-adapter/command-trees.js';
1
+ export { name, TuiCommandTreeRuntime } from './dsh-adapter/command-trees.js';
2
+ export type { TuiCommandTreeProvider } from './dsh-adapter/command-trees.js';
2
3
  export { default } from './dsh-adapter/command-trees.js';
3
4
  //# sourceMappingURL=command-trees.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"command-trees.d.ts","sourceRoot":"","sources":["../../src/command-trees.ts"],"names":[],"mappings":"AAEA,cAAc,gCAAgC,CAAA;AAC9C,OAAO,EAAE,OAAO,EAAE,MAAM,gCAAgC,CAAA"}
1
+ {"version":3,"file":"command-trees.d.ts","sourceRoot":"","sources":["../../src/command-trees.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,IAAI,EAAE,qBAAqB,EAAE,MAAM,gCAAgC,CAAA;AAC5E,YAAY,EAAE,sBAAsB,EAAE,MAAM,gCAAgC,CAAA;AAC5E,OAAO,EAAE,OAAO,EAAE,MAAM,gCAAgC,CAAA"}
@@ -1,4 +1,4 @@
1
1
  // Re-export shim: the Cordis-backed implementation lives behind the adapter
2
2
  // boundary so UI consumers never import official @deepseek-ai/* packages.
3
- export * from './dsh-adapter/command-trees.js';
3
+ export { name, TuiCommandTreeRuntime } from './dsh-adapter/command-trees.js';
4
4
  export { default } from './dsh-adapter/command-trees.js';
@@ -1,15 +1,20 @@
1
1
  import React from 'react';
2
2
  import type { LoadedContext } from '../dsh-adapter/channel.js';
3
3
  /**
4
- * The startup context summary: a one-line inventory of what a
4
+ * The startup context panel: a collapsed one-line summary of what a
5
5
  * fresh conversation will load for the current agent (system prompt
6
6
  * sections, workspace instruction files, dynamic context, skill catalog,
7
- * tools). `/context` renders the existing details as a local report; the
8
- * summary itself disappears once the transcript has rows. Renders nothing
9
- * for an empty snapshot.
7
+ * tools), expandable to the grouped details with Ctrl+P. The `/context`
8
+ * local command still prints the same details as a transcript report.
9
+ * The panel renders only while the transcript is still empty — the first
10
+ * message's rows take over. Renders nothing for an empty snapshot.
10
11
  * @param context - the channel's loaded-context snapshot.
12
+ * @param open - whether the grouped details are shown.
13
+ * @param onToggle - flips `open`; fired by the Ctrl+P keybinding.
11
14
  */
12
- export declare function LoadedContextPanel({ context }: {
15
+ export declare function LoadedContextPanel({ context, open, onToggle, }: {
13
16
  context: LoadedContext;
17
+ open: boolean;
18
+ onToggle: () => void;
14
19
  }): React.ReactNode;
15
20
  //# sourceMappingURL=LoadedContextPanel.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"LoadedContextPanel.d.ts","sourceRoot":"","sources":["../../../src/components/LoadedContextPanel.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AAGzB,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAA;AAG9D;;;;;;;;GAQG;AACH,wBAAgB,kBAAkB,CAAC,EAAE,OAAO,EAAE,EAAE;IAAE,OAAO,EAAE,aAAa,CAAA;CAAE,GAAG,KAAK,CAAC,SAAS,CAW3F"}
1
+ {"version":3,"file":"LoadedContextPanel.d.ts","sourceRoot":"","sources":["../../../src/components/LoadedContextPanel.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AAGzB,OAAO,KAAK,EAAE,aAAa,EAAsB,MAAM,2BAA2B,CAAA;AA+BlF;;;;;;;;;;;GAWG;AACH,wBAAgB,kBAAkB,CAAC,EACjC,OAAO,EACP,IAAI,EACJ,QAAQ,GACT,EAAE;IACD,OAAO,EAAE,aAAa,CAAA;IACtB,IAAI,EAAE,OAAO,CAAA;IACb,QAAQ,EAAE,MAAM,IAAI,CAAA;CACrB,GAAG,KAAK,CAAC,SAAS,CAoElB"}
@@ -1,19 +1,30 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { t } from '../i18n.js';
3
3
  import { Box, Text } from '../ui.js';
4
- import { summarizeLoadedContext } from '../utils/loaded-context.js';
4
+ import { summarizeLoadedContext, truncateContextText } from '../utils/loaded-context.js';
5
+ /** One named entry (section or dynamic context) with its full text. */
6
+ function Entry({ entry }) {
7
+ return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { bold: true, dimColor: true, children: entry.name }), _jsx(Text, { dimColor: true, wrap: "wrap", children: truncateContextText(entry.text) })] }));
8
+ }
9
+ /** A titled group of rows inside the expanded panel. */
10
+ function Group({ title, children }) {
11
+ return (_jsxs(Box, { flexDirection: "column", marginTop: 1, children: [_jsx(Text, { bold: true, color: "subtle", children: title }), _jsx(Box, { flexDirection: "column", paddingLeft: 2, children: children })] }));
12
+ }
5
13
  /**
6
- * The startup context summary: a one-line inventory of what a
14
+ * The startup context panel: a collapsed one-line summary of what a
7
15
  * fresh conversation will load for the current agent (system prompt
8
16
  * sections, workspace instruction files, dynamic context, skill catalog,
9
- * tools). `/context` renders the existing details as a local report; the
10
- * summary itself disappears once the transcript has rows. Renders nothing
11
- * for an empty snapshot.
17
+ * tools), expandable to the grouped details with Ctrl+P. The `/context`
18
+ * local command still prints the same details as a transcript report.
19
+ * The panel renders only while the transcript is still empty — the first
20
+ * message's rows take over. Renders nothing for an empty snapshot.
12
21
  * @param context - the channel's loaded-context snapshot.
22
+ * @param open - whether the grouped details are shown.
23
+ * @param onToggle - flips `open`; fired by the Ctrl+P keybinding.
13
24
  */
14
- export function LoadedContextPanel({ context }) {
25
+ export function LoadedContextPanel({ context, open, onToggle, }) {
15
26
  const summary = summarizeLoadedContext(context);
16
27
  if (summary === '')
17
28
  return null;
18
- return (_jsx(Box, { marginTop: 1, marginBottom: 1, paddingX: 1, children: _jsxs(Text, { wrap: "truncate", children: [t('context-loaded'), " \u00B7 ", summary, _jsx(Text, { dimColor: true, children: " \u00B7 /context" })] }) }));
29
+ return (_jsxs(Box, { flexDirection: "column", marginTop: 1, marginBottom: 1, children: [_jsx(Box, { paddingX: 1, backgroundColor: open ? 'userMessageBackground' : undefined, children: _jsxs(Text, { bold: open, wrap: "truncate", children: [open ? '▼' : '▶', " ", _jsxs(Text, { dimColor: true, children: ["\uFF08Ctrl+P", open ? t('context-panel-collapse') : t('context-panel-expand'), "\uFF09"] }), " ", t('context-loaded'), " \u00B7 ", summary] }) }), open && (_jsxs(Box, { flexDirection: "column", paddingX: 1, paddingTop: 1, children: [context.sections.length > 0 && (_jsx(Group, { title: t('context-panel-sections', { n: context.sections.length }), children: context.sections.map(section => (_jsx(Entry, { entry: section }, section.name))) })), context.files.length > 0 && (_jsx(Group, { title: t('context-panel-files', { n: context.files.length }), children: context.files.map(file => (_jsx(Text, { dimColor: true, children: file.displayPath }, file.displayPath))) })), context.contexts.length > 0 && (_jsx(Group, { title: t('context-panel-runtime', { n: context.contexts.length }), children: context.contexts.map(entry => (_jsx(Entry, { entry: entry }, entry.name))) })), context.skills.length > 0 && (_jsx(Group, { title: t('context-panel-skills', { n: context.skills.length }), children: context.skills.map(skill => (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { bold: true, dimColor: true, children: skill.name }), _jsx(Text, { dimColor: true, wrap: "wrap", children: skill.description })] }, skill.name))) })), context.tools.length > 0 && (_jsx(Group, { title: t('context-panel-tools', { n: context.tools.length }), children: context.tools.map(tool => (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { bold: true, dimColor: true, children: tool.name }), _jsx(Text, { dimColor: true, wrap: "wrap", children: truncateContextText(tool.description, 160) })] }, tool.name))) })), _jsx(Text, { dimColor: true, children: "\u00B7 /context" })] }))] }));
19
30
  }
@@ -1 +1 @@
1
- {"version":3,"file":"LogoV2.d.ts","sourceRoot":"","sources":["../../../src/components/LogoV2.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AAoDzB;;;;;;;;;;;;;GAaG;AACH,wBAAgB,MAAM,CAAC,EACrB,KAAK,EACL,MAAM,EACN,GAAG,EACH,SAAiB,GAClB,EAAE;IACD,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IAC3B,GAAG,EAAE,MAAM,CAAA;IACX,iFAAiF;IACjF,SAAS,CAAC,EAAE,OAAO,CAAA;CACpB,GAAG,KAAK,CAAC,SAAS,CA+ElB"}
1
+ {"version":3,"file":"LogoV2.d.ts","sourceRoot":"","sources":["../../../src/components/LogoV2.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AAwDzB;;;;;;;;;;;;;GAaG;AACH,wBAAgB,MAAM,CAAC,EACrB,KAAK,EACL,MAAM,EACN,GAAG,EACH,SAAiB,GAClB,EAAE;IACD,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IAC3B,GAAG,EAAE,MAAM,CAAA;IACX,iFAAiF;IACjF,SAAS,CAAC,EAAE,OAAO,CAAA;CACpB,GAAG,KAAK,CAAC,SAAS,CAqFlB"}
@@ -9,6 +9,7 @@ import { getTheme } from '../theme.js';
9
9
  import { useTheme } from './design-system/ThemeProvider.js';
10
10
  import { parseRGB } from './Spinner/spinnerUtils.js';
11
11
  import { renderBigText } from './bigfont.js';
12
+ import { stringWidth } from '../ink/stringWidth.js';
12
13
  import { BRAND, FLASH, ICE, PALE, sweep } from './shimmer.js';
13
14
  import { STANDARD_FRAME_INDEX, WhaleArt } from './Whale.js';
14
15
  import { OPENING_SEQUENCE } from './whaleFrames.js';
@@ -35,13 +36,16 @@ const WHALE_MIN_COLUMNS = 64;
35
36
  */
36
37
  const FULL_WHALE_WIDTH = 40;
37
38
  /**
38
- * Leading spaces that center the welcome line under the drawn whale: the
39
- * art's bounding box spans sprite columns 3..34 (center 18.5) of the
40
- * 40-wide box, and the tagline measure 14
41
- * columns — 18.5 − 7 = 11.5 → 12. Centered on the full 40-column box
42
- * instead would need 13, which reads one column right of the whale body.
39
+ * Center of the whale art's bounding box: sprite columns 3..34 (center
40
+ * 18.5) of the 40-wide box. The welcome tagline is indented so its own
41
+ * center lands on this column — for the 14-column Chinese tagline that is
42
+ * 18.5 − 7 = 11.5 → 12 leading spaces. (Centering on the full 40-column
43
+ * box would need 13, which reads one column right of the whale body.)
44
+ * The pad is recomputed from the rendered tagline's display width so
45
+ * longer locales — e.g. the 21-column English tagline → 8 — stay
46
+ * centered under the art too.
43
47
  */
44
- const WELCOME_PAD = 12;
48
+ const WHALE_CENTER = 18.5;
45
49
  /** `max` → `Max` (effort levels arrive lower-case from the adapter). */
46
50
  function capitalize(text) {
47
51
  return text.length === 0 ? text : text[0].toUpperCase() + text.slice(1);
@@ -89,7 +93,12 @@ export function LogoV2({ model, effort, cwd, skipIntro = false, }) {
89
93
  // Frozen clock for the settled header: t=0 parks every sweep highlight
90
94
  // off-screen, leaving the static gradient behind.
91
95
  const t = settled ? 0 : time;
96
+ const tagline = tr('logo-tagline');
97
+ // Indent that centers the tagline under the whale art's bounding box.
98
+ const welcomePad = showWhale
99
+ ? Math.max(0, Math.round(WHALE_CENTER - stringWidth(tagline) / 2))
100
+ : 2;
92
101
  const bigDeepSeek = renderBigText('DEEPSEEK', t, wordmarkRGB, taglineRGB, FLASH, 60);
93
102
  const bigHarness = renderBigText('HARNESS', t, taglineRGB, PALE, FLASH, 60);
94
- return (_jsxs(Box, { ref: ref, flexDirection: "column", marginTop: 1, children: [_jsxs(Box, { flexDirection: "row", gap: 2, width: "100%", alignItems: "center", children: [showWhale && _jsx(WhaleArt, { frameIndex: frameIndex, width: FULL_WHALE_WIDTH }), _jsxs(Box, { flexDirection: "column", flexShrink: 1, children: [_jsxs(Text, { wrap: "truncate-end", children: [sweep('✦ dsh-TUI', t, wordmarkRGB, wordmarkShimmerRGB, 60), _jsx(Text, { dimColor: true, children: ' v' + VERSION })] }), bigDeepSeek.map((row, index) => (_jsx(Text, { wrap: "truncate-end", children: row }, `ds-${index}`))), bigHarness.map((row, index) => (_jsx(Text, { wrap: "truncate-end", children: row }, `h-${index}`))), _jsxs(Text, { wrap: "truncate-end", children: [model, effort !== undefined && _jsx(Text, { dimColor: true, children: ' · ' + capitalize(effort) + ' effort' })] }), _jsx(Text, { dimColor: true, wrap: "truncate-end", children: cwd }), _jsxs(Text, { wrap: "truncate-end", children: [_jsx(Text, { dimColor: true, children: tr('logo-tip-prefix') }), "/model", _jsxs(Text, { dimColor: true, children: [" ", tr('logo-tip-model'), " \u00B7 "] }), "/help", _jsxs(Text, { dimColor: true, children: [" ", tr('logo-tip-help'), " \u00B7 "] }), "Tab", _jsxs(Text, { dimColor: true, children: [" ", tr('logo-tip-tab')] })] })] })] }), _jsx(Box, { marginTop: 1, paddingLeft: showWhale ? WELCOME_PAD : 2, children: _jsx(Text, { children: sweep(tr('logo-tagline'), t, taglineRGB, FLASH, 60) }) })] }));
103
+ return (_jsxs(Box, { ref: ref, flexDirection: "column", marginTop: 1, children: [_jsxs(Box, { flexDirection: "row", gap: 2, width: "100%", alignItems: "center", children: [showWhale && _jsx(WhaleArt, { frameIndex: frameIndex, width: FULL_WHALE_WIDTH }), _jsxs(Box, { flexDirection: "column", flexShrink: 1, children: [_jsxs(Text, { wrap: "truncate-end", children: [sweep('✦ dsh-TUI', t, wordmarkRGB, wordmarkShimmerRGB, 60), _jsx(Text, { dimColor: true, children: ' v' + VERSION })] }), bigDeepSeek.map((row, index) => (_jsx(Text, { wrap: "truncate-end", children: row }, `ds-${index}`))), bigHarness.map((row, index) => (_jsx(Text, { wrap: "truncate-end", children: row }, `h-${index}`))), _jsxs(Text, { wrap: "truncate-end", children: [model, effort !== undefined && _jsx(Text, { dimColor: true, children: ' · ' + capitalize(effort) + ' effort' })] }), _jsx(Text, { dimColor: true, wrap: "truncate-end", children: cwd }), _jsxs(Text, { wrap: "truncate-end", children: [_jsx(Text, { dimColor: true, children: tr('logo-tip-prefix') }), "/model", _jsxs(Text, { dimColor: true, children: [" ", tr('logo-tip-model'), " \u00B7 "] }), "/help", _jsxs(Text, { dimColor: true, children: [" ", tr('logo-tip-help'), " \u00B7 "] }), "Tab", _jsxs(Text, { dimColor: true, children: [" ", tr('logo-tip-tab')] })] })] })] }), _jsx(Box, { marginTop: 1, paddingLeft: welcomePad, children: _jsx(Text, { children: sweep(tagline, t, taglineRGB, FLASH, 60) }) })] }));
95
104
  }
@@ -18,7 +18,19 @@ type Props = {
18
18
  /**
19
19
  * 混合渲染 Markdown 内容:表格用带边框的 flexbox 组件,其余内容由
20
20
  * formatToken 生成 ANSI 字符串放入 Text。高亮对象异步就绪后自动刷新。
21
+ *
22
+ * memo by content: finished transcript blocks render with the SAME string
23
+ * identity for the whole session (StreamingMarkdown keeps its stable prefix
24
+ * identity-stable precisely to hit this); without the memo every parent
25
+ * re-render re-ran the full token→ANSI→yoga pipeline for every settled
26
+ * block — the dominant long-output stall (string-width via wrap-ansi, 60%+
27
+ * of CPU in streaming profiles).
28
+ */
29
+ declare function MarkdownImpl({ children, dimColor, cacheTokens }: Props): React.ReactNode;
30
+ /**
31
+ * Memoized Markdown: skips the whole token→ANSI→layout pipeline when the
32
+ * content string is the same reference (see MarkdownImpl's doc comment).
21
33
  */
22
- export declare function Markdown({ children, dimColor, cacheTokens }: Props): React.ReactNode;
34
+ export declare const Markdown: React.MemoExoticComponent<typeof MarkdownImpl>;
23
35
  export {};
24
36
  //# sourceMappingURL=Markdown.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"Markdown.d.ts","sourceRoot":"","sources":["../../../src/components/Markdown.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AAOzB;;;;;;;;GAQG;AAEH,KAAK,KAAK,GAAG;IACX,QAAQ,EAAE,MAAM,CAAA;IAChB,+BAA+B;IAC/B,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,+CAA+C;IAC/C,WAAW,CAAC,EAAE,OAAO,CAAA;CACtB,CAAA;AA4GD;;;GAGG;AACH,wBAAgB,QAAQ,CAAC,EAAE,QAAQ,EAAE,QAAgB,EAAE,WAAkB,EAAE,EAAE,KAAK,GAAG,KAAK,CAAC,SAAS,CA6BnG"}
1
+ {"version":3,"file":"Markdown.d.ts","sourceRoot":"","sources":["../../../src/components/Markdown.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AAOzB;;;;;;;;GAQG;AAEH,KAAK,KAAK,GAAG;IACX,QAAQ,EAAE,MAAM,CAAA;IAChB,+BAA+B;IAC/B,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,+CAA+C;IAC/C,WAAW,CAAC,EAAE,OAAO,CAAA;CACtB,CAAA;AA4GD;;;;;;;;;;GAUG;AACH,iBAAS,YAAY,CAAC,EAAE,QAAQ,EAAE,QAAgB,EAAE,WAAkB,EAAE,EAAE,KAAK,GAAG,KAAK,CAAC,SAAS,CA6BhG;AAED;;;GAGG;AACH,eAAO,MAAM,QAAQ,gDAMpB,CAAA"}
@@ -90,8 +90,15 @@ function renderTokensToNodes(tokens, highlight, dimColor) {
90
90
  /**
91
91
  * 混合渲染 Markdown 内容:表格用带边框的 flexbox 组件,其余内容由
92
92
  * formatToken 生成 ANSI 字符串放入 Text。高亮对象异步就绪后自动刷新。
93
+ *
94
+ * memo by content: finished transcript blocks render with the SAME string
95
+ * identity for the whole session (StreamingMarkdown keeps its stable prefix
96
+ * identity-stable precisely to hit this); without the memo every parent
97
+ * re-render re-ran the full token→ANSI→yoga pipeline for every settled
98
+ * block — the dominant long-output stall (string-width via wrap-ansi, 60%+
99
+ * of CPU in streaming profiles).
93
100
  */
94
- export function Markdown({ children, dimColor = false, cacheTokens = true }) {
101
+ function MarkdownImpl({ children, dimColor = false, cacheTokens = true }) {
95
102
  const [highlight, setHighlight] = React.useState(null);
96
103
  React.useEffect(() => {
97
104
  let mounted = true;
@@ -110,3 +117,10 @@ export function Markdown({ children, dimColor = false, cacheTokens = true }) {
110
117
  }, [children, dimColor, highlight, cacheTokens]);
111
118
  return (_jsx(Box, { flexDirection: "column", gap: 1, children: renderedNodes }));
112
119
  }
120
+ /**
121
+ * Memoized Markdown: skips the whole token→ANSI→layout pipeline when the
122
+ * content string is the same reference (see MarkdownImpl's doc comment).
123
+ */
124
+ export const Markdown = React.memo(MarkdownImpl, (prev, next) => prev.children === next.children &&
125
+ prev.dimColor === next.dimColor &&
126
+ prev.cacheTokens === next.cacheTokens);
@@ -2,7 +2,7 @@ import React from 'react';
2
2
  import { type ScrollBoxHandle } from '../ui.js';
3
3
  import type { ChatRow } from '../dsh-adapter/channel.js';
4
4
  import type { DOMElement } from '../ink/dom.js';
5
- export declare function MessageList({ rows, expanded, expandedRows, selectedId, onToggleRow, model, diffLayout, showAll, onToggleAll, onLoadOlder, thinkingVisible, registerRowRef, scrollHandle, forceMountRowId, newSinceRowId, onUnseenCount, failureHintRowId, failureHint, }: {
5
+ export declare function MessageList({ rows, expanded, expandedRows, selectedId, onToggleRow, model, diffLayout, thinkingFold, showAll, onToggleAll, onLoadOlder, thinkingVisible, registerRowRef, scrollHandle, forceMountRowId, newSinceRowId, onUnseenCount, failureHintRowId, failureHint, }: {
6
6
  rows: readonly ChatRow[];
7
7
  expanded: boolean;
8
8
  expandedRows: ReadonlySet<number>;
@@ -11,6 +11,8 @@ export declare function MessageList({ rows, expanded, expandedRows, selectedId,
11
11
  model: string;
12
12
  /** Edit/Write diff presentation preference (forwarded to tool cards). */
13
13
  diffLayout?: 'auto' | 'split' | 'unified';
14
+ /** Thinking-block display mode from channel (`preview`/`full`). */
15
+ thinkingFold?: 'preview' | 'full';
14
16
  showAll: boolean;
15
17
  onToggleAll: () => void;
16
18
  /** Restore folded-away older rows from the session log (CC-style "load
@@ -1 +1 @@
1
- {"version":3,"file":"MessageList.d.ts","sourceRoot":"","sources":["../../../src/components/MessageList.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AAEzB,OAAO,EAA8B,KAAK,eAAe,EAAE,MAAM,UAAU,CAAA;AAC3E,OAAO,KAAK,EAAE,OAAO,EAAyC,MAAM,2BAA2B,CAAA;AAC/F,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,eAAe,CAAA;AAuC/C,wBAAgB,WAAW,CAAC,EAC1B,IAAI,EACJ,QAAQ,EACR,YAAY,EACZ,UAAU,EACV,WAAW,EACX,KAAK,EACL,UAAmB,EACnB,OAAO,EACP,WAAW,EACX,WAAW,EACX,eAAsB,EACtB,cAAc,EACd,YAAY,EACZ,eAAe,EACf,aAAa,EACb,aAAa,EACb,gBAAgB,EAChB,WAAW,GACZ,EAAE;IACD,IAAI,EAAE,SAAS,OAAO,EAAE,CAAA;IACxB,QAAQ,EAAE,OAAO,CAAA;IACjB,YAAY,EAAE,WAAW,CAAC,MAAM,CAAC,CAAA;IACjC,UAAU,EAAE,MAAM,GAAG,IAAI,CAAA;IACzB,WAAW,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAA;IACpC,KAAK,EAAE,MAAM,CAAA;IACb,yEAAyE;IACzE,UAAU,CAAC,EAAE,MAAM,GAAG,OAAO,GAAG,SAAS,CAAA;IACzC,OAAO,EAAE,OAAO,CAAA;IAChB,WAAW,EAAE,MAAM,IAAI,CAAA;IACvB;2EACuE;IACvE,WAAW,CAAC,EAAE,MAAM,IAAI,CAAA;IACxB,eAAe,CAAC,EAAE,OAAO,CAAA;IACzB,8EAA8E;IAC9E,cAAc,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,EAAE,UAAU,GAAG,IAAI,KAAK,IAAI,CAAA;IAC/D,oDAAoD;IACpD,YAAY,CAAC,EAAE,eAAe,GAAG,IAAI,CAAA;IACrC,4EAA4E;IAC5E,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC/B;8EAC0E;IAC1E,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC7B,0EAA0E;IAC1E,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAA;IACvC;;;;OAIG;IACH,gBAAgB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAChC,4DAA4D;IAC5D,WAAW,CAAC,EAAE,MAAM,CAAA;CACrB,qBAiRA;AA2QD;;;;;GAKG;AACH,wBAAgB,UAAU,CAAC,EACzB,KAAK,EACL,MAAM,EACN,GAAG,GACJ,EAAE;IACD,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IAC3B,GAAG,EAAE,MAAM,CAAA;CACZ,GAAG,KAAK,CAAC,SAAS,CAMlB"}
1
+ {"version":3,"file":"MessageList.d.ts","sourceRoot":"","sources":["../../../src/components/MessageList.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AAEzB,OAAO,EAA8B,KAAK,eAAe,EAAE,MAAM,UAAU,CAAA;AAC3E,OAAO,KAAK,EAAE,OAAO,EAAyC,MAAM,2BAA2B,CAAA;AAC/F,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,eAAe,CAAA;AAuC/C,wBAAgB,WAAW,CAAC,EAC1B,IAAI,EACJ,QAAQ,EACR,YAAY,EACZ,UAAU,EACV,WAAW,EACX,KAAK,EACL,UAAmB,EACnB,YAAwB,EACxB,OAAO,EACP,WAAW,EACX,WAAW,EACX,eAAsB,EACtB,cAAc,EACd,YAAY,EACZ,eAAe,EACf,aAAa,EACb,aAAa,EACb,gBAAgB,EAChB,WAAW,GACZ,EAAE;IACD,IAAI,EAAE,SAAS,OAAO,EAAE,CAAA;IACxB,QAAQ,EAAE,OAAO,CAAA;IACjB,YAAY,EAAE,WAAW,CAAC,MAAM,CAAC,CAAA;IACjC,UAAU,EAAE,MAAM,GAAG,IAAI,CAAA;IACzB,WAAW,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAA;IACpC,KAAK,EAAE,MAAM,CAAA;IACb,yEAAyE;IACzE,UAAU,CAAC,EAAE,MAAM,GAAG,OAAO,GAAG,SAAS,CAAA;IACzC,mEAAmE;IACnE,YAAY,CAAC,EAAE,SAAS,GAAG,MAAM,CAAA;IACjC,OAAO,EAAE,OAAO,CAAA;IAChB,WAAW,EAAE,MAAM,IAAI,CAAA;IACvB;2EACuE;IACvE,WAAW,CAAC,EAAE,MAAM,IAAI,CAAA;IACxB,eAAe,CAAC,EAAE,OAAO,CAAA;IACzB,8EAA8E;IAC9E,cAAc,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,EAAE,UAAU,GAAG,IAAI,KAAK,IAAI,CAAA;IAC/D,oDAAoD;IACpD,YAAY,CAAC,EAAE,eAAe,GAAG,IAAI,CAAA;IACrC,4EAA4E;IAC5E,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC/B;8EAC0E;IAC1E,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC7B,0EAA0E;IAC1E,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAA;IACvC;;;;OAIG;IACH,gBAAgB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAChC,4DAA4D;IAC5D,WAAW,CAAC,EAAE,MAAM,CAAA;CACrB,qBA6VA;AAmRD;;;;;GAKG;AACH,wBAAgB,UAAU,CAAC,EACzB,KAAK,EACL,MAAM,EACN,GAAG,GACJ,EAAE;IACD,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IAC3B,GAAG,EAAE,MAAM,CAAA;CACZ,GAAG,KAAK,CAAC,SAAS,CAMlB"}