@deepseek-harness-tui/dsh-tui 0.8.0 → 0.8.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 (434) hide show
  1. package/README.md +32 -15
  2. package/cordis.patch.yml +21 -2
  3. package/cordis.yml +12 -1
  4. package/dsh-ecosystem-spec/protocols/profile-definitions.d.ts +13 -0
  5. package/dsh-ecosystem-spec/protocols/profile-definitions.js +59 -0
  6. package/dsh-ecosystem-spec/protocols/tui-channel-http.d.ts +15 -0
  7. package/dsh-ecosystem-spec/protocols/tui-channel-http.js +32 -0
  8. package/dsh-ecosystem-spec/protocols/tui-channel.d.ts +59 -0
  9. package/dsh-ecosystem-spec/protocols/tui-channel.js +166 -0
  10. package/dsh-ecosystem-spec/protocols/tui-contributions.d.ts +42 -0
  11. package/dsh-ecosystem-spec/protocols/tui-contributions.js +143 -0
  12. package/dsh-ecosystem-spec/registry/README.md +14 -0
  13. package/dsh-ecosystem-spec/registry/contracts/decision-events-v1alpha1.json +13 -0
  14. package/dsh-ecosystem-spec/registry/contracts/scene-v1alpha1.json +13 -0
  15. package/dsh-ecosystem-spec/registry/contracts/settings-section-v1alpha1.json +13 -0
  16. package/dsh-ecosystem-spec/registry/contracts/tui-channel-v1alpha1.json +30 -0
  17. package/dsh-ecosystem-spec/registry/contracts/workspace-provider-v1alpha1.json +6 -0
  18. package/dsh-ecosystem-spec/registry/host-descriptor.tui.example.json +56 -0
  19. package/dsh-ecosystem-spec/registry/permissions-0.1.json +13 -0
  20. package/dsh-ecosystem-spec/registry/registry-0.15.json +149 -0
  21. package/dsh-ecosystem-spec/schemas/conformance-claim.schema.json +15 -0
  22. package/dsh-ecosystem-spec/schemas/effect-ledger-record.schema.json +16 -0
  23. package/dsh-ecosystem-spec/schemas/host-descriptor.schema.json +26 -0
  24. package/lib/types/cc/figures.d.ts +4 -0
  25. package/lib/types/cc/figures.d.ts.map +1 -1
  26. package/lib/types/cc/figures.js +4 -0
  27. package/lib/types/command-trees.d.ts +2 -1
  28. package/lib/types/command-trees.d.ts.map +1 -1
  29. package/lib/types/command-trees.js +1 -1
  30. package/lib/types/commands.d.ts.map +1 -1
  31. package/lib/types/commands.js +2 -0
  32. package/lib/types/components/ExtensionDialog.d.ts +40 -0
  33. package/lib/types/components/ExtensionDialog.d.ts.map +1 -0
  34. package/lib/types/components/ExtensionDialog.js +210 -0
  35. package/lib/types/components/HelpMenu.js +1 -1
  36. package/lib/types/components/LoadedContextPanel.d.ts +5 -5
  37. package/lib/types/components/LoadedContextPanel.js +6 -6
  38. package/lib/types/components/LogoV2.d.ts.map +1 -1
  39. package/lib/types/components/LogoV2.js +16 -8
  40. package/lib/types/components/Markdown.d.ts +13 -1
  41. package/lib/types/components/Markdown.d.ts.map +1 -1
  42. package/lib/types/components/Markdown.js +15 -1
  43. package/lib/types/components/MessageList.d.ts +3 -1
  44. package/lib/types/components/MessageList.d.ts.map +1 -1
  45. package/lib/types/components/MessageList.js +111 -6
  46. package/lib/types/components/PromptInput.d.ts.map +1 -1
  47. package/lib/types/components/PromptInput.js +26 -12
  48. package/lib/types/components/RewindPicker.d.ts +13 -1
  49. package/lib/types/components/RewindPicker.d.ts.map +1 -1
  50. package/lib/types/components/RewindPicker.js +26 -2
  51. package/lib/types/components/StreamingMarkdown.d.ts +2 -8
  52. package/lib/types/components/StreamingMarkdown.d.ts.map +1 -1
  53. package/lib/types/components/StreamingMarkdown.js +51 -9
  54. package/lib/types/components/ToolUseLoader.d.ts +8 -3
  55. package/lib/types/components/ToolUseLoader.d.ts.map +1 -1
  56. package/lib/types/components/ToolUseLoader.js +43 -9
  57. package/lib/types/components/activityFrames.d.ts +1 -1
  58. package/lib/types/components/activityFrames.d.ts.map +1 -1
  59. package/lib/types/components/activityFrames.js +4 -1
  60. package/lib/types/components/messages/AssistantThinkingMessage.d.ts +5 -1
  61. package/lib/types/components/messages/AssistantThinkingMessage.d.ts.map +1 -1
  62. package/lib/types/components/messages/AssistantThinkingMessage.js +26 -3
  63. package/lib/types/components/messages/AssistantToolUseMessage.d.ts.map +1 -1
  64. package/lib/types/components/messages/AssistantToolUseMessage.js +13 -2
  65. package/lib/types/dsh-adapter/channel.d.ts +61 -11
  66. package/lib/types/dsh-adapter/channel.d.ts.map +1 -1
  67. package/lib/types/dsh-adapter/channel.js +767 -120
  68. package/lib/types/dsh-adapter/childStderr.d.ts.map +1 -1
  69. package/lib/types/dsh-adapter/childStderr.js +10 -0
  70. package/lib/types/dsh-adapter/command-attribution.d.ts +51 -0
  71. package/lib/types/dsh-adapter/command-attribution.d.ts.map +1 -0
  72. package/lib/types/dsh-adapter/command-attribution.js +93 -0
  73. package/lib/types/dsh-adapter/command-errors.d.ts +42 -0
  74. package/lib/types/dsh-adapter/command-errors.d.ts.map +1 -0
  75. package/lib/types/dsh-adapter/command-errors.js +63 -0
  76. package/lib/types/dsh-adapter/command-trees.d.ts +7 -1
  77. package/lib/types/dsh-adapter/command-trees.d.ts.map +1 -1
  78. package/lib/types/dsh-adapter/command-trees.js +70 -20
  79. package/lib/types/dsh-adapter/component-identity.d.ts +33 -0
  80. package/lib/types/dsh-adapter/component-identity.d.ts.map +1 -0
  81. package/lib/types/dsh-adapter/component-identity.js +108 -0
  82. package/lib/types/dsh-adapter/contract.d.ts +1 -1
  83. package/lib/types/dsh-adapter/contract.js +2 -2
  84. package/lib/types/dsh-adapter/decision-guard.d.ts +84 -0
  85. package/lib/types/dsh-adapter/decision-guard.d.ts.map +1 -0
  86. package/lib/types/dsh-adapter/decision-guard.js +348 -0
  87. package/lib/types/dsh-adapter/dialogs.d.ts +145 -0
  88. package/lib/types/dsh-adapter/dialogs.d.ts.map +1 -0
  89. package/lib/types/dsh-adapter/dialogs.js +316 -0
  90. package/lib/types/dsh-adapter/effect-ledger.d.ts +91 -0
  91. package/lib/types/dsh-adapter/effect-ledger.d.ts.map +1 -0
  92. package/lib/types/dsh-adapter/effect-ledger.js +251 -0
  93. package/lib/types/dsh-adapter/extension-events.d.ts +193 -0
  94. package/lib/types/dsh-adapter/extension-events.d.ts.map +1 -0
  95. package/lib/types/dsh-adapter/extension-events.js +207 -0
  96. package/lib/types/dsh-adapter/extensions.d.ts +32 -0
  97. package/lib/types/dsh-adapter/extensions.d.ts.map +1 -0
  98. package/lib/types/dsh-adapter/extensions.js +42 -0
  99. package/lib/types/dsh-adapter/grants.d.ts +25 -0
  100. package/lib/types/dsh-adapter/grants.d.ts.map +1 -0
  101. package/lib/types/dsh-adapter/grants.js +213 -0
  102. package/lib/types/dsh-adapter/host-access.d.ts +37 -0
  103. package/lib/types/dsh-adapter/host-access.d.ts.map +1 -0
  104. package/lib/types/dsh-adapter/host-access.js +652 -0
  105. package/lib/types/dsh-adapter/host-descriptor.d.ts +24 -0
  106. package/lib/types/dsh-adapter/host-descriptor.d.ts.map +1 -0
  107. package/lib/types/dsh-adapter/host-descriptor.js +169 -0
  108. package/lib/types/dsh-adapter/index.d.ts +4 -0
  109. package/lib/types/dsh-adapter/index.d.ts.map +1 -1
  110. package/lib/types/dsh-adapter/index.js +1 -0
  111. package/lib/types/dsh-adapter/message-observer.d.ts +173 -0
  112. package/lib/types/dsh-adapter/message-observer.d.ts.map +1 -0
  113. package/lib/types/dsh-adapter/message-observer.js +553 -0
  114. package/lib/types/dsh-adapter/plugin-host.d.ts +136 -0
  115. package/lib/types/dsh-adapter/plugin-host.d.ts.map +1 -0
  116. package/lib/types/dsh-adapter/plugin-host.js +365 -0
  117. package/lib/types/dsh-adapter/plugin-storage.d.ts +108 -0
  118. package/lib/types/dsh-adapter/plugin-storage.d.ts.map +1 -0
  119. package/lib/types/dsh-adapter/plugin-storage.js +397 -0
  120. package/lib/types/dsh-adapter/plugin.d.ts.map +1 -1
  121. package/lib/types/dsh-adapter/plugin.js +124 -14
  122. package/lib/types/dsh-adapter/plugins-info.d.ts +43 -0
  123. package/lib/types/dsh-adapter/plugins-info.d.ts.map +1 -0
  124. package/lib/types/dsh-adapter/plugins-info.js +276 -0
  125. package/lib/types/dsh-adapter/promptDebug.d.ts +0 -6
  126. package/lib/types/dsh-adapter/promptDebug.d.ts.map +1 -1
  127. package/lib/types/dsh-adapter/promptDebug.js +13 -4
  128. package/lib/types/dsh-adapter/renderers.d.ts +57 -0
  129. package/lib/types/dsh-adapter/renderers.d.ts.map +1 -0
  130. package/lib/types/dsh-adapter/renderers.js +173 -0
  131. package/lib/types/dsh-adapter/sanitize.d.ts +36 -0
  132. package/lib/types/dsh-adapter/sanitize.d.ts.map +1 -0
  133. package/lib/types/dsh-adapter/sanitize.js +65 -0
  134. package/lib/types/dsh-adapter/scenes.d.ts +15 -5
  135. package/lib/types/dsh-adapter/scenes.d.ts.map +1 -1
  136. package/lib/types/dsh-adapter/scenes.js +149 -35
  137. package/lib/types/dsh-adapter/settings-sections.d.ts +7 -3
  138. package/lib/types/dsh-adapter/settings-sections.d.ts.map +1 -1
  139. package/lib/types/dsh-adapter/settings-sections.js +106 -26
  140. package/lib/types/dsh-adapter/shortcuts.d.ts +107 -0
  141. package/lib/types/dsh-adapter/shortcuts.d.ts.map +1 -0
  142. package/lib/types/dsh-adapter/shortcuts.js +340 -0
  143. package/lib/types/dsh-adapter/status.d.ts +74 -0
  144. package/lib/types/dsh-adapter/status.d.ts.map +1 -0
  145. package/lib/types/dsh-adapter/status.js +234 -0
  146. package/lib/types/dsh-adapter/workspaces.d.ts +16 -5
  147. package/lib/types/dsh-adapter/workspaces.d.ts.map +1 -1
  148. package/lib/types/dsh-adapter/workspaces.js +287 -170
  149. package/lib/types/extensions.d.ts +13 -0
  150. package/lib/types/extensions.d.ts.map +1 -0
  151. package/lib/types/extensions.js +21 -0
  152. package/lib/types/force-production-react.d.ts +1 -0
  153. package/lib/types/force-production-react.d.ts.map +1 -0
  154. package/lib/types/force-production-react.js +12 -0
  155. package/lib/types/i18n.d.ts +174 -20
  156. package/lib/types/i18n.d.ts.map +1 -1
  157. package/lib/types/i18n.js +56 -6
  158. package/lib/types/index.d.ts +1 -0
  159. package/lib/types/index.d.ts.map +1 -1
  160. package/lib/types/index.js +1 -0
  161. package/lib/types/ink/components/App.d.ts +2 -0
  162. package/lib/types/ink/components/App.d.ts.map +1 -1
  163. package/lib/types/ink/components/App.js +4 -0
  164. package/lib/types/ink/ink.d.ts +31 -3
  165. package/lib/types/ink/ink.d.ts.map +1 -1
  166. package/lib/types/ink/ink.js +90 -5
  167. package/lib/types/ink/log-update.d.ts.map +1 -1
  168. package/lib/types/ink/log-update.js +225 -56
  169. package/lib/types/ink/output.d.ts +35 -2
  170. package/lib/types/ink/output.d.ts.map +1 -1
  171. package/lib/types/ink/output.js +281 -14
  172. package/lib/types/ink/render-node-to-output.d.ts.map +1 -1
  173. package/lib/types/ink/render-node-to-output.js +14 -6
  174. package/lib/types/ink/screen.d.ts +39 -0
  175. package/lib/types/ink/screen.d.ts.map +1 -1
  176. package/lib/types/ink/screen.js +117 -0
  177. package/lib/types/ink/terminal.d.ts.map +1 -1
  178. package/lib/types/ink/terminal.js +26 -6
  179. package/lib/types/ink/wrapAnsi.d.ts +1 -1
  180. package/lib/types/ink/wrapAnsi.d.ts.map +1 -1
  181. package/lib/types/ink/wrapAnsi.js +29 -2
  182. package/lib/types/plugin-host.d.ts +15 -0
  183. package/lib/types/plugin-host.d.ts.map +1 -0
  184. package/lib/types/plugin-host.js +17 -0
  185. package/lib/types/plugin-spec/negotiate.d.ts +11 -0
  186. package/lib/types/plugin-spec/negotiate.d.ts.map +1 -0
  187. package/lib/types/plugin-spec/negotiate.js +97 -0
  188. package/lib/types/plugin-spec/permission-scope.d.ts +32 -0
  189. package/lib/types/plugin-spec/permission-scope.d.ts.map +1 -0
  190. package/lib/types/plugin-spec/permission-scope.js +90 -0
  191. package/lib/types/plugin-spec/registry.d.ts +23 -0
  192. package/lib/types/plugin-spec/registry.d.ts.map +1 -0
  193. package/lib/types/plugin-spec/registry.js +252 -0
  194. package/lib/types/plugin-spec/schema-check.d.ts +17 -0
  195. package/lib/types/plugin-spec/schema-check.d.ts.map +1 -0
  196. package/lib/types/plugin-spec/schema-check.js +165 -0
  197. package/lib/types/plugin-spec/tui-extension.d.ts +16 -0
  198. package/lib/types/plugin-spec/tui-extension.d.ts.map +1 -0
  199. package/lib/types/plugin-spec/tui-extension.js +79 -0
  200. package/lib/types/plugin-spec/types.d.ts +114 -0
  201. package/lib/types/plugin-spec/types.d.ts.map +1 -0
  202. package/lib/types/plugin-spec/types.js +10 -0
  203. package/lib/types/plugin-spec/validate.d.ts +21 -0
  204. package/lib/types/plugin-spec/validate.d.ts.map +1 -0
  205. package/lib/types/plugin-spec/validate.js +138 -0
  206. package/lib/types/scenes.d.ts +2 -1
  207. package/lib/types/scenes.d.ts.map +1 -1
  208. package/lib/types/scenes.js +1 -1
  209. package/lib/types/screens/Chat.d.ts +14 -1
  210. package/lib/types/screens/Chat.d.ts.map +1 -1
  211. package/lib/types/screens/Chat.js +173 -49
  212. package/lib/types/screens/SessionBrowser.d.ts.map +1 -1
  213. package/lib/types/screens/SessionBrowser.js +10 -3
  214. package/lib/types/sessions/view.d.ts +3 -2
  215. package/lib/types/sessions/view.d.ts.map +1 -1
  216. package/lib/types/sessions/view.js +18 -0
  217. package/lib/types/settings-sections.d.ts +2 -1
  218. package/lib/types/settings-sections.d.ts.map +1 -1
  219. package/lib/types/settings-sections.js +1 -1
  220. package/lib/types/theme.d.ts +5 -0
  221. package/lib/types/theme.d.ts.map +1 -1
  222. package/lib/types/theme.js +15 -0
  223. package/lib/types/update.d.ts +20 -0
  224. package/lib/types/update.d.ts.map +1 -1
  225. package/lib/types/update.js +54 -7
  226. package/lib/types/utils/execFileNoThrow.d.ts.map +1 -1
  227. package/lib/types/utils/execFileNoThrow.js +3 -0
  228. package/lib/types/utils/loaded-context.d.ts +8 -3
  229. package/lib/types/utils/loaded-context.d.ts.map +1 -1
  230. package/lib/types/utils/loaded-context.js +39 -3
  231. package/lib/types/utils/modifiers.d.ts.map +1 -1
  232. package/lib/types/utils/modifiers.js +3 -2
  233. package/lib/types/working-activity.d.ts +12 -0
  234. package/lib/types/working-activity.d.ts.map +1 -1
  235. package/lib/types/working-activity.js +13 -0
  236. package/lib/types/workspaces.d.ts +2 -1
  237. package/lib/types/workspaces.d.ts.map +1 -1
  238. package/lib/types/workspaces.js +1 -1
  239. package/node_modules/@dsh-std/command/CHANGELOG.md +8 -0
  240. package/node_modules/@dsh-std/command/LICENSE +21 -0
  241. package/node_modules/@dsh-std/command/README.md +26 -0
  242. package/node_modules/@dsh-std/command/README.zh.md +28 -0
  243. package/node_modules/@dsh-std/command/lib/index.d.ts +144 -0
  244. package/node_modules/@dsh-std/command/lib/index.d.ts.map +1 -0
  245. package/node_modules/@dsh-std/command/lib/index.js +252 -0
  246. package/node_modules/@dsh-std/command/lib/index.js.map +1 -0
  247. package/node_modules/@dsh-std/command/package.json +22 -0
  248. package/node_modules/@dsh-std/connection/CHANGELOG.md +8 -0
  249. package/node_modules/@dsh-std/connection/LICENSE +21 -0
  250. package/node_modules/@dsh-std/connection/README.md +42 -0
  251. package/node_modules/@dsh-std/connection/README.zh.md +42 -0
  252. package/node_modules/@dsh-std/connection/lib/connection-CdGcSQnu.d.ts +125 -0
  253. package/node_modules/@dsh-std/connection/lib/connection-CdGcSQnu.d.ts.map +1 -0
  254. package/node_modules/@dsh-std/connection/lib/index.d.ts +89 -0
  255. package/node_modules/@dsh-std/connection/lib/index.d.ts.map +1 -0
  256. package/node_modules/@dsh-std/connection/lib/index.js +158 -0
  257. package/node_modules/@dsh-std/connection/lib/index.js.map +1 -0
  258. package/node_modules/@dsh-std/connection/lib/memory.d.ts +24 -0
  259. package/node_modules/@dsh-std/connection/lib/memory.d.ts.map +1 -0
  260. package/node_modules/@dsh-std/connection/lib/memory.js +258 -0
  261. package/node_modules/@dsh-std/connection/lib/memory.js.map +1 -0
  262. package/node_modules/@dsh-std/connection/lib/resolve-3X6ausgz.js +226 -0
  263. package/node_modules/@dsh-std/connection/lib/resolve-3X6ausgz.js.map +1 -0
  264. package/node_modules/@dsh-std/connection/package.json +20 -0
  265. package/node_modules/@dsh-std/core/CHANGELOG.md +8 -0
  266. package/node_modules/@dsh-std/core/LICENSE +21 -0
  267. package/node_modules/@dsh-std/core/README.md +13 -0
  268. package/node_modules/@dsh-std/core/README.zh.md +13 -0
  269. package/node_modules/@dsh-std/core/lib/index.d.ts +112 -0
  270. package/node_modules/@dsh-std/core/lib/index.d.ts.map +1 -0
  271. package/node_modules/@dsh-std/core/lib/index.js +424 -0
  272. package/node_modules/@dsh-std/core/lib/index.js.map +1 -0
  273. package/node_modules/@dsh-std/core/package.json +21 -0
  274. package/node_modules/@dsh-std/manifest/CHANGELOG.md +9 -0
  275. package/node_modules/@dsh-std/manifest/LICENSE +21 -0
  276. package/node_modules/@dsh-std/manifest/README.md +5 -0
  277. package/node_modules/@dsh-std/manifest/README.zh.md +5 -0
  278. package/node_modules/@dsh-std/manifest/lib/index.d.ts +182 -0
  279. package/node_modules/@dsh-std/manifest/lib/index.d.ts.map +1 -0
  280. package/node_modules/@dsh-std/manifest/lib/index.js +625 -0
  281. package/node_modules/@dsh-std/manifest/lib/index.js.map +1 -0
  282. package/node_modules/@dsh-std/manifest/package.json +20 -0
  283. package/node_modules/@dsh-std/manifest/schema/dsh-plugin-0.15.schema.json +144 -0
  284. package/node_modules/@dsh-std/messages/CHANGELOG.md +8 -0
  285. package/node_modules/@dsh-std/messages/LICENSE +21 -0
  286. package/node_modules/@dsh-std/messages/README.md +5 -0
  287. package/node_modules/@dsh-std/messages/README.zh.md +7 -0
  288. package/node_modules/@dsh-std/messages/lib/index.d.ts +50 -0
  289. package/node_modules/@dsh-std/messages/lib/index.d.ts.map +1 -0
  290. package/node_modules/@dsh-std/messages/lib/index.js +151 -0
  291. package/node_modules/@dsh-std/messages/lib/index.js.map +1 -0
  292. package/node_modules/@dsh-std/messages/package.json +20 -0
  293. package/node_modules/@dsh-std/messages/schema/message-observer-envelope-0.15.schema.json +54 -0
  294. package/node_modules/@dsh-std/presentation/CHANGELOG.md +9 -0
  295. package/node_modules/@dsh-std/presentation/LICENSE +21 -0
  296. package/node_modules/@dsh-std/presentation/README.md +27 -0
  297. package/node_modules/@dsh-std/presentation/README.zh.md +29 -0
  298. package/node_modules/@dsh-std/presentation/lib/callback.d.ts +2 -0
  299. package/node_modules/@dsh-std/presentation/lib/callback.js +179 -0
  300. package/node_modules/@dsh-std/presentation/lib/callback.js.map +1 -0
  301. package/node_modules/@dsh-std/presentation/lib/index.d.ts +262 -0
  302. package/node_modules/@dsh-std/presentation/lib/index.d.ts.map +1 -0
  303. package/node_modules/@dsh-std/presentation/lib/index.js +550 -0
  304. package/node_modules/@dsh-std/presentation/lib/index.js.map +1 -0
  305. package/node_modules/@dsh-std/presentation/lib/interaction.d.ts +2 -0
  306. package/node_modules/@dsh-std/presentation/lib/interaction.js +2 -0
  307. package/node_modules/@dsh-std/presentation/lib/operations.d.ts +2 -0
  308. package/node_modules/@dsh-std/presentation/lib/operations.js +2 -0
  309. package/node_modules/@dsh-std/presentation/package.json +25 -0
  310. package/node_modules/@dsh-std/storage/CHANGELOG.md +8 -0
  311. package/node_modules/@dsh-std/storage/LICENSE +21 -0
  312. package/node_modules/@dsh-std/storage/README.md +5 -0
  313. package/node_modules/@dsh-std/storage/README.zh.md +7 -0
  314. package/node_modules/@dsh-std/storage/lib/index.d.ts +48 -0
  315. package/node_modules/@dsh-std/storage/lib/index.d.ts.map +1 -0
  316. package/node_modules/@dsh-std/storage/lib/index.js +104 -0
  317. package/node_modules/@dsh-std/storage/lib/index.js.map +1 -0
  318. package/node_modules/@dsh-std/storage/package.json +20 -0
  319. package/node_modules/@dsh-std/storage/schema/local-storage-0.15.schema.json +56 -0
  320. package/package.json +110 -70
  321. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/connection/CHANGELOG.md +8 -0
  322. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/connection/LICENSE +21 -0
  323. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/connection/README.md +42 -0
  324. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/connection/README.zh.md +42 -0
  325. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/connection/lib/connection-CdGcSQnu.d.ts +125 -0
  326. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/connection/lib/connection-CdGcSQnu.d.ts.map +1 -0
  327. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/connection/lib/index.d.ts +89 -0
  328. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/connection/lib/index.d.ts.map +1 -0
  329. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/connection/lib/index.js +158 -0
  330. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/connection/lib/index.js.map +1 -0
  331. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/connection/lib/memory.d.ts +24 -0
  332. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/connection/lib/memory.d.ts.map +1 -0
  333. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/connection/lib/memory.js +258 -0
  334. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/connection/lib/memory.js.map +1 -0
  335. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/connection/lib/resolve-3X6ausgz.js +226 -0
  336. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/connection/lib/resolve-3X6ausgz.js.map +1 -0
  337. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/connection/package.json +20 -0
  338. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/core/CHANGELOG.md +8 -0
  339. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/core/LICENSE +21 -0
  340. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/core/README.md +13 -0
  341. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/core/README.zh.md +13 -0
  342. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/core/lib/index.d.ts +112 -0
  343. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/core/lib/index.d.ts.map +1 -0
  344. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/core/lib/index.js +424 -0
  345. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/core/lib/index.js.map +1 -0
  346. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/core/package.json +21 -0
  347. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/manifest/CHANGELOG.md +9 -0
  348. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/manifest/LICENSE +21 -0
  349. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/manifest/README.md +5 -0
  350. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/manifest/README.zh.md +5 -0
  351. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/manifest/lib/index.d.ts +182 -0
  352. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/manifest/lib/index.d.ts.map +1 -0
  353. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/manifest/lib/index.js +625 -0
  354. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/manifest/lib/index.js.map +1 -0
  355. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/manifest/package.json +20 -0
  356. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/manifest/schema/dsh-plugin-0.15.schema.json +144 -0
  357. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/presentation/CHANGELOG.md +9 -0
  358. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/presentation/LICENSE +21 -0
  359. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/presentation/README.md +27 -0
  360. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/presentation/README.zh.md +29 -0
  361. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/presentation/lib/callback.d.ts +2 -0
  362. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/presentation/lib/callback.js +179 -0
  363. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/presentation/lib/callback.js.map +1 -0
  364. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/presentation/lib/index.d.ts +262 -0
  365. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/presentation/lib/index.d.ts.map +1 -0
  366. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/presentation/lib/index.js +550 -0
  367. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/presentation/lib/index.js.map +1 -0
  368. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/presentation/lib/interaction.d.ts +2 -0
  369. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/presentation/lib/interaction.js +2 -0
  370. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/presentation/lib/operations.d.ts +2 -0
  371. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/presentation/lib/operations.js +2 -0
  372. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/presentation/package.json +25 -0
  373. package/vendor/dsh-std/packages/connection/node_modules/@dsh-std/core/CHANGELOG.md +8 -0
  374. package/vendor/dsh-std/packages/connection/node_modules/@dsh-std/core/LICENSE +21 -0
  375. package/vendor/dsh-std/packages/connection/node_modules/@dsh-std/core/README.md +13 -0
  376. package/vendor/dsh-std/packages/connection/node_modules/@dsh-std/core/README.zh.md +13 -0
  377. package/vendor/dsh-std/packages/connection/node_modules/@dsh-std/core/lib/index.d.ts +112 -0
  378. package/vendor/dsh-std/packages/connection/node_modules/@dsh-std/core/lib/index.d.ts.map +1 -0
  379. package/vendor/dsh-std/packages/connection/node_modules/@dsh-std/core/lib/index.js +424 -0
  380. package/vendor/dsh-std/packages/connection/node_modules/@dsh-std/core/lib/index.js.map +1 -0
  381. package/vendor/dsh-std/packages/connection/node_modules/@dsh-std/core/package.json +21 -0
  382. package/vendor/dsh-std/packages/manifest/node_modules/@dsh-std/core/CHANGELOG.md +8 -0
  383. package/vendor/dsh-std/packages/manifest/node_modules/@dsh-std/core/LICENSE +21 -0
  384. package/vendor/dsh-std/packages/manifest/node_modules/@dsh-std/core/README.md +13 -0
  385. package/vendor/dsh-std/packages/manifest/node_modules/@dsh-std/core/README.zh.md +13 -0
  386. package/vendor/dsh-std/packages/manifest/node_modules/@dsh-std/core/lib/index.d.ts +112 -0
  387. package/vendor/dsh-std/packages/manifest/node_modules/@dsh-std/core/lib/index.d.ts.map +1 -0
  388. package/vendor/dsh-std/packages/manifest/node_modules/@dsh-std/core/lib/index.js +424 -0
  389. package/vendor/dsh-std/packages/manifest/node_modules/@dsh-std/core/lib/index.js.map +1 -0
  390. package/vendor/dsh-std/packages/manifest/node_modules/@dsh-std/core/package.json +21 -0
  391. package/vendor/dsh-std/packages/messages/node_modules/@dsh-std/core/CHANGELOG.md +8 -0
  392. package/vendor/dsh-std/packages/messages/node_modules/@dsh-std/core/LICENSE +21 -0
  393. package/vendor/dsh-std/packages/messages/node_modules/@dsh-std/core/README.md +13 -0
  394. package/vendor/dsh-std/packages/messages/node_modules/@dsh-std/core/README.zh.md +13 -0
  395. package/vendor/dsh-std/packages/messages/node_modules/@dsh-std/core/lib/index.d.ts +112 -0
  396. package/vendor/dsh-std/packages/messages/node_modules/@dsh-std/core/lib/index.d.ts.map +1 -0
  397. package/vendor/dsh-std/packages/messages/node_modules/@dsh-std/core/lib/index.js +424 -0
  398. package/vendor/dsh-std/packages/messages/node_modules/@dsh-std/core/lib/index.js.map +1 -0
  399. package/vendor/dsh-std/packages/messages/node_modules/@dsh-std/core/package.json +21 -0
  400. package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/connection/CHANGELOG.md +8 -0
  401. package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/connection/LICENSE +21 -0
  402. package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/connection/README.md +42 -0
  403. package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/connection/README.zh.md +42 -0
  404. package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/connection/lib/connection-CdGcSQnu.d.ts +125 -0
  405. package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/connection/lib/connection-CdGcSQnu.d.ts.map +1 -0
  406. package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/connection/lib/index.d.ts +89 -0
  407. package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/connection/lib/index.d.ts.map +1 -0
  408. package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/connection/lib/index.js +158 -0
  409. package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/connection/lib/index.js.map +1 -0
  410. package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/connection/lib/memory.d.ts +24 -0
  411. package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/connection/lib/memory.d.ts.map +1 -0
  412. package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/connection/lib/memory.js +258 -0
  413. package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/connection/lib/memory.js.map +1 -0
  414. package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/connection/lib/resolve-3X6ausgz.js +226 -0
  415. package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/connection/lib/resolve-3X6ausgz.js.map +1 -0
  416. package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/connection/package.json +20 -0
  417. package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/core/CHANGELOG.md +8 -0
  418. package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/core/LICENSE +21 -0
  419. package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/core/README.md +13 -0
  420. package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/core/README.zh.md +13 -0
  421. package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/core/lib/index.d.ts +112 -0
  422. package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/core/lib/index.d.ts.map +1 -0
  423. package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/core/lib/index.js +424 -0
  424. package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/core/lib/index.js.map +1 -0
  425. package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/core/package.json +21 -0
  426. package/vendor/dsh-std/packages/storage/node_modules/@dsh-std/core/CHANGELOG.md +8 -0
  427. package/vendor/dsh-std/packages/storage/node_modules/@dsh-std/core/LICENSE +21 -0
  428. package/vendor/dsh-std/packages/storage/node_modules/@dsh-std/core/README.md +13 -0
  429. package/vendor/dsh-std/packages/storage/node_modules/@dsh-std/core/README.zh.md +13 -0
  430. package/vendor/dsh-std/packages/storage/node_modules/@dsh-std/core/lib/index.d.ts +112 -0
  431. package/vendor/dsh-std/packages/storage/node_modules/@dsh-std/core/lib/index.d.ts.map +1 -0
  432. package/vendor/dsh-std/packages/storage/node_modules/@dsh-std/core/lib/index.js +424 -0
  433. package/vendor/dsh-std/packages/storage/node_modules/@dsh-std/core/lib/index.js.map +1 -0
  434. package/vendor/dsh-std/packages/storage/node_modules/@dsh-std/core/package.json +21 -0
