@deepseek-harness-tui/dsh-tui 0.9.3 → 0.10.0-beta.2

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 (267) hide show
  1. package/README.md +24 -6
  2. package/cordis.patch.yml +117 -39
  3. package/cordis.yml +13 -10
  4. package/lib/types/api.d.ts +7 -0
  5. package/lib/types/api.d.ts.map +1 -0
  6. package/lib/types/api.js +1 -0
  7. package/lib/types/cc/hyperlink.d.ts.map +1 -1
  8. package/lib/types/cc/hyperlink.js +42 -4
  9. package/lib/types/cc/markdown.d.ts.map +1 -1
  10. package/lib/types/cc/markdown.js +15 -5
  11. package/lib/types/commands.d.ts +6 -0
  12. package/lib/types/commands.d.ts.map +1 -1
  13. package/lib/types/commands.js +12 -8
  14. package/lib/types/components/MessageList.d.ts +8 -4
  15. package/lib/types/components/MessageList.d.ts.map +1 -1
  16. package/lib/types/components/MessageList.js +94 -32
  17. package/lib/types/components/PermissionsPicker.d.ts +6 -7
  18. package/lib/types/components/PermissionsPicker.d.ts.map +1 -1
  19. package/lib/types/components/PermissionsPicker.js +20 -14
  20. package/lib/types/components/PromptEditor.d.ts +46 -0
  21. package/lib/types/components/PromptEditor.d.ts.map +1 -0
  22. package/lib/types/components/PromptEditor.js +60 -0
  23. package/lib/types/components/PromptInput.d.ts +12 -0
  24. package/lib/types/components/PromptInput.d.ts.map +1 -1
  25. package/lib/types/components/PromptInput.js +1240 -205
  26. package/lib/types/components/RewindPicker.js +1 -1
  27. package/lib/types/components/SplitDiffView.d.ts +11 -1
  28. package/lib/types/components/SplitDiffView.d.ts.map +1 -1
  29. package/lib/types/components/SplitDiffView.js +9 -2
  30. package/lib/types/components/StreamingMarkdown.d.ts.map +1 -1
  31. package/lib/types/components/StreamingMarkdown.js +140 -3
  32. package/lib/types/components/SubagentDashboard.d.ts.map +1 -1
  33. package/lib/types/components/SubagentDashboard.js +2 -2
  34. package/lib/types/components/SubagentDetailScene.js +1 -1
  35. package/lib/types/components/ThemePicker.d.ts +13 -15
  36. package/lib/types/components/ThemePicker.d.ts.map +1 -1
  37. package/lib/types/components/ThemePicker.js +24 -26
  38. package/lib/types/components/Tooltip.d.ts +67 -0
  39. package/lib/types/components/Tooltip.d.ts.map +1 -0
  40. package/lib/types/components/Tooltip.js +220 -0
  41. package/lib/types/components/approvals/ApprovalPanel.d.ts.map +1 -1
  42. package/lib/types/components/approvals/ApprovalPanel.js +1 -1
  43. package/lib/types/components/design-system/Divider.d.ts +14 -1
  44. package/lib/types/components/design-system/Divider.d.ts.map +1 -1
  45. package/lib/types/components/design-system/Divider.js +86 -8
  46. package/lib/types/components/design-system/ThemeProvider.d.ts +5 -2
  47. package/lib/types/components/design-system/ThemeProvider.d.ts.map +1 -1
  48. package/lib/types/components/design-system/ThemeProvider.js +52 -16
  49. package/lib/types/components/design-system/ThemedBox.d.ts +23 -2
  50. package/lib/types/components/design-system/ThemedBox.d.ts.map +1 -1
  51. package/lib/types/components/messages/AssistantThinkingMessage.d.ts +11 -6
  52. package/lib/types/components/messages/AssistantThinkingMessage.d.ts.map +1 -1
  53. package/lib/types/components/messages/AssistantThinkingMessage.js +10 -6
  54. package/lib/types/components/messages/AssistantToolUseMessage.d.ts +13 -1
  55. package/lib/types/components/messages/AssistantToolUseMessage.d.ts.map +1 -1
  56. package/lib/types/components/messages/AssistantToolUseMessage.js +37 -6
  57. package/lib/types/components/messages/UserPromptMessage.d.ts.map +1 -1
  58. package/lib/types/components/messages/UserPromptMessage.js +7 -1
  59. package/lib/types/components/questions/AskUserQuestionPanel.js +1 -1
  60. package/lib/types/components/questions/PlanReviewPanel.d.ts.map +1 -1
  61. package/lib/types/components/questions/PlanReviewPanel.js +29 -3
  62. package/lib/types/components/sessions/SessionListRow.d.ts +8 -1
  63. package/lib/types/components/sessions/SessionListRow.d.ts.map +1 -1
  64. package/lib/types/components/sessions/SessionListRow.js +9 -2
  65. package/lib/types/components/sessions/SessionPreview.d.ts.map +1 -1
  66. package/lib/types/components/sessions/SessionPreview.js +5 -3
  67. package/lib/types/components/sessions/WorkspaceListRow.d.ts +17 -0
  68. package/lib/types/components/sessions/WorkspaceListRow.d.ts.map +1 -0
  69. package/lib/types/components/sessions/WorkspaceListRow.js +27 -0
  70. package/lib/types/components/smoothReveal.d.ts +97 -0
  71. package/lib/types/components/smoothReveal.d.ts.map +1 -0
  72. package/lib/types/components/smoothReveal.js +279 -0
  73. package/lib/types/customTheme.d.ts +4 -0
  74. package/lib/types/customTheme.d.ts.map +1 -1
  75. package/lib/types/customTheme.js +9 -5
  76. package/lib/types/dsh-adapter/approvals.d.ts +114 -2
  77. package/lib/types/dsh-adapter/approvals.d.ts.map +1 -1
  78. package/lib/types/dsh-adapter/approvals.js +241 -3
  79. package/lib/types/dsh-adapter/channel.d.ts +59 -2
  80. package/lib/types/dsh-adapter/channel.d.ts.map +1 -1
  81. package/lib/types/dsh-adapter/channel.js +499 -80
  82. package/lib/types/dsh-adapter/childStderr.d.ts.map +1 -1
  83. package/lib/types/dsh-adapter/childStderr.js +9 -0
  84. package/lib/types/dsh-adapter/contract.d.ts +17 -13
  85. package/lib/types/dsh-adapter/contract.d.ts.map +1 -1
  86. package/lib/types/dsh-adapter/contract.js +36 -27
  87. package/lib/types/dsh-adapter/credentialRefGuard.d.ts +40 -0
  88. package/lib/types/dsh-adapter/credentialRefGuard.d.ts.map +1 -0
  89. package/lib/types/dsh-adapter/credentialRefGuard.js +43 -0
  90. package/lib/types/dsh-adapter/effect-ledger.d.ts +1 -1
  91. package/lib/types/dsh-adapter/effect-ledger.d.ts.map +1 -1
  92. package/lib/types/dsh-adapter/effect-ledger.js +1 -0
  93. package/lib/types/dsh-adapter/extensions.d.ts +4 -1
  94. package/lib/types/dsh-adapter/extensions.d.ts.map +1 -1
  95. package/lib/types/dsh-adapter/extensions.js +8 -1
  96. package/lib/types/dsh-adapter/index.d.ts +11 -1
  97. package/lib/types/dsh-adapter/index.d.ts.map +1 -1
  98. package/lib/types/dsh-adapter/index.js +2 -0
  99. package/lib/types/dsh-adapter/plugin-test-utils.d.ts +62 -0
  100. package/lib/types/dsh-adapter/plugin-test-utils.d.ts.map +1 -0
  101. package/lib/types/dsh-adapter/plugin-test-utils.js +61 -0
  102. package/lib/types/dsh-adapter/plugin.d.ts +20 -0
  103. package/lib/types/dsh-adapter/plugin.d.ts.map +1 -1
  104. package/lib/types/dsh-adapter/plugin.js +174 -39
  105. package/lib/types/dsh-adapter/plugins-info.d.ts.map +1 -1
  106. package/lib/types/dsh-adapter/plugins-info.js +12 -0
  107. package/lib/types/dsh-adapter/preset-resolution.d.ts +49 -0
  108. package/lib/types/dsh-adapter/preset-resolution.d.ts.map +1 -0
  109. package/lib/types/dsh-adapter/preset-resolution.js +42 -0
  110. package/lib/types/dsh-adapter/presets.d.ts +0 -26
  111. package/lib/types/dsh-adapter/presets.d.ts.map +1 -1
  112. package/lib/types/dsh-adapter/presets.js +4 -14
  113. package/lib/types/dsh-adapter/promptDebug.d.ts.map +1 -1
  114. package/lib/types/dsh-adapter/promptDebug.js +4 -1
  115. package/lib/types/dsh-adapter/providerGuard.d.ts +75 -0
  116. package/lib/types/dsh-adapter/providerGuard.d.ts.map +1 -0
  117. package/lib/types/dsh-adapter/providerGuard.js +104 -0
  118. package/lib/types/dsh-adapter/questions-answerer.d.ts +49 -0
  119. package/lib/types/dsh-adapter/questions-answerer.d.ts.map +1 -0
  120. package/lib/types/dsh-adapter/questions-answerer.js +61 -0
  121. package/lib/types/dsh-adapter/recap.d.ts.map +1 -1
  122. package/lib/types/dsh-adapter/recap.js +12 -6
  123. package/lib/types/dsh-adapter/sanitize.d.ts.map +1 -1
  124. package/lib/types/dsh-adapter/sanitize.js +6 -1
  125. package/lib/types/dsh-adapter/sessions/digest.d.ts +21 -1
  126. package/lib/types/dsh-adapter/sessions/digest.d.ts.map +1 -1
  127. package/lib/types/dsh-adapter/sessions/digest.js +229 -3
  128. package/lib/types/dsh-adapter/sessions/frames.d.ts +3 -0
  129. package/lib/types/dsh-adapter/sessions/frames.d.ts.map +1 -1
  130. package/lib/types/dsh-adapter/sessions/frames.js +34 -19
  131. package/lib/types/dsh-adapter/sessions/list.d.ts.map +1 -1
  132. package/lib/types/dsh-adapter/sessions/list.js +148 -31
  133. package/lib/types/dsh-adapter/sessions/store.d.ts +8 -0
  134. package/lib/types/dsh-adapter/sessions/store.d.ts.map +1 -1
  135. package/lib/types/dsh-adapter/sessions/store.js +24 -4
  136. package/lib/types/dsh-adapter/sessions/types.d.ts +3 -0
  137. package/lib/types/dsh-adapter/sessions/types.d.ts.map +1 -1
  138. package/lib/types/dsh-adapter/settings-sections.d.ts.map +1 -1
  139. package/lib/types/dsh-adapter/settings-sections.js +13 -1
  140. package/lib/types/dsh-adapter/settingsEditor.d.ts +6 -0
  141. package/lib/types/dsh-adapter/settingsEditor.d.ts.map +1 -1
  142. package/lib/types/dsh-adapter/settingsEditor.js +15 -3
  143. package/lib/types/dsh-adapter/subagents.d.ts +4 -0
  144. package/lib/types/dsh-adapter/subagents.d.ts.map +1 -1
  145. package/lib/types/dsh-adapter/subagents.js +8 -0
  146. package/lib/types/dsh-adapter/themes.d.ts +51 -0
  147. package/lib/types/dsh-adapter/themes.d.ts.map +1 -0
  148. package/lib/types/dsh-adapter/themes.js +272 -0
  149. package/lib/types/dsh-adapter/toast.d.ts +67 -0
  150. package/lib/types/dsh-adapter/toast.d.ts.map +1 -0
  151. package/lib/types/dsh-adapter/toast.js +141 -0
  152. package/lib/types/dsh-adapter/types.d.ts +1 -1
  153. package/lib/types/extensions.d.ts +4 -0
  154. package/lib/types/extensions.d.ts.map +1 -1
  155. package/lib/types/extensions.js +5 -1
  156. package/lib/types/history.d.ts.map +1 -1
  157. package/lib/types/history.js +7 -2
  158. package/lib/types/hooks/useRuntimeThemeSnapshot.d.ts +10 -0
  159. package/lib/types/hooks/useRuntimeThemeSnapshot.d.ts.map +1 -0
  160. package/lib/types/hooks/useRuntimeThemeSnapshot.js +32 -0
  161. package/lib/types/i18n.d.ts +199 -41
  162. package/lib/types/i18n.d.ts.map +1 -1
  163. package/lib/types/i18n.js +82 -20
  164. package/lib/types/ink/components/App.d.ts +20 -0
  165. package/lib/types/ink/components/App.d.ts.map +1 -1
  166. package/lib/types/ink/components/App.js +142 -13
  167. package/lib/types/ink/components/Box.d.ts +39 -2
  168. package/lib/types/ink/components/Box.d.ts.map +1 -1
  169. package/lib/types/ink/components/Box.js +61 -37
  170. package/lib/types/ink/events/context-menu-event.d.ts +19 -0
  171. package/lib/types/ink/events/context-menu-event.d.ts.map +1 -0
  172. package/lib/types/ink/events/context-menu-event.js +20 -0
  173. package/lib/types/ink/events/drag-event.d.ts +44 -0
  174. package/lib/types/ink/events/drag-event.d.ts.map +1 -0
  175. package/lib/types/ink/events/drag-event.js +39 -0
  176. package/lib/types/ink/events/event-handlers.d.ts +8 -0
  177. package/lib/types/ink/events/event-handlers.d.ts.map +1 -1
  178. package/lib/types/ink/events/event-handlers.js +8 -0
  179. package/lib/types/ink/hit-test.d.ts +69 -0
  180. package/lib/types/ink/hit-test.d.ts.map +1 -1
  181. package/lib/types/ink/hit-test.js +260 -6
  182. package/lib/types/ink/ink.d.ts +25 -0
  183. package/lib/types/ink/ink.d.ts.map +1 -1
  184. package/lib/types/ink/ink.js +74 -10
  185. package/lib/types/ink/parse-keypress.d.ts +20 -0
  186. package/lib/types/ink/parse-keypress.d.ts.map +1 -1
  187. package/lib/types/ink/parse-keypress.js +190 -22
  188. package/lib/types/ink/renderer.d.ts.map +1 -1
  189. package/lib/types/ink/renderer.js +15 -1
  190. package/lib/types/ink/screen.d.ts +7 -0
  191. package/lib/types/ink/screen.d.ts.map +1 -1
  192. package/lib/types/ink/screen.js +8 -1
  193. package/lib/types/ink/termio/osc.d.ts +2 -1
  194. package/lib/types/ink/termio/osc.d.ts.map +1 -1
  195. package/lib/types/ink/termio/osc.js +26 -5
  196. package/lib/types/plugin-spec/registry.d.ts +1 -1
  197. package/lib/types/plugin-spec/registry.js +1 -1
  198. package/lib/types/presetPrefs.d.ts +6 -5
  199. package/lib/types/presetPrefs.d.ts.map +1 -1
  200. package/lib/types/presetPrefs.js +10 -5
  201. package/lib/types/screens/Chat.d.ts +4 -1
  202. package/lib/types/screens/Chat.d.ts.map +1 -1
  203. package/lib/types/screens/Chat.js +169 -68
  204. package/lib/types/screens/SessionBrowser.d.ts +9 -4
  205. package/lib/types/screens/SessionBrowser.d.ts.map +1 -1
  206. package/lib/types/screens/SessionBrowser.js +483 -75
  207. package/lib/types/screens/SessionTree.d.ts.map +1 -1
  208. package/lib/types/screens/SessionTree.js +5 -1
  209. package/lib/types/screens/Settings.d.ts.map +1 -1
  210. package/lib/types/screens/Settings.js +5 -1
  211. package/lib/types/screens/StatusLine.d.ts.map +1 -1
  212. package/lib/types/screens/StatusLine.js +5 -1
  213. package/lib/types/screens/chatOverlay.d.ts +3 -2
  214. package/lib/types/screens/chatOverlay.d.ts.map +1 -1
  215. package/lib/types/screens/chatOverlay.js +2 -0
  216. package/lib/types/sessionPins.d.ts +33 -0
  217. package/lib/types/sessionPins.d.ts.map +1 -0
  218. package/lib/types/sessionPins.js +181 -0
  219. package/lib/types/sessions/format.d.ts +2 -0
  220. package/lib/types/sessions/format.d.ts.map +1 -1
  221. package/lib/types/sessions/format.js +9 -0
  222. package/lib/types/sessions/view.d.ts +42 -6
  223. package/lib/types/sessions/view.d.ts.map +1 -1
  224. package/lib/types/sessions/view.js +133 -35
  225. package/lib/types/test-utils.d.ts +2 -0
  226. package/lib/types/test-utils.d.ts.map +1 -0
  227. package/lib/types/test-utils.js +8 -0
  228. package/lib/types/theme.d.ts +22 -5
  229. package/lib/types/theme.d.ts.map +1 -1
  230. package/lib/types/theme.js +57 -7
  231. package/lib/types/themeCatalog.d.ts +36 -0
  232. package/lib/types/themeCatalog.d.ts.map +1 -0
  233. package/lib/types/themeCatalog.js +157 -0
  234. package/lib/types/tips.d.ts.map +1 -1
  235. package/lib/types/tips.js +114 -18
  236. package/lib/types/update.d.ts +159 -3
  237. package/lib/types/update.d.ts.map +1 -1
  238. package/lib/types/update.js +497 -16
  239. package/lib/types/utils/debug.d.ts.map +1 -1
  240. package/lib/types/utils/debug.js +2 -1
  241. package/lib/types/utils/keymap.d.ts +1 -1
  242. package/lib/types/utils/keymap.d.ts.map +1 -1
  243. package/lib/types/utils/keymap.js +19 -2
  244. package/lib/types/utils/urlGuard.d.ts +28 -0
  245. package/lib/types/utils/urlGuard.d.ts.map +1 -0
  246. package/lib/types/utils/urlGuard.js +21 -0
  247. package/node_modules/@deepseek-harness-tui/dsh-auth/README.md +3 -0
  248. package/node_modules/@deepseek-harness-tui/dsh-auth/lib/credentials.d.ts +7 -11
  249. package/node_modules/@deepseek-harness-tui/dsh-auth/lib/index.d.ts +16 -1
  250. package/node_modules/@deepseek-harness-tui/dsh-auth/lib/index.js +23 -2
  251. package/node_modules/@deepseek-harness-tui/dsh-auth/lib/interaction.d.ts +5 -5
  252. package/node_modules/@deepseek-harness-tui/dsh-auth/lib/pi-ai.d.ts +23 -0
  253. package/node_modules/@deepseek-harness-tui/dsh-auth/lib/pi-ai.js +27 -0
  254. package/node_modules/@deepseek-harness-tui/dsh-auth/lib/profiles.d.ts +17 -3
  255. package/node_modules/@deepseek-harness-tui/dsh-auth/lib/profiles.js +36 -4
  256. package/node_modules/@deepseek-harness-tui/dsh-auth/package.json +11 -12
  257. package/package.json +114 -74
  258. package/lib/types/dsh-adapter/packaged-skills.d.ts +0 -19
  259. package/lib/types/dsh-adapter/packaged-skills.d.ts.map +0 -1
  260. package/lib/types/dsh-adapter/packaged-skills.js +0 -78
  261. package/skills/audit/SKILL.md +0 -24
  262. package/skills/bug/SKILL.md +0 -27
  263. package/skills/pr-comments/SKILL.md +0 -20
  264. package/skills/practice/SKILL.md +0 -22
  265. package/skills/release-notes/SKILL.md +0 -25
  266. package/skills/review/SKILL.md +0 -25
  267. package/skills/vuln-check/SKILL.md +0 -25
