dsh-better-display 0.1.1

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 (300) hide show
  1. package/CHANGELOG.md +51 -0
  2. package/DESIGN.md +72 -0
  3. package/LICENSE +24 -0
  4. package/README.en.md +41 -0
  5. package/README.md +41 -0
  6. package/THIRD_PARTY_NOTICES.md +65 -0
  7. package/cordis.patch.yml +5 -0
  8. package/cordis.yml +4 -0
  9. package/dshx.yml +5 -0
  10. package/lib/client.js +49923 -0
  11. package/lib/client.js.map +1 -0
  12. package/lib/dsh-better-display.js +256 -0
  13. package/lib/types/client/Blocks.d.ts +45 -0
  14. package/lib/types/client/Blocks.d.ts.map +1 -0
  15. package/lib/types/client/Blocks.js +136 -0
  16. package/lib/types/client/Blocks.js.map +1 -0
  17. package/lib/types/client/ChoreographedFlow.d.ts +22 -0
  18. package/lib/types/client/ChoreographedFlow.d.ts.map +1 -0
  19. package/lib/types/client/ChoreographedFlow.js +170 -0
  20. package/lib/types/client/ChoreographedFlow.js.map +1 -0
  21. package/lib/types/client/ClosedProcessSummary.d.ts +9 -0
  22. package/lib/types/client/ClosedProcessSummary.d.ts.map +1 -0
  23. package/lib/types/client/ClosedProcessSummary.js +15 -0
  24. package/lib/types/client/ClosedProcessSummary.js.map +1 -0
  25. package/lib/types/client/LiveFold.d.ts +10 -0
  26. package/lib/types/client/LiveFold.d.ts.map +1 -0
  27. package/lib/types/client/LiveFold.js +39 -0
  28. package/lib/types/client/LiveFold.js.map +1 -0
  29. package/lib/types/client/McpAppFrame.d.ts +32 -0
  30. package/lib/types/client/McpAppFrame.d.ts.map +1 -0
  31. package/lib/types/client/McpAppFrame.js +193 -0
  32. package/lib/types/client/McpAppFrame.js.map +1 -0
  33. package/lib/types/client/Reader.d.ts +3 -0
  34. package/lib/types/client/Reader.d.ts.map +1 -0
  35. package/lib/types/client/Reader.js +537 -0
  36. package/lib/types/client/Reader.js.map +1 -0
  37. package/lib/types/client/ReasoningCard.d.ts +11 -0
  38. package/lib/types/client/ReasoningCard.d.ts.map +1 -0
  39. package/lib/types/client/ReasoningCard.js +301 -0
  40. package/lib/types/client/ReasoningCard.js.map +1 -0
  41. package/lib/types/client/StickyLane.d.ts +8 -0
  42. package/lib/types/client/StickyLane.d.ts.map +1 -0
  43. package/lib/types/client/StickyLane.js +28 -0
  44. package/lib/types/client/StickyLane.js.map +1 -0
  45. package/lib/types/client/TimelineRail.d.ts +10 -0
  46. package/lib/types/client/TimelineRail.d.ts.map +1 -0
  47. package/lib/types/client/TimelineRail.js +168 -0
  48. package/lib/types/client/TimelineRail.js.map +1 -0
  49. package/lib/types/client/ToolActivity.d.ts +17 -0
  50. package/lib/types/client/ToolActivity.d.ts.map +1 -0
  51. package/lib/types/client/ToolActivity.js +192 -0
  52. package/lib/types/client/ToolActivity.js.map +1 -0
  53. package/lib/types/client/TurnMetrics.d.ts +11 -0
  54. package/lib/types/client/TurnMetrics.d.ts.map +1 -0
  55. package/lib/types/client/TurnMetrics.js +44 -0
  56. package/lib/types/client/TurnMetrics.js.map +1 -0
  57. package/lib/types/client/WaitingStatus.d.ts +6 -0
  58. package/lib/types/client/WaitingStatus.d.ts.map +1 -0
  59. package/lib/types/client/WaitingStatus.js +19 -0
  60. package/lib/types/client/WaitingStatus.js.map +1 -0
  61. package/lib/types/client/conversation-scroll.d.ts +19 -0
  62. package/lib/types/client/conversation-scroll.d.ts.map +1 -0
  63. package/lib/types/client/conversation-scroll.js +25 -0
  64. package/lib/types/client/conversation-scroll.js.map +1 -0
  65. package/lib/types/client/deliverables.d.ts +24 -0
  66. package/lib/types/client/deliverables.d.ts.map +1 -0
  67. package/lib/types/client/deliverables.js +104 -0
  68. package/lib/types/client/deliverables.js.map +1 -0
  69. package/lib/types/client/entry-policy.d.ts +10 -0
  70. package/lib/types/client/entry-policy.d.ts.map +1 -0
  71. package/lib/types/client/entry-policy.js +24 -0
  72. package/lib/types/client/entry-policy.js.map +1 -0
  73. package/lib/types/client/entry.d.ts +4 -0
  74. package/lib/types/client/entry.d.ts.map +1 -0
  75. package/lib/types/client/entry.js +34 -0
  76. package/lib/types/client/entry.js.map +1 -0
  77. package/lib/types/client/fold-choreography.d.ts +28 -0
  78. package/lib/types/client/fold-choreography.d.ts.map +1 -0
  79. package/lib/types/client/fold-choreography.js +31 -0
  80. package/lib/types/client/fold-choreography.js.map +1 -0
  81. package/lib/types/client/index.d.ts +7 -0
  82. package/lib/types/client/index.d.ts.map +1 -0
  83. package/lib/types/client/index.js +153 -0
  84. package/lib/types/client/index.js.map +1 -0
  85. package/lib/types/client/live-turn.d.ts +58 -0
  86. package/lib/types/client/live-turn.d.ts.map +1 -0
  87. package/lib/types/client/live-turn.js +144 -0
  88. package/lib/types/client/live-turn.js.map +1 -0
  89. package/lib/types/client/markdown/MarkdownText.d.ts +39 -0
  90. package/lib/types/client/markdown/MarkdownText.d.ts.map +1 -0
  91. package/lib/types/client/markdown/MarkdownText.js +171 -0
  92. package/lib/types/client/markdown/MarkdownText.js.map +1 -0
  93. package/lib/types/client/markdown/cjkFriendlyStrong.d.ts +9 -0
  94. package/lib/types/client/markdown/cjkFriendlyStrong.d.ts.map +1 -0
  95. package/lib/types/client/markdown/cjkFriendlyStrong.js +73 -0
  96. package/lib/types/client/markdown/cjkFriendlyStrong.js.map +1 -0
  97. package/lib/types/client/markdown/incremental.d.ts +69 -0
  98. package/lib/types/client/markdown/incremental.d.ts.map +1 -0
  99. package/lib/types/client/markdown/incremental.js +108 -0
  100. package/lib/types/client/markdown/incremental.js.map +1 -0
  101. package/lib/types/client/markdown/katex.d.ts +27 -0
  102. package/lib/types/client/markdown/katex.d.ts.map +1 -0
  103. package/lib/types/client/markdown/katex.js +88 -0
  104. package/lib/types/client/markdown/katex.js.map +1 -0
  105. package/lib/types/client/markdown/mathCompatibility.d.ts +11 -0
  106. package/lib/types/client/markdown/mathCompatibility.d.ts.map +1 -0
  107. package/lib/types/client/markdown/mathCompatibility.js +293 -0
  108. package/lib/types/client/markdown/mathCompatibility.js.map +1 -0
  109. package/lib/types/client/markdown/parse.d.ts +25 -0
  110. package/lib/types/client/markdown/parse.d.ts.map +1 -0
  111. package/lib/types/client/markdown/parse.js +42 -0
  112. package/lib/types/client/markdown/parse.js.map +1 -0
  113. package/lib/types/client/markdown/render.d.ts +119 -0
  114. package/lib/types/client/markdown/render.d.ts.map +1 -0
  115. package/lib/types/client/markdown/render.js +435 -0
  116. package/lib/types/client/markdown/render.js.map +1 -0
  117. package/lib/types/client/mcp-app.d.ts +50 -0
  118. package/lib/types/client/mcp-app.d.ts.map +1 -0
  119. package/lib/types/client/mcp-app.js +328 -0
  120. package/lib/types/client/mcp-app.js.map +1 -0
  121. package/lib/types/client/message-chrome.d.ts +19 -0
  122. package/lib/types/client/message-chrome.d.ts.map +1 -0
  123. package/lib/types/client/message-chrome.js +42 -0
  124. package/lib/types/client/message-chrome.js.map +1 -0
  125. package/lib/types/client/motion.d.ts +38 -0
  126. package/lib/types/client/motion.d.ts.map +1 -0
  127. package/lib/types/client/motion.js +367 -0
  128. package/lib/types/client/motion.js.map +1 -0
  129. package/lib/types/client/native/ContextBody.d.ts +130 -0
  130. package/lib/types/client/native/ContextBody.d.ts.map +1 -0
  131. package/lib/types/client/native/ContextBody.js +398 -0
  132. package/lib/types/client/native/ContextBody.js.map +1 -0
  133. package/lib/types/client/native/ContextInjectionRow.d.ts +26 -0
  134. package/lib/types/client/native/ContextInjectionRow.d.ts.map +1 -0
  135. package/lib/types/client/native/ContextInjectionRow.js +27 -0
  136. package/lib/types/client/native/ContextInjectionRow.js.map +1 -0
  137. package/lib/types/client/native/ReferenceIcon.d.ts +16 -0
  138. package/lib/types/client/native/ReferenceIcon.d.ts.map +1 -0
  139. package/lib/types/client/native/ReferenceIcon.js +16 -0
  140. package/lib/types/client/native/ReferenceIcon.js.map +1 -0
  141. package/lib/types/client/native/tool-call-model.d.ts +58 -0
  142. package/lib/types/client/native/tool-call-model.d.ts.map +1 -0
  143. package/lib/types/client/native/tool-call-model.js +219 -0
  144. package/lib/types/client/native/tool-call-model.js.map +1 -0
  145. package/lib/types/client/open-file.d.ts +24 -0
  146. package/lib/types/client/primitive-labels.d.ts +10 -0
  147. package/lib/types/client/primitive-labels.d.ts.map +1 -0
  148. package/lib/types/client/primitive-labels.js +37 -0
  149. package/lib/types/client/primitive-labels.js.map +1 -0
  150. package/lib/types/client/projection.d.ts +41 -0
  151. package/lib/types/client/projection.d.ts.map +1 -0
  152. package/lib/types/client/projection.js +112 -0
  153. package/lib/types/client/projection.js.map +1 -0
  154. package/lib/types/client/reasoning-follow.d.ts +6 -0
  155. package/lib/types/client/reasoning-follow.d.ts.map +1 -0
  156. package/lib/types/client/reasoning-follow.js +12 -0
  157. package/lib/types/client/reasoning-follow.js.map +1 -0
  158. package/lib/types/client/settings-copy.d.ts +6 -0
  159. package/lib/types/client/settings-slots.d.ts +16 -0
  160. package/lib/types/client/store.d.ts +17 -0
  161. package/lib/types/client/store.d.ts.map +1 -0
  162. package/lib/types/client/store.js +13 -0
  163. package/lib/types/client/store.js.map +1 -0
  164. package/lib/types/client/stream-buffer.d.ts +29 -0
  165. package/lib/types/client/stream-buffer.d.ts.map +1 -0
  166. package/lib/types/client/stream-buffer.js +113 -0
  167. package/lib/types/client/stream-buffer.js.map +1 -0
  168. package/lib/types/client/streaming.d.ts +18 -0
  169. package/lib/types/client/streaming.d.ts.map +1 -0
  170. package/lib/types/client/streaming.js +77 -0
  171. package/lib/types/client/streaming.js.map +1 -0
  172. package/lib/types/client/timeline.d.ts +26 -0
  173. package/lib/types/client/timeline.d.ts.map +1 -0
  174. package/lib/types/client/timeline.js +48 -0
  175. package/lib/types/client/timeline.js.map +1 -0
  176. package/lib/types/client/tool-activity.d.ts +52 -0
  177. package/lib/types/client/tool-activity.d.ts.map +1 -0
  178. package/lib/types/client/tool-activity.js +178 -0
  179. package/lib/types/client/tool-activity.js.map +1 -0
  180. package/lib/types/client/types.d.ts +66 -0
  181. package/lib/types/client/types.d.ts.map +1 -0
  182. package/lib/types/client/types.js +2 -0
  183. package/lib/types/client/types.js.map +1 -0
  184. package/lib/types/client/waiting-clock.d.ts +17 -0
  185. package/lib/types/client/waiting-clock.d.ts.map +1 -0
  186. package/lib/types/client/waiting-clock.js +24 -0
  187. package/lib/types/client/waiting-clock.js.map +1 -0
  188. package/lib/types/client/word-motion.d.ts +16 -0
  189. package/lib/types/client/word-motion.d.ts.map +1 -0
  190. package/lib/types/client/word-motion.js +89 -0
  191. package/lib/types/client/word-motion.js.map +1 -0
  192. package/lib/types/client/word-timeline.d.ts +28 -0
  193. package/lib/types/client/word-timeline.d.ts.map +1 -0
  194. package/lib/types/client/word-timeline.js +100 -0
  195. package/lib/types/client/word-timeline.js.map +1 -0
  196. package/lib/types/dsh-better-display.d.ts +17 -0
  197. package/lib/types/dsh-better-display.d.ts.map +1 -0
  198. package/lib/types/dsh-better-display.js +53 -0
  199. package/lib/types/dsh-better-display.js.map +1 -0
  200. package/package.json +121 -0
  201. package/scripts/build-motion-fixture.mjs +26 -0
  202. package/scripts/link-harness-dependencies.mjs +51 -0
  203. package/skills/generative-mcpapps/SKILL.md +47 -0
  204. package/skills/generative-mcpapps/examples/interactive_quiz.html +153 -0
  205. package/skills/generative-mcpapps/references/html_boilerplate.md +122 -0
  206. package/skills/generative-mcpapps/references/sep1865_protocol.md +54 -0
  207. package/src/client/Blocks.tsx +205 -0
  208. package/src/client/ChoreographedFlow.tsx +182 -0
  209. package/src/client/ClosedProcessSummary.tsx +19 -0
  210. package/src/client/LiveFold.tsx +43 -0
  211. package/src/client/McpAppFrame.module.css +210 -0
  212. package/src/client/McpAppFrame.tsx +317 -0
  213. package/src/client/Reader.module.css +329 -0
  214. package/src/client/Reader.tsx +814 -0
  215. package/src/client/ReasoningCard.tsx +285 -0
  216. package/src/client/SettingsSection.module.css +147 -0
  217. package/src/client/SettingsSection.tsx +117 -0
  218. package/src/client/StickyLane.tsx +24 -0
  219. package/src/client/TimelineRail.module.css +170 -0
  220. package/src/client/TimelineRail.tsx +262 -0
  221. package/src/client/ToolActivity.tsx +214 -0
  222. package/src/client/TurnMetrics.module.css +139 -0
  223. package/src/client/TurnMetrics.tsx +160 -0
  224. package/src/client/WaitingStatus.tsx +22 -0
  225. package/src/client/conversation-scroll.ts +28 -0
  226. package/src/client/deliverables.ts +122 -0
  227. package/src/client/entry-policy.ts +20 -0
  228. package/src/client/entry.tsx +40 -0
  229. package/src/client/fold-choreography.ts +38 -0
  230. package/src/client/index.tsx +198 -0
  231. package/src/client/live-turn.ts +155 -0
  232. package/src/client/markdown/MarkdownText.module.css +304 -0
  233. package/src/client/markdown/MarkdownText.tsx +187 -0
  234. package/src/client/markdown/cjkFriendlyStrong.ts +84 -0
  235. package/src/client/markdown/incremental.ts +131 -0
  236. package/src/client/markdown/katex.tsx +91 -0
  237. package/src/client/markdown/mathCompatibility.ts +350 -0
  238. package/src/client/markdown/parse.ts +45 -0
  239. package/src/client/markdown/render.tsx +630 -0
  240. package/src/client/markdown/upstream.json +14 -0
  241. package/src/client/mcp-app.ts +323 -0
  242. package/src/client/message-chrome.ts +47 -0
  243. package/src/client/motion.tsx +338 -0
  244. package/src/client/native/ContextBody.module.css +161 -0
  245. package/src/client/native/ContextBody.tsx +591 -0
  246. package/src/client/native/ContextInjectionRow.module.css +62 -0
  247. package/src/client/native/ContextInjectionRow.tsx +72 -0
  248. package/src/client/native/ReferenceIcon.tsx +35 -0
  249. package/src/client/native/tool-call-model.ts +248 -0
  250. package/src/client/native/upstream.json +41 -0
  251. package/src/client/open-file.ts +94 -0
  252. package/src/client/pending-submission.ts +49 -0
  253. package/src/client/primitive-labels.ts +53 -0
  254. package/src/client/projection.ts +118 -0
  255. package/src/client/reasoning-follow.ts +12 -0
  256. package/src/client/settings-copy.ts +53 -0
  257. package/src/client/settings-slots.ts +20 -0
  258. package/src/client/settings.tsx +73 -0
  259. package/src/client/skill-status.ts +78 -0
  260. package/src/client/store.ts +30 -0
  261. package/src/client/stream-buffer.ts +100 -0
  262. package/src/client/streaming.tsx +62 -0
  263. package/src/client/style.d.ts +4 -0
  264. package/src/client/timeline.ts +77 -0
  265. package/src/client/tool-activity.ts +152 -0
  266. package/src/client/types.ts +66 -0
  267. package/src/client/waiting-clock.ts +25 -0
  268. package/src/client/word-motion.tsx +91 -0
  269. package/src/client/word-timeline.ts +96 -0
  270. package/src/dsh-better-display.ts +108 -0
  271. package/src/skill-roots.ts +138 -0
  272. package/src/skill-status.ts +81 -0
  273. package/tests/browser-closed-gap.tsx +20 -0
  274. package/tests/browser-motion.tsx +65 -0
  275. package/tests/browser-sticky-lanes.tsx +22 -0
  276. package/tests/browser-waiting-clock.tsx +14 -0
  277. package/tests/chat-flow-hook.test.ts +66 -0
  278. package/tests/cloud-gate.test.ts +34 -0
  279. package/tests/conversation-scroll.test.ts +47 -0
  280. package/tests/deliverables.test.ts +128 -0
  281. package/tests/entry.test.ts +42 -0
  282. package/tests/fixtures/maid-composer-hide.html +31 -0
  283. package/tests/fold-choreography.test.ts +38 -0
  284. package/tests/history-word-regression.test.ts +16 -0
  285. package/tests/live-turn.test.ts +218 -0
  286. package/tests/mcp-apps.test.ts +157 -0
  287. package/tests/message-chrome.test.ts +29 -0
  288. package/tests/open-file.test.ts +103 -0
  289. package/tests/pending-submission.test.ts +45 -0
  290. package/tests/projection.test.ts +184 -0
  291. package/tests/reasoning-follow.test.ts +34 -0
  292. package/tests/settings-copy.test.ts +63 -0
  293. package/tests/skill-status.test.ts +137 -0
  294. package/tests/stock-install.test.ts +50 -0
  295. package/tests/stream-buffer.test.ts +97 -0
  296. package/tests/timeline.test.ts +59 -0
  297. package/tests/waiting-clock.test.ts +24 -0
  298. package/tests/word-timeline.test.ts +136 -0
  299. package/tsconfig.json +11 -0
  300. package/tsdown.config.ts +49 -0