@@ -64,6 +64,7 @@ export default class Ink {
64
64
  options;
65
65
  log;
66
66
  terminal;
67
+ app = null;
67
68
  scheduleRender;
68
69
  // Ignore last render after unmounting a tree to prevent empty output before exit
69
70
  isUnmounted = false;
@@ -124,6 +125,11 @@ export default class Ink {
124
125
  // Set alongside altScreenActive so SIGCONT resume knows whether to
125
126
  // re-enable mouse tracking (not all <AlternateScreen> uses want it).
126
127
  altScreenMouseTracking = false;
128
+ // DEC 1049 preserves the physical main screen and cursor. Keep the matching
129
+ // renderer state while an inline full-screen view is active so its exit can
130
+ // diff from what the terminal actually restores instead of printing a full
131
+ // duplicate frame into main-screen scrollback.
132
+ mainScreenFrameState = null;
127
133
  // True when the previous frame's screen buffer cannot be trusted for
128
134
  // blit — selection overlay mutated it, resetFramesForAltScreen()
129
135
  // replaced it with blanks, or forceRedraw() reset it to 0×0. Forces
@@ -417,6 +423,24 @@ export default class Ink {
417
423
  // without the pop we'd accumulate depth on each editor round-trip).
418
424
  this.options.stdout.write('\x1b[?1004h' + (supportsWin32InputMode() ? ENABLE_WIN32_INPUT_MODE : supportsExtendedKeys() ? DISABLE_KITTY_KEYBOARD + ENABLE_KITTY_KEYBOARD + ENABLE_MODIFY_OTHER_KEYS : ''));
419
425
  }
426
+ /**
427
+ * One-shot viewport re-anchor for the NEXT main-screen frame: repaint the
428
+ * visible viewport in place instead of diffing. Exposed for callers that
429
+ * know a layout flip just rewrote the whole frame (Ctrl+O transcript
430
+ * toggle): the ordinary scroll-based diff pushes rows into terminal
431
+ * scrollback on every expand and nothing removes them on collapse — rapid
432
+ * toggles drift the virtual↔scrollback mapping until writes misland.
433
+ * In-place repaint adds nothing to scrollback. No-op in alt-screen
434
+ * (already CSI H-anchored every frame). ONLY sets the flag: the caller's
435
+ * own state change (setExpanded) drives the render that consumes it —
436
+ * forcing an extra render here would paint the OLD layout once more and
437
+ * burn the flag before the real frame lands.
438
+ */
439
+ reanchorViewport() {
440
+ if (this.altScreenActive)
441
+ return;
442
+ this.log.requestViewportReanchor();
443
+ }
420
444
  onRender() {
421
445
  if (this.isUnmounted || this.isPaused) {
422
446
  return;
@@ -855,9 +879,9 @@ export default class Ink {
855
879
  /**
856
880
  * Called by the <AlternateScreen> component on mount/unmount.
857
881
  * Controls cursor.y clamping in the renderer and gates alt-screen-aware
858
- * behavior in SIGCONT/resize/unmount handlers. Repaints on change so
859
- * the first alt-screen frame (and first main-screen frame on exit) is
860
- * a full redraw with no stale diff state.
882
+ * behavior in SIGCONT/resize/unmount handlers. The first alt-screen frame
883
+ * redraws from blank; exit restores the saved main frame for a physical-
884
+ * screen-matched diff, with repaint as the resize fallback.
861
885
  */
862
886
  setAltScreenActive(active, mouseTracking = false) {
863
887
  if (this.altScreenActive === active)
@@ -865,10 +889,31 @@ export default class Ink {
865
889
  this.altScreenActive = active;
866
890
  this.altScreenMouseTracking = active && mouseTracking;
867
891
  if (active) {
892
+ this.mainScreenFrameState = {
893
+ frontFrame: this.frontFrame,
894
+ displayCursor: this.displayCursor,
895
+ columns: this.terminalColumns,
896
+ rows: this.terminalRows
897
+ };
868
898
  this.resetFramesForAltScreen();
869
899
  }
870
900
  else {
871
- this.repaint();
901
+ const saved = this.mainScreenFrameState;
902
+ this.mainScreenFrameState = null;
903
+ if (saved && saved.columns === this.terminalColumns && saved.rows === this.terminalRows) {
904
+ this.frontFrame = saved.frontFrame;
905
+ this.displayCursor = saved.displayCursor;
906
+ this.log.reset();
907
+ // The main React subtree may have changed while the alternate screen
908
+ // was mounted. Disable blitting once, but keep the restored frame as
909
+ // the diff baseline that matches the terminal's physical contents.
910
+ this.prevFrameContaminated = true;
911
+ }
912
+ else {
913
+ // A resize reflows the terminal's saved main buffer, so the old frame
914
+ // is no longer a trustworthy physical baseline.
915
+ this.repaint();
916
+ }
872
917
  }
873
918
  }
874
919
  get isAltScreenActive() {
@@ -954,6 +999,7 @@ export default class Ink {
954
999
  // Cancel any pending throttled render so it doesn't fire between
955
1000
  // cleanupTerminalModes() and process.exit() and write to main screen.
956
1001
  this.scheduleRender.cancel?.();
1002
+ this.app?.detachForShutdown();
957
1003
  // Shutdown bypasses the normal unmount path, so release the process and
958
1004
  // stdout listeners here as well. Otherwise a SIGCONT or resize arriving
959
1005
  // while an updater is running can re-enter the alternate screen or render
@@ -988,6 +1034,39 @@ export default class Ink {
988
1034
  }
989
1035
  }
990
1036
  }
1037
+ /**
1038
+ * Fully detach stdin before handing the terminal to a child process that
1039
+ * inherits it (the /update restart). `detachForShutdown()` restores
1040
+ * cooked mode but leaves the App's 'readable' pump attached — ordinary
1041
+ * exits don't care because the process dies right after, but a parent
1042
+ * that lingers waiting on the child keeps a libuv read pending on the
1043
+ * console and races the child for every keypress: the restarted TUI
1044
+ * sees dropped or entirely swallowed input (issues #284/#307). Remove
1045
+ * the listeners and pause the pump so the child is the sole reader.
1046
+ */
1047
+ detachStdinForHandoff() {
1048
+ const stdin = this.options.stdin;
1049
+ try {
1050
+ this.drainStdin();
1051
+ }
1052
+ catch {
1053
+ // A destroyed stream must not block the handoff.
1054
+ }
1055
+ stdin.removeAllListeners('readable');
1056
+ stdin.removeAllListeners('data');
1057
+ try {
1058
+ stdin.pause();
1059
+ }
1060
+ catch {
1061
+ // Same destroyed-stream tolerance as above.
1062
+ }
1063
+ try {
1064
+ stdin.unref();
1065
+ }
1066
+ catch {
1067
+ // unref on a closed stream can throw on some Node versions.
1068
+ }
1069
+ }
991
1070
  /** @see drainStdin */
992
1071
  drainStdin() {
993
1072
  drainStdin(this.options.stdin);
@@ -1468,9 +1547,12 @@ export default class Ink {
1468
1547
  }
1469
1548
  this.cursorDeclaration = decl;
1470
1549
  };
1550
+ setAppRef(app) {
1551
+ this.app = app;
1552
+ }
1471
1553
  render(node) {
1472
1554
  this.currentNode = node;
1473
- const tree = _jsx(App, { stdin: this.options.stdin, stdout: this.options.stdout, stderr: this.options.stderr, exitOnCtrlC: this.options.exitOnCtrlC, onExit: this.unmount, terminalColumns: this.terminalColumns, terminalRows: this.terminalRows, selection: this.selection, onSelectionChange: this.notifySelectionChange, onClickAt: this.dispatchClick, onHoverAt: this.dispatchHover, getHyperlinkAt: this.getHyperlinkAt, onOpenHyperlink: this.openHyperlink, onMultiClick: this.handleMultiClick, onSelectionDrag: this.handleSelectionDrag, onStdinResume: this.reassertTerminalModes, onCursorDeclaration: this.setCursorDeclaration, dispatchKeyboardEvent: this.dispatchKeyboardEvent, children: _jsx(TerminalWriteProvider, { value: this.writeRaw, children: node }) });
1555
+ const tree = _jsx(App, { ref: this.setAppRef, stdin: this.options.stdin, stdout: this.options.stdout, stderr: this.options.stderr, exitOnCtrlC: this.options.exitOnCtrlC, onExit: this.unmount, terminalColumns: this.terminalColumns, terminalRows: this.terminalRows, selection: this.selection, onSelectionChange: this.notifySelectionChange, onClickAt: this.dispatchClick, onHoverAt: this.dispatchHover, getHyperlinkAt: this.getHyperlinkAt, onOpenHyperlink: this.openHyperlink, onMultiClick: this.handleMultiClick, onSelectionDrag: this.handleSelectionDrag, onStdinResume: this.reassertTerminalModes, onCursorDeclaration: this.setCursorDeclaration, dispatchKeyboardEvent: this.dispatchKeyboardEvent, children: _jsx(TerminalWriteProvider, { value: this.writeRaw, children: node }) });
1474
1556
  // @ts-ignore -- ported CC build; type drift tolerated updateContainerSync exists in react-reconciler but not in @types/react-reconciler
1475
1557
  reconciler.updateContainerSync(tree, this.container, null, noop);
1476
1558
  // @ts-ignore -- ported CC build; type drift tolerated flushSyncWork exists in react-reconciler but not in @types/react-reconciler
@@ -1588,6 +1670,9 @@ export default class Ink {
1588
1670
  // them at the new pools so the next frame's IDs are comparable.
1589
1671
  this.backFrame.screen.charPool = this.charPool;
1590
1672
  this.backFrame.screen.hyperlinkPool = this.hyperlinkPool;
1673
+ if (this.mainScreenFrameState) {
1674
+ migrateScreenPools(this.mainScreenFrameState.frontFrame.screen, this.charPool, this.hyperlinkPool);
1675
+ }
1591
1676
  }
1592
1677
  patchConsole() {
1593
1678
  // biome-ignore lint/suspicious/noConsole: intentionally patching global console
@@ -1 +1 @@
1
- {"version":3,"file":"log-update.d.ts","sourceRoot":"","sources":["../../../src/ink/log-update.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,IAAI,EAAiB,KAAK,EAAE,MAAM,YAAY,CAAA;AAE5D,OAAO,EASL,KAAK,SAAS,EAGf,MAAM,aAAa,CAAA;AAsBpB,KAAK,OAAO,GAAG;IACb,KAAK,EAAE,OAAO,CAAA;IACd,SAAS,EAAE,SAAS,CAAA;CACrB,CAAA;AAKD;;;GAGG;AACH,qBAAa,SAAS;IAGR,OAAO,CAAC,QAAQ,CAAC,OAAO;IAFpC,OAAO,CAAC,KAAK,CAAO;gBAES,OAAO,EAAE,OAAO;IAO7C;;;;;;;;;;;;;;;;;OAiBG;IACH,uBAAuB,IAAI,IAAI;IAI/B;;;;;OAKG;IACH,+BAA+B,CAAC,SAAS,EAAE,KAAK,GAAG,IAAI;IASvD,+HAA+H;IAC/H,KAAK,IAAI,IAAI;IAIb,OAAO,CAAC,eAAe;IAiDvB,OAAO,CAAC,mBAAmB;IAS3B;;;;;;;;OAQG;IACH,MAAM,CACJ,IAAI,EAAE,KAAK,EACX,IAAI,EAAE,KAAK,EACX,SAAS,UAAQ,EACjB,WAAW,UAAO,GACjB,IAAI;CAgbR"}
1
+ {"version":3,"file":"log-update.d.ts","sourceRoot":"","sources":["../../../src/ink/log-update.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,IAAI,EAAiB,KAAK,EAAE,MAAM,YAAY,CAAA;AAE5D,OAAO,EASL,KAAK,SAAS,EAGf,MAAM,aAAa,CAAA;AA4BpB,KAAK,OAAO,GAAG;IACb,KAAK,EAAE,OAAO,CAAA;IACd,SAAS,EAAE,SAAS,CAAA;CACrB,CAAA;AAKD;;;GAGG;AACH,qBAAa,SAAS;IAGR,OAAO,CAAC,QAAQ,CAAC,OAAO;IAFpC,OAAO,CAAC,KAAK,CAAO;gBAES,OAAO,EAAE,OAAO;IAQ7C;;;;;;;;;;;;;;;;;OAiBG;IACH,uBAAuB,IAAI,IAAI;IAI/B;;;;;OAKG;IACH,+BAA+B,CAAC,SAAS,EAAE,KAAK,GAAG,IAAI;IASvD,+HAA+H;IAC/H,KAAK,IAAI,IAAI;IASb,OAAO,CAAC,eAAe;IAiDvB,OAAO,CAAC,mBAAmB;IAS3B;;;;;;;;OAQG;IACH,MAAM,CACJ,IAAI,EAAE,KAAK,EACX,IAAI,EAAE,KAAK,EACX,SAAS,UAAQ,EACjB,WAAW,UAAO,GACjB,IAAI;CAsfR"}
@@ -1,7 +1,7 @@
1
1
  import { ansiCodesToString, diffAnsiCodes, } from '@alcalzone/ansi-tokenize';
2
2
  import { logForDebugging } from '../utils/debug.js';
3
3
  import { cellAt, charInCellAt, diffEach, isEmptyCellAt, shiftRows, visibleCellAtIndex, } from './screen.js';
4
- import { CURSOR_HOME, cursorUp, eraseToEndOfScreen, scrollDown as csiScrollDown, scrollUp as csiScrollUp, RESET_SCROLL_REGION, SGR_RESET, setScrollRegion, } from './termio/csi.js';
4
+ import { CURSOR_HOME, cursorDown, eraseToEndOfScreen, scrollDown as csiScrollDown, scrollUp as csiScrollUp, RESET_SCROLL_REGION, SGR_RESET, setScrollRegion, } from './termio/csi.js';
5
5
  import { isJetBrainsIdeTerminal } from './terminal.js';
6
6
  import { LINK_END, link as oscLink } from './termio/osc.js';
7
7
  const CARRIAGE_RETURN = { type: 'carriageReturn' };
@@ -18,6 +18,7 @@ export class LogUpdate {
18
18
  this.state = {
19
19
  previousOutput: '',
20
20
  reanchorPending: false,
21
+ anchoredPad: 0,
21
22
  };
22
23
  }
23
24
  /**
@@ -58,6 +59,11 @@ export class LogUpdate {
58
59
  /** Drop the previous-output state after the process resumes from suspension (SIGCONT) so terminal content is not clobbered. */
59
60
  reset() {
60
61
  this.state.previousOutput = '';
62
+ // Any anchored-pad offset is void: the next frame repaints from the
63
+ // physical cursor without it (SIGCONT resume, repaint(), forceRedraw,
64
+ // and alt-screen entry all land here — without the clear, rows the
65
+ // repaint just drew at the viewport top would stay marked unreachable).
66
+ this.state.anchoredPad = 0;
61
67
  }
62
68
  renderFullFrame(frame) {
63
69
  const { screen } = frame;
@@ -134,8 +140,13 @@ export class LogUpdate {
134
140
  // not reset here but that would involve predicting the current layout
135
141
  // _after_ the viewport change which means calcuating text wrapping.
136
142
  // Resizing is a rare enough event that it's not practically a big issue.
137
- if (next.viewport.height < prev.viewport.height ||
143
+ if (next.viewport.height !== prev.viewport.height ||
138
144
  (prev.viewport.width !== 0 && next.viewport.width !== prev.viewport.width)) {
145
+ // A resize re-aligns the whole terminal (reflow rewraps scrollback):
146
+ // any anchored-pad offset from a previous shrink repaint is void.
147
+ // Height-only growth is included: the shrink branches below would
148
+ // otherwise mix the old and new viewport heights in their geometry.
149
+ this.state.anchoredPad = 0;
139
150
  return fullResetSequence_CAUSES_FLICKER(next, 'resize', stylePool);
140
151
  }
141
152
  // One-shot viewport re-anchor (see requestViewportReanchor): repaint
@@ -147,6 +158,15 @@ export class LogUpdate {
147
158
  // mutually exclusive anyway).
148
159
  if (this.state.reanchorPending && !altScreen) {
149
160
  this.state.reanchorPending = false;
161
+ // Plain in-place viewport repaint, and the anchored layout (if any)
162
+ // is superseded: pad 0. An earlier revision routed pad>0 frames
163
+ // through shrinkAnchoredRepaint here to preserve the anchored layout
164
+ // — empirically that occasionally painted nothing after alt-screen
165
+ // exits (verify-trace-scene's settle-gap check, ~15% flake), while
166
+ // the plain path is verified stable; a reanchor-with-pad is rare
167
+ // (idle gap right after a settle shrink) and its cost is one stale
168
+ // scrollback copy, not a lost paint.
169
+ this.state.anchoredPad = 0;
150
170
  return repaintViewportInPlace(prev, next, stylePool);
151
171
  }
152
172
  // DECSTBM scroll optimization: when a ScrollBox's scrollTop changed,
@@ -207,19 +227,35 @@ export class LogUpdate {
207
227
  const prevHadScrollback = cursorAtBottom && prev.screen.height >= prev.viewport.height;
208
228
  const isShrinking = next.screen.height < prev.screen.height;
209
229
  const nextFitsViewport = next.screen.height <= prev.viewport.height;
210
- // When shrinking from above-viewport to at-or-below-viewport, content
211
- // that was in scrollback stays there — terminal clears can't pull it
212
- // back — but the viewport itself can be rebuilt in place: the cursor is
213
- // parked at the physical bottom (prevHadScrollback implies it), so the
214
- // viewport rows are all reachable. Repainting them directly avoids the
215
- // former full reset here, whose clearTerminal + whole-frame reprint
216
- // deposited one stale UI copy into the scrollback per shrink.
230
+ // When shrinking from above-viewport to at-or-below-viewport, the rows
231
+ // that scrolled into scrollback during growth come BACK into the live
232
+ // frame, so their scrollback snapshots are now duplicates (seen as the
233
+ // duplicated whale logo after the first turn). Use a clear-terminal
234
+ // reset, not an in-place repaint: at this point the UI's scrollback
235
+ // footprint is at most a viewport tall (prev was at most one row past
236
+ // the viewport), which is exactly what the clear sequence blanks — the
237
+ // in-place repaint kept both copies (snapshot + live). The former full
238
+ // reset here was once changed to an in-place repaint to stop the
239
+ // per-shrink stale-copy deposits (#38 #39 #19), but that fix traded
240
+ // them for a different duplication: the repainted viewport overlaps
241
+ // rows whose growth snapshots are still in scrollback.
217
242
  // Use <= (not <) because even when next height equals viewport height,
218
243
  // the scrollback depth from the previous render differs from a fresh
219
244
  // render.
220
- if (prevHadScrollback && nextFitsViewport && isShrinking) {
221
- logForDebugging(`Viewport repaint (shrink->below): prevHeight=${prev.screen.height}, nextHeight=${next.screen.height}, viewport=${prev.viewport.height}`);
222
- return repaintViewportInPlace(prev, next, stylePool);
245
+ if (!altScreen && prevHadScrollback && nextFitsViewport && isShrinking) {
246
+ // Anchored tail repaint (see shrinkAnchoredRepaint): rows already in
247
+ // scrollback keep their originals; only the changed tail is
248
+ // repainted. The former full reset here reprinted the whole short
249
+ // frame from the viewport top — duplicating every pre-shrink row the
250
+ // user scrolls up to (the whale-logo / duplicated-transcript
251
+ // reports).
252
+ const scrollbackRows = Math.max(0, Math.max(prev.screen.height, next.screen.height) -
253
+ next.viewport.height +
254
+ (prevHadScrollback ? 1 : 0));
255
+ const { patches, anchoredPad } = shrinkAnchoredRepaint(prev, next, stylePool, scrollbackRows);
256
+ this.state.anchoredPad = anchoredPad;
257
+ logForDebugging(`Anchored shrink repaint (shrink->below): prevHeight=${prev.screen.height}, nextHeight=${next.screen.height}, viewport=${prev.viewport.height}, skip=${anchoredPad}`);
258
+ return patches;
223
259
  }
224
260
  // Steady-state scrollback check removed: rows above the viewport used
225
261
  // to force a full reset (ESC[2J+ESC[3J) whenever they changed, which
@@ -240,21 +276,42 @@ export class LogUpdate {
240
276
  const linesToClear = prev.screen.height - next.screen.height;
241
277
  // Main-screen mode + content taller than the viewport: the terminal
242
278
  // viewport does NOT follow the content bottom when content shrinks —
243
- // a diff-loop path would write rows at stale physical offsets. But
244
- // when the cursor sits on its park row (the invariant the renderer
245
- // maintains every main-screen frame), the whole viewport is directly
246
- // addressable: repaint it in place. This branch fires on EVERY turn
247
- // (thinking fold, spinner/esc-hint unmount, markdown reflow shrink
248
- // the frame by a row or two while it is taller than the viewport) —
249
- // the former full reset here was the copy machine behind the
250
- // duplicated-UI scrollback reports (#38 #39 #19). Fall back to the
251
- // full reset only if the cursor is somewhere unexpected.
252
- if (!altScreen && prev.screen.height > prev.viewport.height) {
253
- if (cursorAtBottom) {
254
- logForDebugging(`Viewport repaint (shrink tall): prevHeight=${prev.screen.height}, nextHeight=${next.screen.height}, viewport=${prev.viewport.height}`);
255
- return repaintViewportInPlace(prev, next, this.options.stylePool);
256
- }
257
- return fullResetSequence_CAUSES_FLICKER(next, 'offscreen', this.options.stylePool);
279
+ // a diff-loop path would write rows at stale physical offsets. Two
280
+ // sub-cases:
281
+ //
282
+ // 1. Frame shrinks but stays >= viewport height: take the ordinary
283
+ // diff loop. Its viewportY bookkeeping (the shrinking formula plus
284
+ // cursorRestoreScroll below) addresses rows through the parked
285
+ // cursor correctly, and the diff only touches the few rows that
286
+ // actually changed. A viewport repaint here re-paints the whole
287
+ // window from the viewport top while the frame top sits a few rows
288
+ // ABOVE it (frame is taller than the window) — the growth frames
289
+ // around the shrink freeze those overlapped rows into scrollback
290
+ // as snapshots while the same rows stay live in the window, which
291
+ // the user sees as duplicated transcript rows when scrolling up.
292
+ // 2. Frame shrinks to BELOW the viewport (content now fits): the rows
293
+ // that scrolled into scrollback during growth come BACK into the
294
+ // live frame, so their scrollback snapshots are now duplicates
295
+ // (seen as the duplicated whale logo after the first turn). Use a
296
+ // clear-terminal reset here, not an in-place repaint: at this
297
+ // point the UI's scrollback footprint is at most a viewport tall,
298
+ // which is exactly what the clear sequence blanks, while the
299
+ // in-place repaint would keep both copies. Only fires early in a
300
+ // session (frame ~= viewport); once the transcript outgrows the
301
+ // viewport, case 1 handles every shrink.
302
+ if (!altScreen &&
303
+ prev.screen.height > prev.viewport.height &&
304
+ next.screen.height < next.viewport.height) {
305
+ // Same anchored tail repaint as the shrink->below branch above —
306
+ // rows already in scrollback keep their originals; only the
307
+ // non-scrollback tail is repainted. The former full reset
308
+ // reprinted the whole frame, duplicating the scrollback rows
309
+ // (whale-logo / duplicated-transcript reports).
310
+ const scrollbackRows = Math.max(0, prev.screen.height - prev.viewport.height + cursorRestoreScroll);
311
+ const { patches, anchoredPad } = shrinkAnchoredRepaint(prev, next, this.options.stylePool, scrollbackRows);
312
+ this.state.anchoredPad = anchoredPad;
313
+ logForDebugging(`Anchored shrink repaint (shrink to fit): prevHeight=${prev.screen.height}, nextHeight=${next.screen.height}, viewport=${prev.viewport.height}, skip=${anchoredPad}`);
314
+ return patches;
258
315
  }
259
316
  // eraseLines only works within the viewport - it can't clear scrollback.
260
317
  // If we need to clear more lines than fit in the viewport, some are in
@@ -280,11 +337,18 @@ export class LogUpdate {
280
337
  // an additional row out of view at the end of the previous frame. Without
281
338
  // this, the diff loop treats that row as reachable — but the cursor clamps
282
339
  // at viewport top, causing writes to land 1 row off and garbling the output.
283
- const viewportY = growing
340
+ // Unreachable frame rows = terminal scrollback. Two contributors, take
341
+ // the MAX, never the sum: after an anchored shrink repaint the pad rows
342
+ // ARE the scrollback (heights undercount while H < V); once growth
343
+ // scrolls the blank band away, the heights formula takes over (summing
344
+ // would overcount and skip REACHABLE viewport rows — changes inside
345
+ // the viewport would never paint, scrambling the layout on mid-frame
346
+ // edits like Ctrl+O expansion).
347
+ const viewportY = Math.max(this.state.anchoredPad, growing
284
348
  ? Math.max(0, prev.screen.height - prev.viewport.height + cursorRestoreScroll)
285
349
  : Math.max(prev.screen.height, next.screen.height) -
286
350
  next.viewport.height +
287
- cursorRestoreScroll;
351
+ cursorRestoreScroll);
288
352
  // Rows above viewportY live in terminal scrollback and are skipped by the
289
353
  // diff loop (see below). Clip the damage region to the visible area so the
290
354
  // loop doesn't scan scrollback rows cell-by-cell every frame — with a tall
@@ -518,9 +582,82 @@ function fullResetSequence_CAUSES_FLICKER(frame, reason, stylePool, debug) {
518
582
  const contentRows = Math.min(height, Math.max(1, frame.viewport.height - 1));
519
583
  const startY = height - contentRows;
520
584
  const screen = new VirtualScreen({ x: 0, y: startY }, frame.viewport.width);
521
- renderFrameSlice(screen, frame, startY, height, stylePool);
585
+ renderFrameSlice(screen, frame, startY, height, stylePool, false);
522
586
  return [{ type: 'clearTerminal', reason, debug }, ...screen.diff];
523
587
  }
588
+ /**
589
+ * Anchored tail repaint for shrink-to-fit frames — the fix for the
590
+ * duplicated-whale/duplicated-transcript family. When growth pushed frame
591
+ * rows into terminal scrollback and a shrink then brings the frame back
592
+ * below the viewport, repainting the whole (short) frame from the viewport
593
+ * top re-prints rows whose originals already sit in scrollback — the user
594
+ * scrolls up and sees every pre-shrink row twice.
595
+ *
596
+ * Two layouts, decided by content:
597
+ * - The new frame's top rows are UNCHANGED from the previous frame (the
598
+ * common small shrink — a fold, a spinner unmounting): those rows
599
+ * already live in scrollback and keep their originals. Erase the
600
+ * viewport in place (no 2J — history untouched) and repaint ONLY the
601
+ * changed tail, anchored at the viewport BOTTOM so the frame continues
602
+ * the scrollback seamlessly. The leading blank band is recorded as
603
+ * `anchoredPad` so later frames' viewportY math skips the scrollback
604
+ * rows and relative addressing stays consistent (the band scrolls away
605
+ * naturally as the next turn grows).
606
+ * - The frame's top content CHANGED (a panel collapsing, 38 rows → 2):
607
+ * nothing in scrollback represents the new frame — repaint the whole
608
+ * short frame top-anchored, matching a fresh render (collapse-shrink's
609
+ * contract), pad 0.
610
+ */
611
+ function shrinkAnchoredRepaint(prev, next, stylePool, scrollbackRows) {
612
+ // Seam check, not prefix equality: the scrollback's DEEPEST row (old row
613
+ // skip-1) sits directly above the region we are about to paint — if it
614
+ // equals the new frame's row at the same index, the frame's lineage is
615
+ // intact (the shrink removed rows from the bottom region) and the
616
+ // scrollback originals stay authoritative. Volatile top rows (the
617
+ // status header's ticking counters) don't matter — only the seam does.
618
+ // Walk the skip down until the seam matches; 0 means the frame's top
619
+ // content changed (a collapsing panel) and the whole short frame gets a
620
+ // top-anchored repaint instead.
621
+ const width = Math.min(prev.screen.width, next.screen.width);
622
+ const rowsDiffer = (y) => {
623
+ if (y >= prev.screen.height || y >= next.screen.height)
624
+ return true;
625
+ const po = y * prev.screen.width * 2;
626
+ const no = y * next.screen.width * 2;
627
+ for (let x = 0; x < width * 2; x++) {
628
+ if (prev.screen.cells[po + x] !== next.screen.cells[no + x])
629
+ return true;
630
+ }
631
+ return false;
632
+ };
633
+ let skip = Math.min(scrollbackRows, next.screen.height);
634
+ while (skip > 0 && rowsDiffer(skip - 1))
635
+ skip -= 1;
636
+ const viewportHeight = prev.viewport.height;
637
+ const height = next.screen.height;
638
+ if (skip <= 0) {
639
+ // Top-anchored whole-frame repaint — the fresh-render layout.
640
+ const contentRows = Math.min(height, Math.max(1, viewportHeight - 1));
641
+ const startY = height - contentRows;
642
+ const anchor = CURSOR_HOME + eraseToEndOfScreen();
643
+ const screen = new VirtualScreen({ x: 0, y: startY }, next.viewport.width);
644
+ renderFrameSlice(screen, next, startY, height, stylePool, false);
645
+ return { patches: [{ type: 'stdout', content: anchor }, ...screen.diff], anchoredPad: 0 };
646
+ }
647
+ const rowsToPaint = Math.max(1, Math.min(height, viewportHeight - 1, height - skip));
648
+ const startY = height - rowsToPaint;
649
+ const blankBand = Math.max(0, viewportHeight - 1 - rowsToPaint);
650
+ const anchor = CURSOR_HOME + eraseToEndOfScreen() + (blankBand > 0 ? cursorDown(blankBand) : '');
651
+ const screen = new VirtualScreen({ x: 0, y: startY }, next.viewport.width);
652
+ renderFrameSlice(screen, next, startY, height, stylePool, false);
653
+ return {
654
+ patches: [{ type: 'stdout', content: anchor }, ...screen.diff],
655
+ // Cap at height-1: a seam that matches every row of a very short frame
656
+ // must not mark the WHOLE frame as scrollback — the last row (the
657
+ // input area) stays live and repaintable.
658
+ anchoredPad: Math.min(skip, height - 1),
659
+ };
660
+ }
524
661
  /**
525
662
  * Repaint the visible viewport in place — the main-screen shrink recovery
526
663
  * that replaces fullResetSequence's clearTerminal + whole-frame reprint.
@@ -576,27 +713,42 @@ function viewportRepaintPatches(prev, next, stylePool, resetPrefix, origin) {
576
713
  // steady state, where the park LF materializes one row below the frame).
577
714
  const contentRows = Math.min(height, viewportHeight - 1);
578
715
  const startY = height - contentRows;
579
- // A standalone recovery starts at the parked physical bottom row and must
580
- // reach the viewport top. An appended repaint starts at next.cursor (the
581
- // frame end), so moving up contentRows reaches the actual visible-frame
582
- // start for both short and overflowing frames without crossing into shell
583
- // history. Erase down from there (BCE fill — the SGR reset guarantees the
584
- // default background). The erase also blanks the park row, so no stale
716
+ // Anchor with CSI H (cursor to viewport origin) instead of a RELATIVE
717
+ // cursor-up walk. The relative form assumed the physical cursor sits
718
+ // exactly on the park row — an assumption that breaks after a terminal
719
+ // reflow (a width change rewraps scrollback, shifting the buffer/cursor
720
+ // bookkeeping by a row or two). The over-shot cursor-up lands ABOVE the
721
+ // viewport top, and the subsequent erase+repaint writes a full UI copy
722
+ // into the scrollback (duplicated-history reports). CSI H has absolute
723
+ // semantics — it always lands on the current viewport's top-left — so the
724
+ // repaint can never cross into scrollback regardless of cursor drift.
725
+ // Erase down from there (BCE fill — the SGR reset guarantees the default
726
+ // background). The erase also blanks the park row, so no stale
585
727
  // prompt/status pixels survive below short content.
586
- const rowsToStart = origin === 'viewport-bottom' ? viewportHeight - 1 : contentRows;
587
728
  const anchor = (resetPrefix ? SGR_RESET + LINK_END : '') +
588
- '\r' +
589
- cursorUp(rowsToStart) +
729
+ CURSOR_HOME +
590
730
  eraseToEndOfScreen();
591
731
  const screen = new VirtualScreen({ x: 0, y: startY }, next.viewport.width);
592
- renderFrameSlice(screen, next, startY, height, stylePool);
732
+ renderFrameSlice(screen, next, startY, height, stylePool, false);
593
733
  return [{ type: 'stdout', content: anchor }, ...screen.diff];
594
734
  }
595
735
  /**
596
736
  * Render a slice of rows from the frame's screen.
597
737
  * Each row is rendered followed by a newline. Cursor ends at (0, endY).
598
738
  */
599
- function renderFrameSlice(screen, frame, startY, endY, stylePool) {
739
+ function renderFrameSlice(screen, frame, startY, endY, stylePool,
740
+ /**
741
+ * When false (repaint / full-reset paths), advance rows with
742
+ * CR + cursor-down instead of bare LFs. The repaint paints exactly a
743
+ * viewport of rows starting at the viewport top; a trailing LF on the
744
+ * bottom row SCROLLS the terminal, pushing a copy of the just-painted
745
+ * rows into the scrollback that the cursor bookkeeping never accounted
746
+ * for — every later relative move lands offset, writing content into
747
+ * the history (duplicated transcript rows on scroll-up). cursor-down
748
+ * clamps at the bottom margin instead of scrolling, so the physical
749
+ * cursor stays exactly where the model expects it.
750
+ */
751
+ allowScroll = true) {
600
752
  let currentStyleId = stylePool.none;
601
753
  let currentHyperlink = undefined;
602
754
  // Track the styleId of the last rendered cell on this line (-1 if none).
@@ -605,22 +757,30 @@ function renderFrameSlice(screen, frame, startY, endY, stylePool) {
605
757
  const { width: screenWidth, cells, charPool, hyperlinkPool } = frame.screen;
606
758
  let index = startY * screenWidth;
607
759
  for (let y = startY; y < endY; y += 1) {
608
- // Advance cursor to this row using LF (not CSI CUD / cursor-down).
609
- // CSI CUD stops at the viewport bottom margin and cannot scroll,
610
- // but LF scrolls the viewport to create new lines. Without this,
760
+ // Advance cursor to this row. With allowScroll, use LF (not CSI CUD /
761
+ // cursor-down): CSI CUD stops at the viewport bottom margin and cannot
762
+ // scroll, but LF scrolls the viewport to create new lines. Without this,
611
763
  // when the cursor is at the viewport bottom, moveCursorTo's
612
764
  // cursor-down silently fails, creating a permanent off-by-one
613
765
  // between the virtual cursor and the real terminal cursor.
614
766
  if (screen.cursor.y < y) {
615
767
  const rowsToAdvance = y - screen.cursor.y;
616
- screen.txn(prev => {
617
- const patches = new Array(1 + rowsToAdvance);
618
- patches[0] = CARRIAGE_RETURN;
619
- for (let i = 0; i < rowsToAdvance; i++) {
620
- patches[1 + i] = NEWLINE;
621
- }
622
- return [patches, { dx: -prev.x, dy: rowsToAdvance }];
623
- });
768
+ if (allowScroll) {
769
+ screen.txn(prev => {
770
+ const patches = new Array(1 + rowsToAdvance);
771
+ patches[0] = CARRIAGE_RETURN;
772
+ for (let i = 0; i < rowsToAdvance; i++) {
773
+ patches[1 + i] = NEWLINE;
774
+ }
775
+ return [patches, { dx: -prev.x, dy: rowsToAdvance }];
776
+ });
777
+ }
778
+ else {
779
+ screen.txn(prev => [
780
+ [CARRIAGE_RETURN, { type: 'cursorMove', x: 0, y: rowsToAdvance }],
781
+ { dx: -prev.x, dy: rowsToAdvance },
782
+ ]);
783
+ }
624
784
  }
625
785
  // Reset at start of each line — no cell rendered yet
626
786
  lastRenderedStyleId = -1;
@@ -650,10 +810,19 @@ function renderFrameSlice(screen, frame, startY, endY, stylePool) {
650
810
  // skip empty cells, we must reset explicitly.
651
811
  currentStyleId = transitionStyle(screen.diff, stylePool, currentStyleId, stylePool.none);
652
812
  currentHyperlink = transitionHyperlink(screen.diff, currentHyperlink, undefined);
653
- // CR+LF at end of row — \r resets to column 0, \n moves to next line.
654
- // Without \r, the terminal cursor stays at whatever column content ended
655
- // (since we skip trailing spaces, this can be mid-row).
656
- screen.txn(prev => [[CARRIAGE_RETURN, NEWLINE], { dx: -prev.x, dy: 1 }]);
813
+ // CR+LF (or CR+cursor-down for non-scrolling slices) at end of row —
814
+ // \r resets to column 0. Without \r, the terminal cursor stays at
815
+ // whatever column content ended (since we skip trailing spaces, this
816
+ // can be mid-row).
817
+ if (allowScroll) {
818
+ screen.txn(prev => [[CARRIAGE_RETURN, NEWLINE], { dx: -prev.x, dy: 1 }]);
819
+ }
820
+ else {
821
+ screen.txn(prev => [
822
+ [CARRIAGE_RETURN, { type: 'cursorMove', x: 0, y: 1 }],
823
+ { dx: -prev.x, dy: 1 },
824
+ ]);
825
+ }
657
826
  }
658
827
  // Reset any open style/hyperlink at end of slice
659
828
  transitionStyle(screen.diff, stylePool, currentStyleId, stylePool.none);
@@ -22,16 +22,29 @@ type ClusteredChar = {
22
22
  * no caller can accidentally grow it unboundedly.
23
23
  */
24
24
  export declare class CharCache {
25
+ /**
26
+ * LRU of line → clustered characters. A full clear on overflow
27
+ * (the original behavior) thrashes during long-session streaming: the
28
+ * mounted window's static lines alone approach the char budget, so the
29
+ * streaming row's per-frame new keys tip it over every frame or two,
30
+ * nuking the cache and forcing ~1800 line re-tokenizations per frame
31
+ * (measured ~100ms/frame at a 175-row session). Evicting oldest-first
32
+ * keeps the hot mounted lines resident while streaming churn falls out
33
+ * the back. Recency is refreshed on hit (delete + re-insert) so lines
34
+ * read every frame are never evicted by one-shot churn.
35
+ */
25
36
  private map;
26
37
  private chars;
27
38
  /**
28
- * Return the cached clustered characters for a line, if present.
39
+ * Return the cached clustered characters for a line, if present, and
40
+ * refresh the entry's recency.
29
41
  * @param line - the line to look up.
30
42
  * @returns the cached characters, or undefined on a miss.
31
43
  */
32
44
  get(line: string): ClusteredChar[] | undefined;
33
45
  /**
34
- * Cache clustered characters for a line, enforcing the cache bounds.
46
+ * Cache clustered characters for a line, enforcing the cache bounds by
47
+ * evicting least-recently-used entries (never a full clear).
35
48
  * @param line - the line key.
36
49
  * @param characters - the clustered characters to cache.
37
50
  */
@@ -130,6 +143,26 @@ export default class Output {
130
143
  private screen;
131
144
  private readonly operations;
132
145
  private charCache;
146
+ /**
147
+ * LRU of line → recorded packed cell run, mirroring charCache's bounds.
148
+ * The main-screen architecture repaints every settled line each frame;
149
+ * the recorded run replays as raw two-word stores (no interning, no
150
+ * packing, no guard branches) — the long-session streaming fix. Runs are
151
+ * validated against the screen they were interned on and die with it.
152
+ */
153
+ private packedLines;
154
+ private packedChars;
155
+ /** Stable owner handle passed to writeLineToScreen (no per-call closure). */
156
+ private readonly packedOwner;
157
+ /** Insert into the packed-lines LRU, evicting oldest entries on budget. */
158
+ private setPackedLine;
159
+ /**
160
+ * Streaming-tail slot, shared by every writeLineToScreen call of this
161
+ * Output (only one line grows per frame during streaming). Survives reset()
162
+ * like charCache - it is keyed by strict line extension, so stale entries
163
+ * can never produce a wrong hit.
164
+ */
165
+ private streamingSlot;
133
166
  constructor(options: Options);
134
167
  /**
135
168
  * Reuse this Output for a new frame. Zeroes the screen buffer, clears
@@ -1 +1 @@
1
- {"version":3,"file":"output.d.ts","sourceRoot":"","sources":["../../../src/ink/output.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"output.d.ts","sourceRoot":"","sources":["../../../src/ink/output.ts"],"names":[],"mappings":"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"}