@@ -44,7 +44,7 @@ export function RewindPicker({ rows, focusIndex, confirmRow, modes = null, modeI
44
44
  //(ListItem 保证单行截断)。rewind 是不可见确认的高危操作,焦点必须
45
45
  // 始终在屏。框架行:浮层预留 8 + Pane 2 + 标题块 3 + 页脚 1 = 14。
46
46
  const { start, end } = listWindow(rows.map((_, i) => (i === 0 ? 2 : 1)), focusIndex, Math.max(terminalRows - 14, 2));
47
- return (_jsxs(Pane, { color: "permission", children: [_jsxs(Box, { flexDirection: "column", children: [_jsxs(Box, { marginBottom: 1, children: [_jsx(Text, { color: "remember", bold: true, children: t('rewind-title') }), _jsx(Text, { dimColor: true, children: t('rewind-subtitle') })] }), rows.length === 0 ? (_jsx(ListItem, { isFocused: false, children: t('rewind-empty') })) : (rows.slice(start, end).map((row, index) => {
47
+ return (_jsxs(Pane, { color: "permission", children: [_jsxs(Box, { flexDirection: "column", children: [_jsxs(Box, { marginBottom: 1, flexDirection: "column", children: [_jsx(Text, { color: "remember", bold: true, children: t('rewind-title') }), _jsx(Text, { dimColor: true, children: t('rewind-subtitle') })] }), rows.length === 0 ? (_jsx(ListItem, { isFocused: false, children: t('rewind-empty') })) : (rows.slice(start, end).map((row, index) => {
48
48
  const absoluteIndex = start + index;
49
49
  return (_jsx(ListItem, { isFocused: absoluteIndex === focusIndex, description: absoluteIndex === 0 ? t('rewind-last-message') : undefined, showScrollUp: absoluteIndex === start && start > 0, showScrollDown: absoluteIndex === end - 1 && end < rows.length, onClick: onPickRow !== undefined && !busy
50
50
  ? () => onPickRow(absoluteIndex)
@@ -3,7 +3,7 @@ import type { ToolFileDiff } from '../dsh-adapter/channel.js';
3
3
  export { chalkFromToken } from '../cc/syntaxTheme.js';
4
4
  export { parseAnsiRuns, highlightLines } from '../cc/syntaxRuns.js';
5
5
  import type { ToolBackground } from '../tuiDisplayPrefs.js';
6
- export declare function SplitDiffView({ diffs, width, maxRows, verbose, toolBackground, }: {
6
+ export declare function SplitDiffView({ diffs, width, maxRows, verbose, toolBackground, reveal, }: {
7
7
  readonly diffs: readonly ToolFileDiff[];
8
8
  /** Content width available to the whole two-pane block (divider included). */
9
9
  readonly width: number;
@@ -11,5 +11,15 @@ export declare function SplitDiffView({ diffs, width, maxRows, verbose, toolBack
11
11
  readonly maxRows: number;
12
12
  readonly verbose: boolean;
13
13
  readonly toolBackground?: ToolBackground;
14
+ /**
15
+ * Smooth-streaming participation (the card owning this view computes
16
+ * eligibility): when present, the capped row list reveals line-by-line at
17
+ * the shared ~30fps cadence instead of painting as one block. The `+N
18
+ * lines` fold hint stays rendered throughout — it describes the cap, not
19
+ * the reveal.
20
+ */
21
+ readonly reveal?: {
22
+ readonly key: string;
23
+ };
14
24
  }): React.ReactNode;
15
25
  //# sourceMappingURL=SplitDiffView.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"SplitDiffView.d.ts","sourceRoot":"","sources":["../../../src/components/SplitDiffView.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AAIzB,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAA;AAO7D,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAA;AACrD,OAAO,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAA;AAGnE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAA;AAuR3D,wBAAgB,aAAa,CAAC,EAC5B,KAAK,EACL,KAAK,EACL,OAAO,EACP,OAAO,EACP,cAAuB,GACxB,EAAE;IACD,QAAQ,CAAC,KAAK,EAAE,SAAS,YAAY,EAAE,CAAA;IACvC,8EAA8E;IAC9E,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,qEAAqE;IACrE,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;IACxB,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAA;IACzB,QAAQ,CAAC,cAAc,CAAC,EAAE,cAAc,CAAA;CACzC,GAAG,KAAK,CAAC,SAAS,CAkFlB"}
1
+ {"version":3,"file":"SplitDiffView.d.ts","sourceRoot":"","sources":["../../../src/components/SplitDiffView.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AAIzB,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAA;AAO7D,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAA;AACrD,OAAO,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAA;AAGnE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAA;AAwR3D,wBAAgB,aAAa,CAAC,EAC5B,KAAK,EACL,KAAK,EACL,OAAO,EACP,OAAO,EACP,cAAuB,EACvB,MAAM,GACP,EAAE;IACD,QAAQ,CAAC,KAAK,EAAE,SAAS,YAAY,EAAE,CAAA;IACvC,8EAA8E;IAC9E,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,qEAAqE;IACrE,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;IACxB,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAA;IACzB,QAAQ,CAAC,cAAc,CAAC,EAAE,cAAc,CAAA;IACxC;;;;;;OAMG;IACH,QAAQ,CAAC,MAAM,CAAC,EAAE;QAAE,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;KAAE,CAAA;CAC3C,GAAG,KAAK,CAAC,SAAS,CAwFlB"}
@@ -11,6 +11,7 @@ export { chalkFromToken } from '../cc/syntaxTheme.js';
11
11
  export { parseAnsiRuns, highlightLines } from '../cc/syntaxRuns.js';
12
12
  import { getTheme } from '../theme.js';
13
13
  import { useTheme } from './design-system/ThemeProvider.js';
14
+ import { revealLinesOf } from './smoothReveal.js';
14
15
  /** Replace tabs so width math and alignment hold (pi convention). */
15
16
  const expandTabs = (text) => text.replaceAll('\t', ' ');
16
17
  /**
@@ -210,7 +211,7 @@ function PaneLine({ side, kind, width, tone, toolBackground, padLeft = false, })
210
211
  const prefix = padLeft ? ` ${marker}` : marker;
211
212
  return (_jsxs(Box, { width: width, flexShrink: 0, backgroundColor: backgroundColor, children: [_jsx(Text, { dimColor: true, backgroundColor: backgroundColor, children: `${prefix} ` }), side === undefined ? (_jsx(Text, { backgroundColor: backgroundColor, children: " " })) : (_jsx(Text, { wrap: "truncate", backgroundColor: backgroundColor, children: side.segments.map((segment, index) => segment.changed ? (_jsx(Text, { color: wordColor, bold: true, backgroundColor: backgroundColor, children: segment.text }, index)) : (_jsx(Text, { color: segment.color, backgroundColor: backgroundColor, children: segment.text }, index))) }))] }));
212
213
  }
213
- export function SplitDiffView({ diffs, width, maxRows, verbose, toolBackground = 'none', }) {
214
+ export function SplitDiffView({ diffs, width, maxRows, verbose, toolBackground = 'none', reveal, }) {
214
215
  const [hl, setHl] = React.useState(null);
215
216
  React.useEffect(() => {
216
217
  let mounted = true;
@@ -243,6 +244,12 @@ export function SplitDiffView({ diffs, width, maxRows, verbose, toolBackground =
243
244
  const capped = verbose || totalRows <= maxRows || totalRows - maxRows === 1;
244
245
  const visible = capped ? rows : rows.slice(0, maxRows);
245
246
  const hidden = totalRows - visible.length;
247
+ // Smooth reveal reads happen during render (the owning card subscribes to
248
+ // the scheduler's version, so its re-render drives this view too).
249
+ const revealedRows = reveal !== undefined
250
+ ? revealLinesOf(reveal.key, visible.length, { enabled: true, active: true })
251
+ : visible.length;
252
+ const shown = revealedRows >= visible.length ? visible : visible.slice(0, revealedRows);
246
253
  const paneWidth = Math.max(20, Math.floor((width - 1) / 2));
247
254
  // Whole-hunk highlight per file (multi-line lexer state preserved);
248
255
  // only the visible rows merge syntax runs with word flags below.
@@ -253,7 +260,7 @@ export function SplitDiffView({ diffs, width, maxRows, verbose, toolBackground =
253
260
  next: highlightLines(expandTabs(diff.newText), language, hl, syntaxTheme, themeSig),
254
261
  };
255
262
  });
256
- return (_jsxs(Box, { flexDirection: "column", width: paneWidth * 2 + 1, children: [visible.map((row, index) => {
263
+ return (_jsxs(Box, { flexDirection: "column", width: paneWidth * 2 + 1, children: [shown.map((row, index) => {
257
264
  if ('separator' in row) {
258
265
  return (_jsx(Box, { width: paneWidth * 2 + 1, children: _jsx(Text, { dimColor: true, wrap: "truncate", children: row.separator === '⋯' ? '⋯' : ` ${row.separator}` }) }, index));
259
266
  }
@@ -1 +1 @@
1
- {"version":3,"file":"StreamingMarkdown.d.ts","sourceRoot":"","sources":["../../../src/components/StreamingMarkdown.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AAmDzB,wBAAgB,iBAAiB,CAAC,EAChC,QAAQ,EACR,QAAgB,GACjB,EAAE;IACD,QAAQ,EAAE,MAAM,CAAA;IAChB,QAAQ,CAAC,EAAE,OAAO,CAAA;CACnB,GAAG,KAAK,CAAC,SAAS,CAmDlB"}
1
+ {"version":3,"file":"StreamingMarkdown.d.ts","sourceRoot":"","sources":["../../../src/components/StreamingMarkdown.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AAmLzB,wBAAgB,iBAAiB,CAAC,EAChC,QAAQ,EACR,QAAgB,GACjB,EAAE;IACD,QAAQ,EAAE,MAAM,CAAA;IAChB,QAAQ,CAAC,EAAE,OAAO,CAAA;CACnB,GAAG,KAAK,CAAC,SAAS,CAkFlB"}
@@ -2,7 +2,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import React from 'react';
3
3
  import { marked } from 'marked';
4
4
  import Box from '../ink/components/Box.js';
5
- import { stripPromptXMLTags } from '../cc/markdown.js';
5
+ import { formatToken, stripPromptXMLTags } from '../cc/markdown.js';
6
6
  import { t } from '../i18n.js';
7
7
  import { Markdown } from './Markdown.js';
8
8
  /**
@@ -47,6 +47,113 @@ function clipSuffixTail(suffix, cut) {
47
47
  const dropped = cut.current;
48
48
  return `${t('streaming-folded', { count: dropped })}\n\n${suffix.slice(cut.current)}`;
49
49
  }
50
+ const UNSAFE_BOUNDARY = {
51
+ safe: false,
52
+ gap: 0,
53
+ endsWithTable: false,
54
+ trailingEmptyTextNode: false,
55
+ };
56
+ /**
57
+ * Token types whose formatted output is provably blank, taken from
58
+ * `cc/markdown.ts`'s dispatcher table: `space`/`br` emit a single newline,
59
+ * `def`/`del`/`html` emit nothing. Kept in step with `analyzeSuffixStart`,
60
+ * which splits the same set into "newline" and "empty" halves.
61
+ */
62
+ function isBlankTokenType(type) {
63
+ return type === 'space' || type === 'br' || type === 'def' || type === 'del' || type === 'html';
64
+ }
65
+ /** Newlines contributed by a blank token: one for `space`/`br`, none otherwise. */
66
+ function blankTokenNewlines(type) {
67
+ return type === 'space' || type === 'br' ? 1 : 0;
68
+ }
69
+ /**
70
+ * Analyze the candidate stable tokens using the same formatter as Markdown.
71
+ * Both split halves trim their outer whitespace, so the trailing newline count
72
+ * determines the Yoga gap: one newline merely starts the next row; every
73
+ * additional newline is one genuinely blank row. Tables are separate layout
74
+ * nodes and therefore keep Markdown's fixed one-row node gap.
75
+ *
76
+ * Only the LAST token that can produce visible text decides the outcome, so
77
+ * the region is walked backwards and formatted one token at a time instead of
78
+ * concatenating (and syntax-highlighting) every settled block on each new
79
+ * boundary. `formatToken` runs once for the common case.
80
+ */
81
+ function analyzeStableBoundary(tokens, suffixIndex) {
82
+ // A table resets the accumulated text, so only the segment after the last
83
+ // one matters; anything before it is already folded into the table branch.
84
+ let segmentStart = 0;
85
+ let hasTable = false;
86
+ for (let i = suffixIndex - 1; i >= 0; i--) {
87
+ if (tokens[i].type === 'table') {
88
+ segmentStart = i + 1;
89
+ hasTable = true;
90
+ break;
91
+ }
92
+ }
93
+ // Skip the trailing run of blank tokens, banking their newlines: they sit
94
+ // after the last visible text and therefore only extend its newline run.
95
+ let blankNewlines = 0;
96
+ let cursor = suffixIndex - 1;
97
+ while (cursor >= segmentStart && isBlankTokenType(tokens[cursor].type)) {
98
+ blankNewlines += blankTokenNewlines(tokens[cursor].type);
99
+ cursor--;
100
+ }
101
+ // Format backwards until a token actually produces visible text. A token
102
+ // that formats to blank is folded into the newline run and skipped over.
103
+ for (let i = cursor; i >= segmentStart; i--) {
104
+ const token = tokens[i];
105
+ const ansiText = formatToken(token);
106
+ if (ansiText.trim() === '') {
107
+ blankNewlines += ansiText.match(/\n+$/)?.[0].length ?? 0;
108
+ continue;
109
+ }
110
+ const trailingNewlines = (ansiText.match(/\n+$/)?.[0].length ?? 0) + blankNewlines;
111
+ // A zero-newline boundary (notably `hr` followed immediately by prose)
112
+ // belongs in one Markdown component; separate column children would force
113
+ // a line break that the whole-document formatter does not contain.
114
+ if (trailingNewlines === 0)
115
+ return UNSAFE_BOUNDARY;
116
+ return {
117
+ safe: true,
118
+ gap: Math.max(0, trailingNewlines - 1),
119
+ endsWithTable: false,
120
+ trailingEmptyTextNode: false,
121
+ };
122
+ }
123
+ if (hasTable) {
124
+ return {
125
+ safe: true,
126
+ gap: 1,
127
+ endsWithTable: true,
128
+ // Whitespace after the table still occupies a zero-height text node
129
+ // between two tables.
130
+ trailingEmptyTextNode: blankNewlines > 0,
131
+ };
132
+ }
133
+ // Definitions/raw HTML alone render nothing; advancing them buys no stable
134
+ // work and can lose whitespace that belongs to the next visible block.
135
+ return UNSAFE_BOUNDARY;
136
+ }
137
+ /** Find the first visible suffix node without formatting the growing token.
138
+ * Whitespace around invisible definitions/HTML is internal in a whole render
139
+ * but gets trimmed at the start of the split suffix, so count it into the
140
+ * boundary gap explicitly. */
141
+ function analyzeSuffixStart(tokens, startIndex) {
142
+ let leadingNewlines = 0;
143
+ for (let i = startIndex; i < tokens.length; i++) {
144
+ const type = tokens[i]?.type;
145
+ if (type === 'table')
146
+ return { kind: 'table', leadingNewlines };
147
+ if (type === 'space' || type === 'br') {
148
+ leadingNewlines += 1;
149
+ continue;
150
+ }
151
+ if (type === 'def' || type === 'del' || type === 'html')
152
+ continue;
153
+ return { kind: 'text', leadingNewlines };
154
+ }
155
+ return { kind: undefined, leadingNewlines };
156
+ }
50
157
  export function StreamingMarkdown({ children, dimColor = false, }) {
51
158
  // The stable prefix is kept as ONE string identity across renders: a
52
159
  // fresh substring per render would break Markdown's React.memo and
@@ -54,11 +161,19 @@ export function StreamingMarkdown({ children, dimColor = false, }) {
54
161
  // identity only changes when a new block boundary advances the prefix.
55
162
  const prefixRef = React.useRef('');
56
163
  const cutRef = React.useRef(0);
164
+ const boundaryGapRef = React.useRef(0);
165
+ const prefixVisibleRef = React.useRef(false);
166
+ const prefixEndsWithTableRef = React.useRef(false);
167
+ const prefixTrailingEmptyTextRef = React.useRef(false);
57
168
  const stripped = stripPromptXMLTags(children);
58
169
  // Reset if text was replaced (defensive; normally unmount handles this)
59
170
  if (!stripped.startsWith(prefixRef.current)) {
60
171
  prefixRef.current = '';
61
172
  cutRef.current = 0;
173
+ boundaryGapRef.current = 0;
174
+ prefixVisibleRef.current = false;
175
+ prefixEndsWithTableRef.current = false;
176
+ prefixTrailingEmptyTextRef.current = false;
62
177
  }
63
178
  // Lex only from current boundary — O(unstable length), not O(full text)
64
179
  const boundary = prefixRef.current.length;
@@ -72,12 +187,34 @@ export function StreamingMarkdown({ children, dimColor = false, }) {
72
187
  for (let i = 0; i < lastContentIdx; i++) {
73
188
  advance += tokens[i].raw.length;
74
189
  }
190
+ let suffixTokenIndex = 0;
75
191
  if (advance > 0) {
76
- prefixRef.current = stripped.substring(0, boundary + advance);
192
+ const stableBoundary = analyzeStableBoundary(tokens, lastContentIdx);
193
+ if (stableBoundary.safe) {
194
+ prefixRef.current = stripped.substring(0, boundary + advance);
195
+ boundaryGapRef.current = stableBoundary.gap;
196
+ prefixVisibleRef.current = true;
197
+ prefixEndsWithTableRef.current = stableBoundary.endsWithTable;
198
+ prefixTrailingEmptyTextRef.current = stableBoundary.trailingEmptyTextNode;
199
+ suffixTokenIndex = lastContentIdx;
200
+ }
77
201
  }
78
202
  const stablePrefix = prefixRef.current;
79
203
  const suffixSource = stripped.substring(stablePrefix.length);
80
204
  const unstableSuffix = clipSuffixTail(suffixSource, cutRef);
205
+ const suffixStart = cutRef.current > 0
206
+ ? { kind: 'text', leadingNewlines: 0 }
207
+ : analyzeSuffixStart(tokens, suffixTokenIndex);
208
+ const boundaryGap = prefixVisibleRef.current && suffixStart.kind !== undefined
209
+ ? prefixEndsWithTableRef.current
210
+ ? suffixStart.kind === 'table' &&
211
+ (prefixTrailingEmptyTextRef.current || suffixStart.leadingNewlines > 0)
212
+ ? 2
213
+ : 1
214
+ : suffixStart.kind === 'table'
215
+ ? 1
216
+ : boundaryGapRef.current + suffixStart.leadingNewlines
217
+ : 0;
81
218
  // A lexer boundary must be strictly outside the stable prefix. If marked
82
219
  // reports a raw span that ends at the current cursor (possible around an
83
220
  // unfinished fence/table while deltas arrive), rendering both branches can
@@ -85,5 +222,5 @@ export function StreamingMarkdown({ children, dimColor = false, }) {
85
222
  // an overlapping empty/duplicate boundary.
86
223
  const hasDistinctSuffix = unstableSuffix !== '' &&
87
224
  (stablePrefix === '' || !unstableSuffix.startsWith(stablePrefix));
88
- return (_jsxs(Box, { flexDirection: "column", gap: 1, children: [stablePrefix && _jsx(Markdown, { dimColor: dimColor, children: stablePrefix }), hasDistinctSuffix && _jsx(Markdown, { dimColor: dimColor, cacheTokens: false, children: unstableSuffix })] }));
225
+ return (_jsxs(Box, { flexDirection: "column", gap: boundaryGap, children: [stablePrefix && _jsx(Markdown, { dimColor: dimColor, children: stablePrefix }), hasDistinctSuffix && _jsx(Markdown, { dimColor: dimColor, cacheTokens: false, children: unstableSuffix })] }));
89
226
  }
@@ -1 +1 @@
1
- {"version":3,"file":"SubagentDashboard.d.ts","sourceRoot":"","sources":["../../../src/components/SubagentDashboard.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AAGzB,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAA;AAMhE,MAAM,WAAW,sBAAsB;IACrC,SAAS,EAAE,aAAa,EAAE,CAAA;IAC1B,OAAO,EAAE,MAAM,IAAI,CAAA;IACnB,QAAQ,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAA;CACrC;AAED,yCAAyC;AACzC,wBAAgB,UAAU,CAAC,EAAE,OAAO,EAAE,EAAE;IAAE,OAAO,EAAE,MAAM,IAAI,CAAA;CAAE,GAAG,KAAK,CAAC,SAAS,CAWhF;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,EAChC,SAAS,EACT,OAAO,EACP,QAAQ,EACT,EAAE,sBAAsB,GAAG,KAAK,CAAC,SAAS,CA2G1C"}
1
+ {"version":3,"file":"SubagentDashboard.d.ts","sourceRoot":"","sources":["../../../src/components/SubagentDashboard.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AAGzB,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAA;AAMhE,MAAM,WAAW,sBAAsB;IACrC,SAAS,EAAE,aAAa,EAAE,CAAA;IAC1B,OAAO,EAAE,MAAM,IAAI,CAAA;IACnB,QAAQ,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAA;CACrC;AAED,yCAAyC;AACzC,wBAAgB,UAAU,CAAC,EAAE,OAAO,EAAE,EAAE;IAAE,OAAO,EAAE,MAAM,IAAI,CAAA;CAAE,GAAG,KAAK,CAAC,SAAS,CAWhF;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,EAChC,SAAS,EACT,OAAO,EACP,QAAQ,EACT,EAAE,sBAAsB,GAAG,KAAK,CAAC,SAAS,CA0G1C"}
@@ -49,10 +49,10 @@ export function SubagentDashboard({ subagents, onClose, onSelect }) {
49
49
  const running = subagents.filter(s => s.status === 'running').length;
50
50
  const completed = subagents.filter(s => s.status === 'completed').length;
51
51
  const failed = subagents.filter(s => s.status === 'failed').length;
52
- return (_jsxs(Box, { flexDirection: "column", paddingX: 2, paddingY: 1, children: [_jsx(Divider, { color: "claude", title: t('subagent-dashboard-title'), padding: 4 }), _jsxs(Box, { flexDirection: "row", gap: 3, marginTop: 1, marginBottom: 1, children: [_jsxs(Text, { children: [_jsx(Text, { color: "claude", children: running }), _jsxs(Text, { dimColor: true, children: [" ", t('subagent-count-running')] })] }), _jsxs(Text, { children: [_jsx(Text, { color: "success", children: completed }), _jsxs(Text, { dimColor: true, children: [" ", t('subagent-count-completed')] })] }), failed > 0 && (_jsxs(Text, { children: [_jsx(Text, { color: "error", children: failed }), _jsxs(Text, { dimColor: true, children: [" ", t('subagent-count-failed')] })] })), _jsx(Box, { flexGrow: 1 }), _jsx(ExitButton, { onClick: onClose })] }), _jsx(Box, { flexDirection: "column", maxHeight: Math.max(10, rows - 10), marginTop: 1, children: _jsx(ScrollBox, { ref: scrollRef, flexDirection: "column", flexGrow: 1, children: subagents.length === 0 ? (_jsxs(Box, { flexDirection: "column", alignItems: "center", marginTop: Math.max(2, Math.floor((rows - 16) / 3)), children: [_jsx(Text, { dimColor: true, children: '○' }), _jsx(Text, { dimColor: true, children: t('subagent-none') }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { dimColor: true, children: t('subagent-empty-hint') }) })] })) : (subagents.map((subagent, index) => (_jsxs(Box, { flexDirection: "column", children: [_jsx(SubagentCard, { subagent: subagent, focused: index === focusIndex, onClick: onSelect !== undefined
52
+ return (_jsxs(Box, { flexDirection: "column", paddingX: 2, paddingY: 1, children: [_jsx(Divider, { color: "claude", title: t('subagent-dashboard-title') }), _jsxs(Box, { flexDirection: "row", gap: 3, marginTop: 1, marginBottom: 1, children: [_jsxs(Text, { children: [_jsx(Text, { color: "claude", children: running }), _jsxs(Text, { dimColor: true, children: [" ", t('subagent-count-running')] })] }), _jsxs(Text, { children: [_jsx(Text, { color: "success", children: completed }), _jsxs(Text, { dimColor: true, children: [" ", t('subagent-count-completed')] })] }), failed > 0 && (_jsxs(Text, { children: [_jsx(Text, { color: "error", children: failed }), _jsxs(Text, { dimColor: true, children: [" ", t('subagent-count-failed')] })] })), _jsx(Box, { flexGrow: 1 }), _jsx(ExitButton, { onClick: onClose })] }), _jsx(Box, { flexDirection: "column", maxHeight: Math.max(10, rows - 10), marginTop: 1, children: _jsx(ScrollBox, { ref: scrollRef, flexDirection: "column", flexGrow: 1, children: subagents.length === 0 ? (_jsxs(Box, { flexDirection: "column", alignItems: "center", marginTop: Math.max(2, Math.floor((rows - 16) / 3)), children: [_jsx(Text, { dimColor: true, children: '○' }), _jsx(Text, { dimColor: true, children: t('subagent-none') }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { dimColor: true, children: t('subagent-empty-hint') }) })] })) : (subagents.map((subagent, index) => (_jsxs(Box, { flexDirection: "column", children: [_jsx(SubagentCard, { subagent: subagent, focused: index === focusIndex, onClick: onSelect !== undefined
53
53
  // Click = view detail, same as Enter on the focused card.
54
54
  ? () => onSelect(subagent.agentId)
55
- : undefined }), index < subagents.length - 1 && (_jsx(Text, { dimColor: true, children: '─'.repeat(Math.max(20, Math.min(72, columns - 6))) }))] }, subagent.agentId)))) }) }), _jsx(Divider, { color: "subtle", title: "", padding: 4 }), _jsx(Box, { marginTop: 0, children: _jsx(Text, { dimColor: true, children: onSelect
55
+ : undefined }), index < subagents.length - 1 && (_jsx(Text, { dimColor: true, children: '─'.repeat(Math.max(20, Math.min(72, columns - 6))) }))] }, subagent.agentId)))) }) }), _jsx(Divider, { color: "subtle", title: "" }), _jsx(Box, { marginTop: 0, children: _jsx(Text, { dimColor: true, children: onSelect
56
56
  ? t('subagent-dashboard-hint-detail')
57
57
  : t('subagent-dashboard-hint-basic') }) })] }));
58
58
  }
@@ -138,5 +138,5 @@ export function SubagentDetailScene({ subagent, onBack, onInterrupt, }) {
138
138
  return (_jsxs(React.Fragment, { children: [_jsx(Box, { onClick: () => setPage(name), backgroundColor: !active ? 'userMessageBackgroundHover' : undefined, children: _jsx(Text, { color: active ? 'claude' : undefined, bold: active, inverse: active, children: ` ${label} ` }) }), _jsx(Text, { dimColor: true, children: name === PAGES[PAGES.length - 1] ? '' : '│' })] }, name));
139
139
  };
140
140
  return (_jsxs(Box, { flexDirection: "column", paddingX: 2, paddingY: 1, children: [_jsxs(Box, { flexDirection: "row", gap: 1, children: [_jsx(Text, { color: info.color, bold: true, children: info.glyph }), _jsx(Text, { bold: true, children: `${t('subagent-card-prefix')}${subagent.description}` }), _jsx(Text, { dimColor: true, children: "\u00B7" }), _jsx(Text, { color: info.color, children: info.label }), _jsx(Box, { flexGrow: 1 }), _jsx(ExitButton, { onClick: onBack })] }), _jsxs(Text, { children: [_jsx(Text, { children: subagent.model ?? subagent.provider ?? 'default' }), _jsx(Text, { dimColor: true, children: ` · ${formatDuration(elapsed)} · ${totalTokens || '—'} tok · ${subagent.toolCalls.length} tools` })] }), _jsxs(Text, { dimColor: true, children: [`${t('subagent-started')} ${formatTimestamp(subagent.startedAt)}`
141
- + (subagent.completedAt ? ` · ${t('subagent-completed')} ${formatTimestamp(subagent.completedAt)}` : ''), ` · id ${subagent.agentId}`] }), subagent.error && (_jsx(Box, { marginTop: 0, children: _jsx(Text, { color: "error", wrap: "wrap", children: `${t('subagent-error-label')}: ${subagent.error}` }) })), _jsxs(Box, { flexDirection: "row", gap: 0, marginTop: 1, children: [tab('summary', t('subagent-tab-summary')), tab('output', t('subagent-output-label')), tab('tools', t('subagent-tools')), _jsx(Text, { dimColor: true, children: ` ${pageIndex + 1}/${PAGES.length}` })] }), _jsx(Text, { dimColor: true, children: '─'.repeat(Math.max(20, Math.min(72, columns - 6))) }), _jsx(Box, { flexDirection: "column", paddingX: 1, maxHeight: Math.max(10, rows - 14), children: _jsxs(ScrollBox, { ref: scrollRef, flexDirection: "column", flexGrow: 1, children: [page === 'summary' && (_jsxs(Box, { flexDirection: "column", children: [_jsx(StatGrid, { subagent: subagent, totalTokens: totalTokens, elapsed: elapsed, statusLabel: info.label, statusColor: info.color }), subagent.summary && (_jsxs(Box, { flexDirection: "column", marginTop: 1, children: [_jsx(Text, { dimColor: true, bold: true, children: '─ summary ' }), _jsx(Text, { wrap: "wrap", children: subagent.summary })] })), !subagent.summary && (_jsx(Text, { dimColor: true, children: isRunning ? t('subagent-no-output') : t('subagent-no-summary') }))] })), page === 'output' && (subagent.outputEvents.length === 0 && subagent.output.length === 0 ? (_jsx(Text, { dimColor: true, children: t('subagent-no-output') })) : (subagent.outputEvents.map((line, index) => (_jsxs(Text, { wrap: "wrap", dimColor: line.kind === 'thinking' || line.kind === 'system', color: line.kind === 'error' ? 'error' : undefined, children: [line.kind === 'thinking' ? ' ⌁ ' : ' ', line.text, !line.settled && isRunning ? ' ▍' : ''] }, index))))), page === 'tools' && (subagent.toolCalls.length === 0 ? (_jsx(Text, { dimColor: true, children: t('subagent-no-tools') })) : (subagent.toolCalls.map((tool, index) => (_jsxs(Box, { flexDirection: "column", marginTop: index === 0 ? 0 : 1, children: [_jsxs(Box, { flexDirection: "row", gap: 1, children: [_jsx(Text, { color: tool.status === 'failed' ? 'error' : tool.status === 'running' ? 'warning' : 'success', children: tool.status === 'running' ? '·' : tool.status === 'failed' ? '×' : '✓' }), _jsx(Text, { color: toolNameColor(tool.name), children: tool.name }), tool.endedAt && _jsx(Text, { dimColor: true, children: formatDuration(tool.endedAt - tool.startedAt) })] }), tool.argsPreview && (_jsx(Box, { flexDirection: "row", paddingLeft: 2, children: _jsx(JsonArgsText, { raw: tool.argsPreview }) })), tool.resultPreview && (_jsx(Box, { flexDirection: "row", paddingLeft: 2, children: _jsx(Text, { dimColor: true, wrap: "wrap", children: `⎿ ${tool.resultPreview}` }) })), tool.error && (_jsx(Box, { flexDirection: "row", paddingLeft: 2, children: _jsx(Text, { color: "error", wrap: "wrap", children: tool.error }) }))] }, tool.id ?? index)))))] }) }), _jsx(Divider, { color: "subtle", title: "", padding: 4 }), _jsxs(Box, { marginTop: 0, flexDirection: "row", children: [_jsx(Text, { dimColor: true, children: `←/→ ${t('subagent-hint-page')} · ↑/↓ ${t('subagent-hint-scroll')}` }), isRunning && onInterrupt && (_jsxs(_Fragment, { children: [_jsx(Text, { dimColor: true, children: ' · ' }), _jsx(Box, { onClick: () => onInterrupt(subagent.agentId), children: _jsx(Text, { dimColor: true, bold: true, color: "warning", children: "X interrupt" }) })] })), _jsx(Text, { dimColor: true, children: ` · Esc ${t('subagent-hint-back')}` })] })] }));
141
+ + (subagent.completedAt ? ` · ${t('subagent-completed')} ${formatTimestamp(subagent.completedAt)}` : ''), ` · id ${subagent.agentId}`] }), subagent.error && (_jsx(Box, { marginTop: 0, children: _jsx(Text, { color: "error", wrap: "wrap", children: `${t('subagent-error-label')}: ${subagent.error}` }) })), _jsxs(Box, { flexDirection: "row", gap: 0, marginTop: 1, children: [tab('summary', t('subagent-tab-summary')), tab('output', t('subagent-output-label')), tab('tools', t('subagent-tools')), _jsx(Text, { dimColor: true, children: ` ${pageIndex + 1}/${PAGES.length}` })] }), _jsx(Text, { dimColor: true, children: '─'.repeat(Math.max(20, Math.min(72, columns - 6))) }), _jsx(Box, { flexDirection: "column", paddingX: 1, maxHeight: Math.max(10, rows - 14), children: _jsxs(ScrollBox, { ref: scrollRef, flexDirection: "column", flexGrow: 1, children: [page === 'summary' && (_jsxs(Box, { flexDirection: "column", children: [_jsx(StatGrid, { subagent: subagent, totalTokens: totalTokens, elapsed: elapsed, statusLabel: info.label, statusColor: info.color }), subagent.summary && (_jsxs(Box, { flexDirection: "column", marginTop: 1, children: [_jsx(Text, { dimColor: true, bold: true, children: '─ summary ' }), _jsx(Text, { wrap: "wrap", children: subagent.summary })] })), !subagent.summary && (_jsx(Text, { dimColor: true, children: isRunning ? t('subagent-no-output') : t('subagent-no-summary') }))] })), page === 'output' && (subagent.outputEvents.length === 0 && subagent.output.length === 0 ? (_jsx(Text, { dimColor: true, children: t('subagent-no-output') })) : (subagent.outputEvents.map((line, index) => (_jsxs(Text, { wrap: "wrap", dimColor: line.kind === 'thinking' || line.kind === 'system', color: line.kind === 'error' ? 'error' : undefined, children: [line.kind === 'thinking' ? ' ⌁ ' : ' ', line.text, !line.settled && isRunning ? ' ▍' : ''] }, index))))), page === 'tools' && (subagent.toolCalls.length === 0 ? (_jsx(Text, { dimColor: true, children: t('subagent-no-tools') })) : (subagent.toolCalls.map((tool, index) => (_jsxs(Box, { flexDirection: "column", marginTop: index === 0 ? 0 : 1, children: [_jsxs(Box, { flexDirection: "row", gap: 1, children: [_jsx(Text, { color: tool.status === 'failed' ? 'error' : tool.status === 'running' ? 'warning' : 'success', children: tool.status === 'running' ? '·' : tool.status === 'failed' ? '×' : '✓' }), _jsx(Text, { color: toolNameColor(tool.name), children: tool.name }), tool.endedAt && _jsx(Text, { dimColor: true, children: formatDuration(tool.endedAt - tool.startedAt) })] }), tool.argsPreview && (_jsx(Box, { flexDirection: "row", paddingLeft: 2, children: _jsx(JsonArgsText, { raw: tool.argsPreview }) })), tool.resultPreview && (_jsx(Box, { flexDirection: "row", paddingLeft: 2, children: _jsx(Text, { dimColor: true, wrap: "wrap", children: `⎿ ${tool.resultPreview}` }) })), tool.error && (_jsx(Box, { flexDirection: "row", paddingLeft: 2, children: _jsx(Text, { color: "error", wrap: "wrap", children: tool.error }) }))] }, tool.id ?? index)))))] }) }), _jsx(Divider, { color: "subtle", title: "" }), _jsxs(Box, { marginTop: 0, flexDirection: "row", children: [_jsx(Text, { dimColor: true, children: `←/→ ${t('subagent-hint-page')} · ↑/↓ ${t('subagent-hint-scroll')}` }), isRunning && onInterrupt && (_jsxs(_Fragment, { children: [_jsx(Text, { dimColor: true, children: ' · ' }), _jsx(Box, { onClick: () => onInterrupt(subagent.agentId), children: _jsx(Text, { dimColor: true, bold: true, color: "warning", children: "X interrupt" }) })] })), _jsx(Text, { dimColor: true, children: ` · Esc ${t('subagent-hint-back')}` })] })] }));
142
142
  }
@@ -1,27 +1,25 @@
1
1
  import React from 'react';
2
2
  import { type SelectOption } from './Select.js';
3
+ import type { TuiThemeHost } from '../dsh-adapter/themes.js';
3
4
  /**
4
- * The full selectable theme list: the `auto` pseudo-theme (follows the
5
- * terminal background via OSC 11, light/dark) first, then the three
6
- * built-in palettes (display order), then discovered user themes from
7
- * ~/.dsh-tui/themes (sorted by file name). A user theme named `auto` is
8
- * shadowed by the built-in pseudo-theme (getTheme resolves `auto` first),
9
- * so it is filtered out to keep the list truthful. Shared by ThemePicker
10
- * (render) and the /theme command (focus index), so both always see the
11
- * same ordering.
5
+ * Build the full selectable list from the shared catalog: `auto`, built-ins,
6
+ * static JSON themes, and optional plugin themes. Shared by ThemePicker (render)
7
+ * and the /theme command (focus index), so both always see the same ordering.
12
8
  */
13
- export declare function getThemeOptions(): SelectOption[];
9
+ export declare function getThemeOptions(themeHost?: TuiThemeHost): SelectOption[];
14
10
  /**
15
11
  * Color-theme picker in the ActivityPicker style: a permission-colored Pane
16
- * listing the `auto` pseudo-theme and the built-in palettes first, then
17
- * every user theme found in ~/.dsh-tui/themes — each row shows the display
18
- * name, its base and three key color swatches; `❯` marks focus, `✓` the
19
- * active theme. Enter applies through the ThemeProvider setter (persists to
20
- * ~/.dsh-tui/theme.json and hot swaps), Esc cancels.
12
+ * listing the `auto` pseudo-theme and built-in palettes first, followed by
13
+ * static JSON and plugin themes — each row shows the display name, base and
14
+ * three key color swatches; `❯` marks focus, `✓` the active theme. Enter
15
+ * applies through the ThemeProvider setter (persists to ~/.dsh-tui/theme.json
16
+ * and hot swaps), Esc cancels.
21
17
  */
22
- export declare function ThemePicker({ focusIndex, currentTheme, onPick, }: {
18
+ export declare function ThemePicker({ focusIndex, currentTheme, themeHost, onPick, }: {
23
19
  focusIndex: number;
24
20
  currentTheme: string | undefined;
21
+ /** Optional runtime theme host; static themes work without it. */
22
+ themeHost?: TuiThemeHost;
25
23
  /** Mouse pick (fullscreen): clicked row's absolute index (Chat applies
26
24
  * the same code path as the keyboard Enter). */
27
25
  onPick?: (index: number) => void;
@@ -1 +1 @@
1
- {"version":3,"file":"ThemePicker.d.ts","sourceRoot":"","sources":["../../../src/components/ThemePicker.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AAIzB,OAAO,EAAU,KAAK,YAAY,EAAE,MAAM,aAAa,CAAA;AAuCvD;;;;;;;;;GASG;AACH,wBAAgB,eAAe,IAAI,YAAY,EAAE,CAsBhD;AAED;;;;;;;GAOG;AACH,wBAAgB,WAAW,CAAC,EAC1B,UAAU,EACV,YAAY,EACZ,MAAM,GACP,EAAE;IACD,UAAU,EAAE,MAAM,CAAA;IAClB,YAAY,EAAE,MAAM,GAAG,SAAS,CAAA;IAChC;qDACiD;IACjD,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAA;CACjC,GAAG,KAAK,CAAC,SAAS,CAuBlB"}
1
+ {"version":3,"file":"ThemePicker.d.ts","sourceRoot":"","sources":["../../../src/components/ThemePicker.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AAIzB,OAAO,EAAU,KAAK,YAAY,EAAE,MAAM,aAAa,CAAA;AAIvD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAA;AAqC5D;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,SAAS,CAAC,EAAE,YAAY,GAAG,YAAY,EAAE,CAYxE;AAED;;;;;;;GAOG;AACH,wBAAgB,WAAW,CAAC,EAC1B,UAAU,EACV,YAAY,EACZ,SAAS,EACT,MAAM,GACP,EAAE;IACD,UAAU,EAAE,MAAM,CAAA;IAClB,YAAY,EAAE,MAAM,GAAG,SAAS,CAAA;IAChC,kEAAkE;IAClE,SAAS,CAAC,EAAE,YAAY,CAAA;IACxB;qDACiD;IACjD,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAA;CACjC,GAAG,KAAK,CAAC,SAAS,CA2BlB"}
@@ -5,8 +5,8 @@ import { Box, Text } from '../ui.js';
5
5
  import { Pane } from './design-system/Pane.js';
6
6
  import { Select } from './Select.js';
7
7
  import { HintLine } from './design-system/HintLine.js';
8
- import { getTheme, THEME_NAMES, AUTO_THEME_NAME } from '../theme.js';
9
- import { buildTheme, listCustomThemes } from '../customTheme.js';
8
+ import { listThemeCatalog } from '../themeCatalog.js';
9
+ import { useRuntimeThemeSnapshot } from '../hooks/useRuntimeThemeSnapshot.js';
10
10
  /** One double-block swatch character per preview key, in a row. */
11
11
  const SWATCH = '██';
12
12
  /** Theme keys previewed in the picker, chosen for visual contrast. */
@@ -23,35 +23,33 @@ function optionFor(name, displayName, theme, description) {
23
23
  };
24
24
  }
25
25
  /**
26
- * The full selectable theme list: the `auto` pseudo-theme (follows the
27
- * terminal background via OSC 11, light/dark) first, then the three
28
- * built-in palettes (display order), then discovered user themes from
29
- * ~/.dsh-tui/themes (sorted by file name). A user theme named `auto` is
30
- * shadowed by the built-in pseudo-theme (getTheme resolves `auto` first),
31
- * so it is filtered out to keep the list truthful. Shared by ThemePicker
32
- * (render) and the /theme command (focus index), so both always see the
33
- * same ordering.
26
+ * Build the full selectable list from the shared catalog: `auto`, built-ins,
27
+ * static JSON themes, and optional plugin themes. Shared by ThemePicker (render)
28
+ * and the /theme command (focus index), so both always see the same ordering.
34
29
  */
35
- export function getThemeOptions() {
36
- const auto = optionFor(AUTO_THEME_NAME, AUTO_THEME_NAME, getTheme(AUTO_THEME_NAME), t('theme-auto-base'));
37
- const builtins = THEME_NAMES.map(name => {
38
- const theme = getTheme(name);
39
- return optionFor(name, name, theme, t('theme-builtin-base', { name }));
30
+ export function getThemeOptions(themeHost) {
31
+ return listThemeCatalog(themeHost).map(item => {
32
+ const base = item.base ?? 'dark';
33
+ const description = item.source === 'auto'
34
+ ? t('theme-auto-base')
35
+ : item.source === 'builtin'
36
+ ? t('theme-builtin-base', { name: item.name })
37
+ : item.source === 'runtime'
38
+ ? t('theme-plugin-base', { base, name: item.name })
39
+ : t('theme-user-base', { base, name: item.name });
40
+ return optionFor(item.name, item.displayName, item.theme, description);
40
41
  });
41
- const custom = listCustomThemes()
42
- .filter(spec => spec.name !== AUTO_THEME_NAME)
43
- .map(spec => optionFor(spec.name, spec.displayName, buildTheme(spec), t('theme-user-base', { base: spec.base, name: spec.name })));
44
- return [auto, ...builtins, ...custom];
45
42
  }
46
43
  /**
47
44
  * Color-theme picker in the ActivityPicker style: a permission-colored Pane
48
- * listing the `auto` pseudo-theme and the built-in palettes first, then
49
- * every user theme found in ~/.dsh-tui/themes — each row shows the display
50
- * name, its base and three key color swatches; `❯` marks focus, `✓` the
51
- * active theme. Enter applies through the ThemeProvider setter (persists to
52
- * ~/.dsh-tui/theme.json and hot swaps), Esc cancels.
45
+ * listing the `auto` pseudo-theme and built-in palettes first, followed by
46
+ * static JSON and plugin themes — each row shows the display name, base and
47
+ * three key color swatches; `❯` marks focus, `✓` the active theme. Enter
48
+ * applies through the ThemeProvider setter (persists to ~/.dsh-tui/theme.json
49
+ * and hot swaps), Esc cancels.
53
50
  */
54
- export function ThemePicker({ focusIndex, currentTheme, onPick, }) {
55
- const options = React.useMemo(() => getThemeOptions(), []);
51
+ export function ThemePicker({ focusIndex, currentTheme, themeHost, onPick, }) {
52
+ const runtimeThemeSnapshot = useRuntimeThemeSnapshot(themeHost);
53
+ const options = React.useMemo(() => getThemeOptions(themeHost), [runtimeThemeSnapshot, themeHost]);
56
54
  return (_jsx(Pane, { color: "permission", children: _jsxs(Box, { flexDirection: "column", children: [_jsx(Box, { marginBottom: 1, children: _jsx(Text, { color: "remember", bold: true, children: t('picker-title-theme') }) }), _jsx(Select, { options: options, focusIndex: focusIndex, selectedValue: currentTheme, visibleOptionCount: 6, onPick: onPick ? index => onPick(index) : undefined }), _jsx(Text, { dimColor: true, italic: true, children: _jsx(HintLine, { text: t('hint-confirm-exit') }) })] }) }));
57
55
  }
@@ -0,0 +1,67 @@
1
+ import React from 'react';
2
+ import type { PointerEvent } from '../ink/events/pointer-event.js';
3
+ /** Tooltip content: a fixed string or a getter resolved at show time. */
4
+ export type TooltipContent = string | (() => string);
5
+ export type TooltipOptions = {
6
+ /** Dwell before the tooltip appears. Default 600ms. */
7
+ delayMs?: number;
8
+ };
9
+ /** Hover props returned by {@link useTooltip}; spread onto a Box. */
10
+ export type TooltipHoverProps = {
11
+ onMouseEnter: (event?: PointerEvent) => void;
12
+ onMouseLeave: () => void;
13
+ };
14
+ /**
15
+ * Per-attachment owner: identifies which element's timer is pending and
16
+ * which element owns the shown tooltip (so one element's leave can never
17
+ * hide another's tooltip).
18
+ */
19
+ type TooltipOwner = {
20
+ timer: ReturnType<typeof setTimeout> | null;
21
+ };
22
+ type TooltipState = {
23
+ owner: TooltipOwner;
24
+ anchorCol: number;
25
+ anchorRow: number;
26
+ content: string;
27
+ };
28
+ /** Subscribe to tooltip show/hide; returns an unsubscribe function. */
29
+ export declare function subscribeTooltip(listener: () => void): () => void;
30
+ /** Stable snapshot for useSyncExternalStore (identity kept until changed). */
31
+ export declare function getTooltipSnapshot(): TooltipState | null;
32
+ /** Hide shown content and invalidate every pending dwell timer. */
33
+ export declare function clearTooltip(): void;
34
+ /**
35
+ * Hover props that pop a tooltip with the full content after a dwell.
36
+ *
37
+ * The pointer position (screen col/row) is taken from the PointerEvent the
38
+ * renderer hands to `onMouseEnter` — the runtime always passes one even
39
+ * though the prop type is `() => void`, which is why the handler's parameter
40
+ * is optional (a `() => void` prop accepts a function with only optional
41
+ * parameters).
42
+ */
43
+ export declare function useTooltip(content: TooltipContent, opts?: TooltipOptions): TooltipHoverProps;
44
+ /**
45
+ * Convenience wrapper for list rows built from data (a component boundary
46
+ * makes the hook call legal per item). Renders a plain Box carrying the
47
+ * hover props around `children`.
48
+ */
49
+ export declare function TooltipTarget({ content, children, }: {
50
+ content: TooltipContent;
51
+ children: React.ReactNode;
52
+ }): React.ReactNode;
53
+ /**
54
+ * The singleton floating layer: subscribes to the tooltip store and renders
55
+ * the rounded card above the anchor row (below it when there is no room),
56
+ * horizontally clamped inside the terminal. Mount once, LAST in the tree so
57
+ * it paints over everything else. Terminal resize hides the tooltip — its
58
+ * anchor is screen geometry that just went stale.
59
+ */
60
+ export declare function TooltipLayer({ invalidationKey, subscribeInvalidation, }?: {
61
+ /** Changes when a modal/screen geometry transition makes the anchor stale. */
62
+ invalidationKey?: unknown;
63
+ /** Scroll/viewport subscription; every notification invalidates the anchor. */
64
+ subscribeInvalidation?: (listener: () => void) => () => void;
65
+ }): React.ReactNode;
66
+ export {};
67
+ //# sourceMappingURL=Tooltip.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Tooltip.d.ts","sourceRoot":"","sources":["../../../src/components/Tooltip.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AAMzB,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gCAAgC,CAAA;AAiBlE,yEAAyE;AACzE,MAAM,MAAM,cAAc,GAAG,MAAM,GAAG,CAAC,MAAM,MAAM,CAAC,CAAA;AAEpD,MAAM,MAAM,cAAc,GAAG;IAC3B,uDAAuD;IACvD,OAAO,CAAC,EAAE,MAAM,CAAA;CACjB,CAAA;AAED,qEAAqE;AACrE,MAAM,MAAM,iBAAiB,GAAG;IAC9B,YAAY,EAAE,CAAC,KAAK,CAAC,EAAE,YAAY,KAAK,IAAI,CAAA;IAC5C,YAAY,EAAE,MAAM,IAAI,CAAA;CACzB,CAAA;AAED;;;;GAIG;AACH,KAAK,YAAY,GAAG;IAAE,KAAK,EAAE,UAAU,CAAC,OAAO,UAAU,CAAC,GAAG,IAAI,CAAA;CAAE,CAAA;AAEnE,KAAK,YAAY,GAAG;IAClB,KAAK,EAAE,YAAY,CAAA;IACnB,SAAS,EAAE,MAAM,CAAA;IACjB,SAAS,EAAE,MAAM,CAAA;IACjB,OAAO,EAAE,MAAM,CAAA;CAChB,CAAA;AAYD,uEAAuE;AACvE,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,IAAI,GAAG,MAAM,IAAI,CAKjE;AAED,8EAA8E;AAC9E,wBAAgB,kBAAkB,IAAI,YAAY,GAAG,IAAI,CAExD;AAcD,mEAAmE;AACnE,wBAAgB,YAAY,IAAI,IAAI,CAMnC;AAED;;;;;;;;GAQG;AACH,wBAAgB,UAAU,CAAC,OAAO,EAAE,cAAc,EAAE,IAAI,CAAC,EAAE,cAAc,GAAG,iBAAiB,CAuC5F;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,EAC5B,OAAO,EACP,QAAQ,GACT,EAAE;IACD,OAAO,EAAE,cAAc,CAAA;IACvB,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAA;CAC1B,GAAG,KAAK,CAAC,SAAS,CAGlB;AAED;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,EAC3B,eAAe,EACf,qBAAqB,GACtB,GAAE;IACD,8EAA8E;IAC9E,eAAe,CAAC,EAAE,OAAO,CAAA;IACzB,+EAA+E;IAC/E,qBAAqB,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,IAAI,KAAK,MAAM,IAAI,CAAA;CACxD,GAAG,KAAK,CAAC,SAAS,CAgEvB"}