@@ -0,0 +1,72 @@
1
+ import { useState } from 'react'
2
+ import type { ContextMessageNode } from '@deepseek-ai/dsh-client-ui-conversation/client'
3
+ import type { TranslateNS } from '@deepseek-ai/dsh-client-ui-slots'
4
+ import { DisclosureRow, IconBrowseOutline16 } from '@deepseek-ai/dsh-client-ui-primitives'
5
+ import { ReferenceIcon } from './ReferenceIcon.js'
6
+ import { contextBody } from './ContextBody.js'
7
+ import css from './ContextInjectionRow.module.css'
8
+
9
+ /** Props for the logged non-user message presentation. */
10
+ export interface ContextInjectionRowProps {
11
+ content: ContextMessageNode['content']
12
+ source: ContextMessageNode['source']
13
+ /** Role and producer name projected from the durable source. */
14
+ provenance: ContextMessageNode['provenance']
15
+ /** Producer-declared information form; null renders the opaque body. */
16
+ form: ContextMessageNode['form']
17
+ /** The owning view's locale seat, passed down as a plain prop. */
18
+ t: TranslateNS<'chat'>
19
+ }
20
+
21
+ /**
22
+ * Render logged context with the Tool calls disclosure chrome from Figma.
23
+ *
24
+ * The header names the role the context plays and, beside it, the producer the
25
+ * durable source identifies, so a reader can tell an injected skill catalog
26
+ * from a workspace instruction file or a recalled session without expanding.
27
+ * The expanded body follows the producer-declared form; an absent or unknown
28
+ * form renders the opaque body.
29
+ * @param props - Durable content, its projected producer role/name and form, and the locale seat.
30
+ * @returns A collapsed context row with a bounded, form-specific body.
31
+ */
32
+ export function ContextInjectionRow({ content, source, provenance, form, t }: ContextInjectionRowProps) {
33
+ const [open, setOpen] = useState(false)
34
+ // Resolved rather than declared: a form whose fields are unreadable renders
35
+ // the opaque body, and the marker must say what the row actually shows.
36
+ const { rendered, summary, body } = contextBody(form, { content, source, t })
37
+
38
+ return (
39
+ <DisclosureRow
40
+ className={css.root}
41
+ icon={provenance.role === 'recall'
42
+ ? <span data-context-recall-icon><ReferenceIcon kind="session" /></span>
43
+ : <IconBrowseOutline16 size={14} />}
44
+ chevronClassName={css.chevron}
45
+ title={t(provenance.role === 'recall' ? 'message.contextRecall' : 'message.contextInjection')}
46
+ collapsedContent={provenance.label === null ? undefined : (
47
+ /* ToolRow's separator shape: an aria-hidden dot, so the accessible name
48
+ stays the two readable parts and the two disclosure rows expose one
49
+ name shape. A source that names no producer drops the dot with it. */
50
+ <>
51
+ <span className={css.sep} aria-hidden />
52
+ <span className={css.source} data-context-source>{provenance.label}</span>
53
+ {summary !== null && (
54
+ <>
55
+ <span className={css.sep} aria-hidden />
56
+ <span className={css.summary} data-context-summary>{summary}</span>
57
+ </>
58
+ )}
59
+ </>
60
+ )}
61
+ keepContentWhenOpen
62
+ open={open}
63
+ expandable
64
+ expandOnRowClick
65
+ onToggle={() => { setOpen(value => !value) }}
66
+ >
67
+ <div className={css.body} data-context-injection-body data-context-form={rendered ?? undefined}>
68
+ {body}
69
+ </div>
70
+ </DisclosureRow>
71
+ )
72
+ }
@@ -0,0 +1,35 @@
1
+ import type { ReactNode } from 'react'
2
+ import {
3
+ IconBrowseOutline16, IconFolderClose16,
4
+ } from '@deepseek-ai/dsh-client-ui-primitives'
5
+
6
+ /** Reference domains with distinct composer and transcript glyphs. */
7
+ export type ReferenceIconKind = 'session' | 'file' | 'folder'
8
+
9
+ /** Props shared by inline reference glyphs. */
10
+ export interface ReferenceIconProps {
11
+ kind: ReferenceIconKind
12
+ size?: number
13
+ className?: string | undefined
14
+ }
15
+
16
+ /**
17
+ * Render the icon that identifies one inline reference domain.
18
+ * @param props - Reference kind, optional size, and optional CSS class.
19
+ * @returns The corresponding current-color SVG glyph.
20
+ */
21
+ export function ReferenceIcon({ kind, size = 16, className }: ReferenceIconProps): ReactNode {
22
+ switch (kind) {
23
+ case 'session':
24
+ return (
25
+ <svg width={size} height={size} className={className} viewBox="0 0 16 16" fill="none" aria-hidden>
26
+ <path
27
+ d="M8 0.597656C3.91296 0.597656 0.599716 3.91103 0.599609 7.99805C0.599609 9.13171 0.854567 10.2079 1.31152 11.1699L1.59277 11.7607L2.77441 11.1992L2.49414 10.6084L2.36035 10.3076C2.06865 9.59612 1.90723 8.81645 1.90723 7.99805C1.90733 4.63362 4.63554 1.90625 8 1.90625C11.3644 1.90635 14.0917 4.63368 14.0918 7.99805C14.0918 11.3625 11.3644 14.0907 8 14.0908C7.311 14.0908 6.80642 14.0414 6.35938 13.918C5.919 13.7963 5.50105 13.5929 5.00098 13.2441C4.26805 12.7329 3.21756 12.5526 2.35156 13.0996L2.33789 13.1084L2.32422 13.1182L1.74805 13.5234L2.18164 14.8184L3.05957 14.2002C3.37505 14.0068 3.84248 14.0319 4.25195 14.3174C4.84447 14.7307 5.39718 15.009 6.01172 15.1787C6.61963 15.3465 7.25579 15.3984 8 15.3984C12.087 15.3983 15.4004 12.0851 15.4004 7.99805C15.4003 3.9111 12.087 0.59776 8 0.597656ZM4.56836 8.50977V9.80371H8.12402V8.50977H4.56836ZM4.56836 7.30078H11.4619V6.00684H4.56836V7.30078Z"
28
+ fill="currentColor"
29
+ />
30
+ </svg>
31
+ )
32
+ case 'file': return <IconBrowseOutline16 size={size} className={className} />
33
+ case 'folder': return <IconFolderClose16 size={size} className={className} />
34
+ }
35
+ }
@@ -0,0 +1,248 @@
1
+ /**
2
+ * Pure row-model derivation for tool summary rows: variant classification,
3
+ * one-line summary, expanded-body text, and flattened result output from the
4
+ * frozen call slice. Input material comes from the call ARGUMENTS; output and
5
+ * error material from the settled result node. A call whose render intent is
6
+ * a terminal card gets its expanded body from the views instead, through
7
+ * `terminalCardModel` in terminal-card-model.ts.
8
+ */
9
+ // The block union's defining home is runtime (fold-product types); this
10
+ // contract only forwards it (type-definition authority stays with the layer
11
+ // that produces the values).
12
+ import { abbreviateHomePath } from '@deepseek-ai/dsh-util-workspace-path'
13
+ import type { ToolCallBlock, ToolResultNode } from '@deepseek-ai/dsh-client-ui-conversation/client'
14
+
15
+ export type { ToolCallBlock } from '@deepseek-ai/dsh-client-ui-conversation/client'
16
+
17
+ /** Tool-call row variants selected by the generic atomic renderer. */
18
+ export type ToolRowVariant = 'search' | 'read' | 'bash' | 'write' | 'edit' | 'code' | 'others'
19
+
20
+ /** Row state semantic; colors self-supplied via StateDot (design gives none). */
21
+ export type ToolRowState = 'running' | 'ok' | 'error' | 'stopped'
22
+
23
+ /** Figma row titles per variant (design literals, not translatable copy). */
24
+ export const VARIANT_TITLES: Record<ToolRowVariant, string> = {
25
+ search: 'Search', read: 'Read', bash: 'Bash',
26
+ write: 'Write', edit: 'Edit', code: 'Code', others: 'Tool call',
27
+ }
28
+
29
+ /**
30
+ * Known tool name -> variant.
31
+ *
32
+ * `cordis_define` is deliberately absent: ui-cordis registers a keyed
33
+ * `tool.call.toolview` entry for it, and a keyed hit REPLACES the generic row
34
+ * (this table is only reached through GenericToolCard, the dispatch fallback in
35
+ * ToolCallTree). An entry here would be unreachable, and a second title for the
36
+ * same call would be a second answer to a question the card already owns.
37
+ */
38
+ const TOOL_VARIANTS: Record<string, ToolRowVariant> = {
39
+ bash: 'bash',
40
+ // The PowerShell twin is a shell tool: the bash row family (icon, colors)
41
+ // with its own title from TOOL_TITLES, not the generic `others` row.
42
+ pwsh: 'bash',
43
+ read: 'read',
44
+ web_fetch: 'read',
45
+ web_search: 'search',
46
+ grep: 'search',
47
+ glob: 'search',
48
+ write: 'write',
49
+ edit: 'edit',
50
+ run_code: 'code',
51
+ cordis_package_inspect: 'read',
52
+ cordis_runtime_inspect: 'read',
53
+ // The three run-control verbs take one package id and produce a receipt, so
54
+ // the generic row is the decided intent, not an unclassified default: there is
55
+ // no program to show (that is `cordis_define`'s card) and no file to open. The
56
+ // id lands in the summary slot, and the titles below name the act.
57
+ cordis_run: 'others',
58
+ cordis_stop: 'others',
59
+ cordis_undefine: 'others',
60
+ }
61
+
62
+ /** Tool-owned titles that refine a generic row variant without replacing it. */
63
+ const TOOL_TITLES: Record<string, string> = {
64
+ cordis_package_inspect: 'Inspect',
65
+ cordis_runtime_inspect: 'Inspect',
66
+ cordis_run: 'Run Cordis Plugin',
67
+ cordis_stop: 'Stop Cordis Plugin',
68
+ cordis_undefine: 'Remove Cordis Plugin',
69
+ pwsh: 'Pwsh',
70
+ }
71
+
72
+ /**
73
+ * Classify a tool name into its row variant.
74
+ * @param toolName - wire tool name.
75
+ * @returns matching variant, others when unknown.
76
+ */
77
+ export function classifyTool(toolName: string): ToolRowVariant {
78
+ return TOOL_VARIANTS[toolName] ?? 'others'
79
+ }
80
+
81
+ /** Everything ToolRow needs, derived once from the frozen slice. */
82
+ export interface ToolRowModel {
83
+ variant: ToolRowVariant
84
+ title: string
85
+ summary: string
86
+ /**
87
+ * Filesystem path from args (`path` / `file_path`) when the row is a file
88
+ * tool; absent for URL reads and non-file tools. The chat view resolves
89
+ * relative values against the session cwd before opening.
90
+ */
91
+ filePath: string | undefined
92
+ /** Expanded-body input text (pretty args); null = no input section. */
93
+ body: string | null
94
+ /** Flattened result text ({@link resultText}); null while running or when the result carries no text. */
95
+ output: string | null
96
+ /** First line of the result text on an error row; null for every other state. */
97
+ errorSummary: string | null
98
+ state: ToolRowState
99
+ }
100
+
101
+ /**
102
+ * Flatten a settled result's content blocks to display text: text blocks
103
+ * verbatim, other block shapes as pretty JSON. Empty content on a failed call
104
+ * falls back to the structured error's `name: code` line.
105
+ * @param node - the settled result node.
106
+ * @returns the flattened result text (may be empty).
107
+ */
108
+ export function resultText(node: ToolResultNode): string {
109
+ const parts: string[] = []
110
+ for (const block of node.content) {
111
+ if (block.type === 'text') parts.push(block.text)
112
+ else parts.push(JSON.stringify(block, null, 2))
113
+ }
114
+ if (parts.length === 0 && node.error !== undefined) {
115
+ parts.push(`${node.error.name}: ${node.error.code}`)
116
+ }
117
+ return parts.join('\n')
118
+ }
119
+
120
+ function parseArgs(argsRaw: string): unknown {
121
+ try {
122
+ return JSON.parse(argsRaw)
123
+ } catch {
124
+ // Non-JSON args (mid-stream truncation): summary/body fall back to the raw string.
125
+ return undefined
126
+ }
127
+ }
128
+
129
+ function firstLine(text: string): string {
130
+ const nl = text.indexOf('\n')
131
+ return nl === -1 ? text : text.slice(0, nl)
132
+ }
133
+
134
+ function pickString(args: Record<string, unknown>, keys: readonly string[]): string | undefined {
135
+ for (const key of keys) {
136
+ const v = args[key]
137
+ if (typeof v === 'string' && v !== '') return v
138
+ }
139
+ return undefined
140
+ }
141
+
142
+ /** Summary key preference per variant (args-derived; result-derived summaries are a ledger item). */
143
+ const SUMMARY_KEYS: Record<ToolRowVariant, readonly string[]> = {
144
+ bash: ['description', 'command'],
145
+ read: ['path', 'file_path', 'url'],
146
+ search: ['query', 'pattern', 'url'],
147
+ write: ['path', 'file_path'],
148
+ edit: ['path', 'file_path'],
149
+ code: ['description'],
150
+ others: [],
151
+ }
152
+
153
+ /**
154
+ * Strip the workspace root from a workspace-rooted absolute path (display only).
155
+ * @param text - the path to shorten.
156
+ * @param cwd - session workspace root; absent or empty leaves the path unchanged.
157
+ * @returns the path relative to the workspace root, or unchanged when it is not rooted there.
158
+ */
159
+ export function relativizeToCwd(text: string, cwd: string | undefined): string {
160
+ if (cwd === undefined || cwd === '') return text
161
+ const root = cwd.replace(/[/\\]+$/, '')
162
+ if (text.startsWith(`${root}/`) || text.startsWith(`${root}\\`)) return text.slice(root.length + 1)
163
+ return text
164
+ }
165
+
166
+ function deriveSummary(variant: ToolRowVariant, argsRaw: string): string {
167
+ const parsed = parseArgs(argsRaw)
168
+ if (typeof parsed !== 'object' || parsed === null) return firstLine(argsRaw)
169
+ const args = parsed as Record<string, unknown>
170
+ if (variant === 'search' && Array.isArray(args.queries)) {
171
+ const queries = args.queries.filter((query): query is string => typeof query === 'string' && query !== '')
172
+ if (queries.length > 0) return queries.map(firstLine).join(', ')
173
+ }
174
+ const picked = pickString(args, SUMMARY_KEYS[variant])
175
+ if (picked !== undefined) return firstLine(picked)
176
+ for (const v of Object.values(args)) {
177
+ if (typeof v === 'string' && v !== '') return firstLine(v)
178
+ }
179
+ return firstLine(argsRaw)
180
+ }
181
+
182
+ /** Path keys only — never `url` (web_fetch lands on the read variant). */
183
+ const FILE_PATH_KEYS = ['path', 'file_path'] as const
184
+
185
+ /** File-tool variants whose summary may be an openable workspace path. */
186
+ const FILE_PATH_VARIANTS: ReadonlySet<ToolRowVariant> = new Set(['read', 'write', 'edit'])
187
+
188
+ function deriveFilePath(variant: ToolRowVariant, argsRaw: string): string | undefined {
189
+ if (!FILE_PATH_VARIANTS.has(variant)) return undefined
190
+ const parsed = parseArgs(argsRaw)
191
+ if (typeof parsed !== 'object' || parsed === null) return undefined
192
+ const picked = pickString(parsed as Record<string, unknown>, FILE_PATH_KEYS)
193
+ return picked === undefined ? undefined : firstLine(picked)
194
+ }
195
+
196
+ function deriveBody(variant: ToolRowVariant, argsRaw: string): string | null {
197
+ if (argsRaw === '') return null
198
+ const parsed = parseArgs(argsRaw)
199
+ if (parsed === undefined) return argsRaw
200
+ // The code row's expanded body IS the program (monospace via the row's
201
+ // variant styling), not the args JSON envelope around it.
202
+ if (variant === 'code' && typeof parsed === 'object' && parsed !== null) {
203
+ const code = (parsed as Record<string, unknown>).code
204
+ if (typeof code === 'string' && code !== '') return code
205
+ }
206
+ return JSON.stringify(parsed, null, 2)
207
+ }
208
+
209
+ /**
210
+ * Derive the full row model from a frozen call slice.
211
+ * @param toolName - wire tool name (dispatch-supplied; survives windowless results).
212
+ * @param block - RunningToolCall or ToolResultNode off the snapshot caches.
213
+ * @param cwd - session workspace root; workspace-rooted path summaries display relative to it.
214
+ * @param home - host account home; a leftover POSIX home path displays as `~`.
215
+ * @returns the row model.
216
+ */
217
+ export function toolRowModel(toolName: string, block: ToolCallBlock, cwd?: string, home?: string): ToolRowModel {
218
+ const variant = classifyTool(toolName)
219
+ const done = 'kind' in block
220
+ const argsRaw = (done ? block.call?.argsRaw : block.argsRaw) ?? ''
221
+ const state: ToolRowState = !done ? 'running'
222
+ : block.error?.code === 'interrupted' ? 'stopped'
223
+ : block.isError ? 'error' : 'ok'
224
+ const base = argsRaw === ''
225
+ ? block.callId
226
+ : abbreviateHomePath(relativizeToCwd(deriveSummary(variant, argsRaw), cwd), home)
227
+ const toolTitle = TOOL_TITLES[toolName]
228
+ // Others keeps the static "Tool call" title (figma literal); the real tool
229
+ // name rides the mutable summary slot unless the tool owns a specific title.
230
+ const summary = variant === 'others' && toolName !== '' && toolTitle === undefined
231
+ ? `${toolName} · ${base}`
232
+ : base
233
+ // The empty string is "no text" for both derived result fields: a settled
234
+ // call with blank content has nothing to expand, and a blank first line
235
+ // would erase the collapsed error row's summary slot.
236
+ const output = done ? (resultText(block) || null) : null
237
+ const errorSummary = state === 'error' && output !== null ? firstLine(output) : null
238
+ return {
239
+ variant,
240
+ title: toolTitle ?? VARIANT_TITLES[variant],
241
+ summary,
242
+ filePath: deriveFilePath(variant, argsRaw),
243
+ body: deriveBody(variant, argsRaw),
244
+ output,
245
+ errorSummary,
246
+ state,
247
+ }
248
+ }
@@ -0,0 +1,41 @@
1
+ {
2
+ "repository": "DeepSeek Harness",
3
+ "commit": "b150a551b8d465e31e418e1b2eaf5e79bbb7d28e",
4
+ "adaptations": [
5
+ "Public type import in place of private type-only path",
6
+ "ESM .js import suffixes",
7
+ "Local ReferenceIcon location"
8
+ ],
9
+ "files": [
10
+ {
11
+ "source": "packages/client/ui-conversation/src/client/chat/ContextInjectionRow.tsx",
12
+ "output": "ContextInjectionRow.tsx",
13
+ "sha256": "afb41e6285f2abaf0eb6a1f843dbc135c8480bd48848beed1f94e395ac0f68c8"
14
+ },
15
+ {
16
+ "source": "packages/client/ui-conversation/src/client/chat/ContextInjectionRow.module.css",
17
+ "output": "ContextInjectionRow.module.css",
18
+ "sha256": "af61636789223d2476ec818a76d795bc7c13ca1a433f834d78702a825bd000de"
19
+ },
20
+ {
21
+ "source": "packages/client/ui-conversation/src/client/chat/ContextBody.tsx",
22
+ "output": "ContextBody.tsx",
23
+ "sha256": "94f41356d59bdcdae7225f3cb8cd9b0c0b1a32d24153d82512bfba7c40443cc8"
24
+ },
25
+ {
26
+ "source": "packages/client/ui-conversation/src/client/chat/ContextBody.module.css",
27
+ "output": "ContextBody.module.css",
28
+ "sha256": "b3838bf9cf38af53b31fd6333b4766ad063024d9a59cd636a38e3e0e07d7bf61"
29
+ },
30
+ {
31
+ "source": "packages/client/ui-conversation/src/client/reference/ReferenceIcon.tsx",
32
+ "output": "ReferenceIcon.tsx",
33
+ "sha256": "6315cb1ceafa830cd9e38ce296ddf5791ab93562e4f5a7bdbda1ef9ac6f7ed57"
34
+ },
35
+ {
36
+ "source": "packages/client/ui-tool/src/client/tool/models/tool-call-model.ts",
37
+ "output": "tool-call-model.ts",
38
+ "sha256": "02536cb0be80168351857083d0f0b71554216cd3195bad8016b1e4bb567acc7f"
39
+ }
40
+ ]
41
+ }
@@ -0,0 +1,94 @@
1
+ export type DeliverableOpenMode = 'external' | 'sidebar';
2
+
3
+ export interface OpenModeSnapshot {
4
+ getSnapshot: () => { deliverableOpenMode?: DeliverableOpenMode };
5
+ subscribe?: (fn: () => void) => () => void;
6
+ }
7
+
8
+ export function deliverableOpenModeOf(value: unknown): DeliverableOpenMode {
9
+ return value === 'sidebar' ? 'sidebar' : 'external';
10
+ }
11
+
12
+ export function modeFromSnapshot(store: OpenModeSnapshot | undefined): DeliverableOpenMode {
13
+ try {
14
+ return deliverableOpenModeOf(store?.getSnapshot()?.deliverableOpenMode);
15
+ } catch {
16
+ return 'external';
17
+ }
18
+ }
19
+
20
+ /** Workspace-folder affordances stay on the OS opener, not the Sidebar switch. */
21
+ export function isFolderOpenPath(path: string): boolean {
22
+ return path === '.' || path === '';
23
+ }
24
+
25
+ function isWindowsStylePath(value: string): boolean {
26
+ return /^[A-Za-z]:[/\\]/.test(value) || value.startsWith('\\\\');
27
+ }
28
+
29
+ function isAbsoluteWorkspacePath(path: string): boolean {
30
+ return path.startsWith('/') || isWindowsStylePath(path);
31
+ }
32
+
33
+ function encodeSegment(segment: string): string {
34
+ return encodeURIComponent(segment).replace(/%3A/gi, ':');
35
+ }
36
+
37
+ function encodePath(path: string): string {
38
+ return path.split('/').map(encodeSegment).join('/');
39
+ }
40
+
41
+ /**
42
+ * Session-scoped `dsh-resource://file/session/<id>/…` address.
43
+ * Matches official `fileAddressFor` / `sessionFileAddress` (0.1.5).
44
+ */
45
+ export function fileAddressFor(sessionId: string, cwd: string | undefined, path: string): string {
46
+ const normalized = path.replace(/\\/g, '/');
47
+ const address = (relative: string): string => {
48
+ const cleaned = relative.replace(/\\/g, '/').replace(/^(?:\.\/)+/, '');
49
+ return `dsh-resource://file/session/${encodeSegment(sessionId)}/${encodePath(cleaned)}`;
50
+ };
51
+ if (!isAbsoluteWorkspacePath(normalized)) return address(normalized);
52
+ const root = cwd === undefined ? '' : cwd.replace(/\\/g, '/').replace(/\/+$/, '');
53
+ if (root !== '' && normalized === root) return address('');
54
+ if (root !== '' && normalized.startsWith(`${root}/`)) return address(normalized.slice(root.length + 1));
55
+ return address(normalized);
56
+ }
57
+
58
+ export function resolveOpenWorkspacePath(
59
+ cwd: string | undefined,
60
+ path: string,
61
+ resolveWorkspacePath: (cwd: string | undefined, path: string) => string,
62
+ ): string {
63
+ return isFolderOpenPath(path) ? (cwd ?? '.') : resolveWorkspacePath(cwd, path);
64
+ }
65
+
66
+ export async function openDeliverableFile(args: {
67
+ path: string;
68
+ mode: DeliverableOpenMode;
69
+ sessionId: string;
70
+ cwd: string | undefined;
71
+ resolveWorkspacePath: (cwd: string | undefined, path: string) => string;
72
+ openExternal: (absolutePath: string) => Promise<void>;
73
+ openSidebar?: (address: string) => void;
74
+ fileAddressFor?: (sessionId: string, cwd: string | undefined, path: string) => string;
75
+ warn?: (message: string, extra?: unknown) => void;
76
+ }): Promise<'sidebar' | 'external'> {
77
+ const targetPath = resolveOpenWorkspacePath(args.cwd, args.path, args.resolveWorkspacePath);
78
+ const useSidebar = args.mode === 'sidebar' && !isFolderOpenPath(args.path);
79
+ if (useSidebar) {
80
+ if (typeof args.openSidebar !== 'function') {
81
+ args.warn?.('[dsh-better-display] sidebarRight.openResource is not available; falling back to system app');
82
+ } else {
83
+ try {
84
+ const address = (args.fileAddressFor ?? fileAddressFor)(args.sessionId, args.cwd, args.path);
85
+ args.openSidebar(address);
86
+ return 'sidebar';
87
+ } catch (error) {
88
+ args.warn?.('[dsh-better-display] sidebar open failed; falling back to system app', error);
89
+ }
90
+ }
91
+ }
92
+ await args.openExternal(targetPath);
93
+ return 'external';
94
+ }
@@ -0,0 +1,49 @@
1
+ /** Official PendingSubmission.images is optional at runtime; text-only sends omit it. */
2
+
3
+ export interface PendingSubmissionImage {
4
+ readonly previewUrl: string;
5
+ readonly width?: number;
6
+ readonly height?: number;
7
+ readonly name?: string;
8
+ }
9
+
10
+ export interface PendingSubmissionLike {
11
+ readonly images?: readonly PendingSubmissionImage[] | null;
12
+ readonly attachments?: readonly {
13
+ readonly type?: string;
14
+ readonly value?: PendingSubmissionImage;
15
+ }[] | null;
16
+ }
17
+
18
+ export interface PendingSubmissionEcho extends PendingSubmissionLike {
19
+ readonly requestId: string;
20
+ readonly text?: string;
21
+ readonly time?: number;
22
+ readonly placement?: string;
23
+ }
24
+
25
+ export function asReadonlyArray<T>(value: unknown): readonly T[] {
26
+ return Array.isArray(value) ? value as readonly T[] : [];
27
+ }
28
+
29
+ function isImage(value: unknown): value is PendingSubmissionImage {
30
+ return value !== null && typeof value === 'object' && typeof (value as PendingSubmissionImage).previewUrl === 'string';
31
+ }
32
+
33
+ /**
34
+ * Safe image list for conversation.view pending echoes.
35
+ * Official 0.1.5 uses `images`; older hosts used `attachments`.
36
+ * Either field may be missing — never read `.length` on undefined.
37
+ */
38
+ export function pendingSubmissionImages(submission: PendingSubmissionLike | null | undefined): readonly PendingSubmissionImage[] {
39
+ if (submission == null) return [];
40
+ if (Array.isArray(submission.images)) return submission.images.filter(isImage);
41
+ if (Array.isArray(submission.attachments)) {
42
+ const images: PendingSubmissionImage[] = [];
43
+ for (const item of submission.attachments) {
44
+ if (item?.type === 'image' && isImage(item.value)) images.push(item.value);
45
+ }
46
+ return images;
47
+ }
48
+ return [];
49
+ }
@@ -0,0 +1,53 @@
1
+ import type {
2
+ DiffBlockLabels,
3
+ JsonTreeLabels,
4
+ MarkdownLabels,
5
+ ReadBlockLabels,
6
+ SearchBlockLabels,
7
+ TerminalBlockLabels,
8
+ WebBlockLabels,
9
+ } from '@deepseek-ai/dsh-client-ui-primitives';
10
+
11
+ export const markdownLabels: MarkdownLabels = {
12
+ code: { copyLabel: '复制', copiedLabel: '已复制' },
13
+ footnotes: '脚注',
14
+ };
15
+
16
+ export const readBlockLabels: ReadBlockLabels = {
17
+ window: (shown, total) => `显示 ${shown} / ${total} 行`,
18
+ copy: '复制', copied: '已复制', collapseAria: '收起文件内容',
19
+ expandAria: hidden => `展开其余 ${hidden} 行`, collapse: '收起', expand: hidden => `展开其余 ${hidden} 行`,
20
+ };
21
+
22
+ export const terminalBlockLabels: TerminalBlockLabels = {
23
+ signal: signal => `信号 ${signal}`, exitCode: code => `退出码 ${code}`,
24
+ running: '执行中', failed: '失败', done: '已完成', copy: '复制', copied: '已复制',
25
+ noOutput: '没有输出', collapseAria: '收起命令输出', collapse: '收起',
26
+ expandAria: hidden => `展开其余 ${hidden} 行`, expand: hidden => `展开其余 ${hidden} 行`,
27
+ };
28
+
29
+ export const diffBlockLabels: DiffBlockLabels = {
30
+ copy: '复制', copied: '已复制', collapseAria: '收起差异', collapse: '收起',
31
+ expandAria: hidden => `展开其余 ${hidden} 行`, expand: hidden => `展开其余 ${hidden} 行`,
32
+ files: count => `${count} 个文件`,
33
+ };
34
+
35
+ export const searchBlockLabels: SearchBlockLabels = {
36
+ pathsSummary: (shown, total, truncated) => `${shown} / ${total} 个路径${truncated ? '(结果已截断)' : ''}`,
37
+ matchesSummary: (shown, total, files, truncated) => `${shown} / ${total} 处匹配 · ${files} 个文件${truncated ? '(结果已截断)' : ''}`,
38
+ copy: '复制', copied: '已复制', noResults: '没有结果', collapseAria: '收起搜索结果', collapse: '收起',
39
+ expandAria: hidden => `展开其余 ${hidden} 行`, expand: hidden => `展开其余 ${hidden} 行`,
40
+ };
41
+
42
+ export const webBlockLabels: WebBlockLabels = {
43
+ noResults: '没有结果', sourcesTruncated: '来源已截断', http: 'HTTP', contentTruncated: '内容已截断',
44
+ markdown: markdownLabels,
45
+ };
46
+
47
+ export const jsonTreeLabels: JsonTreeLabels = {
48
+ copyValue: '复制值', copyJson: '复制 JSON', copyPath: '复制路径', copyPrettyJson: '复制格式化 JSON',
49
+ copyCompactJson: '复制紧凑 JSON', copied: '已复制', copyFailed: '复制失败', collapseNode: '收起节点',
50
+ expandNode: '展开节点', copyButtonTitle: action => action,
51
+ };
52
+
53
+ export const truncatedJsonLabel = (total: number): string => `内容过长,已截断(共 ${total} 个字符)`;