@neurocode-ai/app 1.18.8

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 (577) hide show
  1. package/AGENTS.md +35 -0
  2. package/README.md +50 -0
  3. package/V1_API_MIGRATION.md +220 -0
  4. package/bunfig.toml +3 -0
  5. package/create-effect-simplification-spec.md +515 -0
  6. package/e2e/performance/AGENTS.md +13 -0
  7. package/e2e/performance/README.md +79 -0
  8. package/e2e/performance/benchmark.ts +144 -0
  9. package/e2e/performance/chrome-trace.ts +95 -0
  10. package/e2e/performance/playwright.config.ts +20 -0
  11. package/e2e/performance/playwright.uncapped.config.ts +13 -0
  12. package/e2e/performance/timeline/first-navigation-benchmark.spec.ts +87 -0
  13. package/e2e/performance/timeline/first-navigation-metrics.ts +32 -0
  14. package/e2e/performance/timeline/first-navigation-probe.ts +86 -0
  15. package/e2e/performance/timeline/home-tab-navigation-benchmark.spec.ts +114 -0
  16. package/e2e/performance/timeline/navigation-milestones.ts +128 -0
  17. package/e2e/performance/timeline/review-pane-scaling-benchmark.spec.ts +312 -0
  18. package/e2e/performance/timeline/session-parent-hydration-benchmark.spec.ts +172 -0
  19. package/e2e/performance/timeline/session-tab-flash.spec.ts +67 -0
  20. package/e2e/performance/timeline/session-tab-repaint-probe.ts +251 -0
  21. package/e2e/performance/timeline/session-tab-switch-benchmark.spec.ts +144 -0
  22. package/e2e/performance/timeline/session-tab-switch-metrics.ts +59 -0
  23. package/e2e/performance/timeline/session-tab-switch-probe.ts +226 -0
  24. package/e2e/performance/timeline/session-timeline-benchmark.fixture.ts +504 -0
  25. package/e2e/performance/timeline/session-timeline-benchmark.spec.ts +306 -0
  26. package/e2e/performance/timeline/session-timeline-profile.ts +40 -0
  27. package/e2e/performance/timeline/session-timeline-stream-probe.ts +547 -0
  28. package/e2e/performance/timeline/session-timeline-stress.fixture.ts +369 -0
  29. package/e2e/performance/timeline/timeline-test-helpers.ts +134 -0
  30. package/e2e/performance/timeline-stability/README.md +61 -0
  31. package/e2e/performance/timeline-stability/adverse.spec.ts +250 -0
  32. package/e2e/performance/timeline-stability/context-matrix.spec.ts +192 -0
  33. package/e2e/performance/timeline-stability/environment-matrix.spec.ts +113 -0
  34. package/e2e/performance/timeline-stability/file-matrix.spec.ts +121 -0
  35. package/e2e/performance/timeline-stability/file-mutation.spec.ts +113 -0
  36. package/e2e/performance/timeline-stability/fixture.test.ts +68 -0
  37. package/e2e/performance/timeline-stability/fixture.ts +566 -0
  38. package/e2e/performance/timeline-stability/interaction.spec.ts +242 -0
  39. package/e2e/performance/timeline-stability/lifecycle.spec.ts +157 -0
  40. package/e2e/performance/timeline-stability/oracle-browser.spec.ts +75 -0
  41. package/e2e/performance/timeline-stability/playwright.config.ts +17 -0
  42. package/e2e/performance/timeline-stability/scroll-interaction.spec.ts +353 -0
  43. package/e2e/performance/timeline-stability/shell-matrix.spec.ts +255 -0
  44. package/e2e/performance/timeline-stability/tool-mutation.spec.ts +106 -0
  45. package/e2e/performance/timeline-stability/tools.spec.ts +198 -0
  46. package/e2e/performance/timeline-stability/transition-matrix.spec.ts +272 -0
  47. package/e2e/performance/unit/chrome-trace-write.test.ts +15 -0
  48. package/e2e/performance/unit/first-navigation-metrics.test.ts +32 -0
  49. package/e2e/performance/unit/mock-server.test.ts +46 -0
  50. package/e2e/performance/unit/navigation-milestones.test.ts +34 -0
  51. package/e2e/performance/unit/session-tab-repaint-probe.test.ts +42 -0
  52. package/e2e/performance/unit/session-tab-switch-metrics.test.ts +86 -0
  53. package/e2e/performance/unit/session-tab-switch-probe.test.ts +55 -0
  54. package/e2e/performance/unit/session-timeline-stream-probe.test.ts +14 -0
  55. package/e2e/performance/unit/session-timeline-visual-tracking.test.ts +16 -0
  56. package/e2e/performance/unit/timeline-test-helpers.test.ts +10 -0
  57. package/e2e/performance/unit/visual-stability.test.ts +392 -0
  58. package/e2e/regression/cross-server-tab-close.spec.ts +153 -0
  59. package/e2e/regression/file-browser-sidebar-tab-switch.spec.ts +150 -0
  60. package/e2e/regression/legacy-new-session.spec.ts +41 -0
  61. package/e2e/regression/new-session-panel-corner.spec.ts +83 -0
  62. package/e2e/regression/prompt-input-v2-command-draft.spec.ts +50 -0
  63. package/e2e/regression/prompt-thinking-level.spec.ts +84 -0
  64. package/e2e/regression/remote-session-settings.spec.ts +302 -0
  65. package/e2e/regression/remote-tab-busy.spec.ts +131 -0
  66. package/e2e/regression/review-image-flash.spec.ts +202 -0
  67. package/e2e/regression/review-line-comment.spec.ts +158 -0
  68. package/e2e/regression/review-open-file.spec.ts +163 -0
  69. package/e2e/regression/review-state-persistence.spec.ts +155 -0
  70. package/e2e/regression/review-tab-switch.spec.ts +147 -0
  71. package/e2e/regression/review-terminal-stacked.spec.ts +308 -0
  72. package/e2e/regression/session-list-path-loading.spec.ts +41 -0
  73. package/e2e/regression/session-request-docks.spec.ts +221 -0
  74. package/e2e/regression/session-timeline-accessibility.spec.ts +22 -0
  75. package/e2e/regression/session-timeline-collapse-state.spec.ts +439 -0
  76. package/e2e/regression/session-timeline-context-resize.spec.ts +374 -0
  77. package/e2e/regression/session-timeline-context-state.spec.ts +31 -0
  78. package/e2e/regression/session-timeline-file-projection.spec.ts +52 -0
  79. package/e2e/regression/session-timeline-file-state.spec.ts +94 -0
  80. package/e2e/regression/session-timeline-history-root.spec.ts +242 -0
  81. package/e2e/regression/session-timeline-lifecycle-state.spec.ts +111 -0
  82. package/e2e/regression/session-timeline-locale-projection.spec.ts +25 -0
  83. package/e2e/regression/session-timeline-projection.spec.ts +287 -0
  84. package/e2e/regression/session-timeline-reasoning-projection.spec.ts +93 -0
  85. package/e2e/regression/session-timeline-reducer-projection.spec.ts +43 -0
  86. package/e2e/regression/session-timeline-shell-outline.spec.ts +228 -0
  87. package/e2e/regression/session-timeline-tool-projection.spec.ts +99 -0
  88. package/e2e/regression/session-timeline-tool-state.spec.ts +77 -0
  89. package/e2e/regression/session-timeline-transport.spec.ts +116 -0
  90. package/e2e/regression/session-todo-dock-navigation.spec.ts +190 -0
  91. package/e2e/regression/subagent-child-navigation.spec.ts +203 -0
  92. package/e2e/regression/tab-navigate-mousedown.spec.ts +127 -0
  93. package/e2e/regression/terminal-composer-focus.spec.ts +227 -0
  94. package/e2e/regression/terminal-hidden.spec.ts +117 -0
  95. package/e2e/regression/terminal-tab-switch.spec.ts +165 -0
  96. package/e2e/reproduction/timeline-suspense/index.html +34 -0
  97. package/e2e/reproduction/timeline-suspense/main.tsx +315 -0
  98. package/e2e/reproduction/timeline-suspense/playwright.config.ts +34 -0
  99. package/e2e/reproduction/timeline-suspense/timeline-suspense.repro.ts +179 -0
  100. package/e2e/reproduction/timeline-suspense/vite.config.ts +7 -0
  101. package/e2e/smoke/session-timeline.fixture.ts +315 -0
  102. package/e2e/smoke/session-timeline.spec.ts +740 -0
  103. package/e2e/tsconfig.json +20 -0
  104. package/e2e/utils/errors.ts +18 -0
  105. package/e2e/utils/mock-server.ts +443 -0
  106. package/e2e/utils/sse-transport.ts +312 -0
  107. package/e2e/utils/visual-stability/analyzer.ts +209 -0
  108. package/e2e/utils/visual-stability/capture.ts +51 -0
  109. package/e2e/utils/visual-stability/index.ts +8 -0
  110. package/e2e/utils/visual-stability/invariant.ts +112 -0
  111. package/e2e/utils/visual-stability/model.ts +47 -0
  112. package/e2e/utils/visual-stability/probe.ts +226 -0
  113. package/e2e/utils/visual-stability/regions.ts +18 -0
  114. package/e2e/utils/visual-stability/reporter.ts +63 -0
  115. package/e2e/utils/visual-stability/scenario.ts +20 -0
  116. package/e2e/utils/visual-stability.ts +54 -0
  117. package/e2e/utils/waits.ts +11 -0
  118. package/happydom.ts +75 -0
  119. package/index.html +28 -0
  120. package/package.json +96 -0
  121. package/playwright.config.ts +45 -0
  122. package/public/_headers +17 -0
  123. package/public/apple-touch-icon-v3.png +1 -0
  124. package/public/apple-touch-icon.png +1 -0
  125. package/public/assets/Inter.ttf +0 -0
  126. package/public/assets/JetBrainsMonoNerdFontMono-Regular.woff2 +0 -0
  127. package/public/favicon-96x96-v3.png +1 -0
  128. package/public/favicon-96x96.png +1 -0
  129. package/public/favicon-v3.ico +1 -0
  130. package/public/favicon-v3.svg +1 -0
  131. package/public/favicon.ico +1 -0
  132. package/public/favicon.svg +1 -0
  133. package/public/oc-theme-preload.js +40 -0
  134. package/public/site.webmanifest +1 -0
  135. package/public/social-share-zen.png +1 -0
  136. package/public/social-share.png +1 -0
  137. package/public/web-app-manifest-192x192.png +1 -0
  138. package/public/web-app-manifest-512x512.png +1 -0
  139. package/src/addons/serialize.test.ts +327 -0
  140. package/src/addons/serialize.ts +642 -0
  141. package/src/app.tsx +659 -0
  142. package/src/assets/help/home.png +0 -0
  143. package/src/assets/help/introducing-tabs.mp4 +0 -0
  144. package/src/assets/help/placeholder.png +0 -0
  145. package/src/assets/help/tabs.png +0 -0
  146. package/src/components/command-palette.ts +281 -0
  147. package/src/components/command-tooltip-keybind.test.ts +22 -0
  148. package/src/components/command-tooltip-keybind.ts +11 -0
  149. package/src/components/debug-bar.tsx +550 -0
  150. package/src/components/dialog-command-palette-v2.css +220 -0
  151. package/src/components/dialog-command-palette-v2.tsx +342 -0
  152. package/src/components/dialog-connect-provider.stories.tsx +73 -0
  153. package/src/components/dialog-connect-provider.tsx +1178 -0
  154. package/src/components/dialog-custom-provider-form.ts +158 -0
  155. package/src/components/dialog-custom-provider.test.ts +80 -0
  156. package/src/components/dialog-custom-provider.tsx +330 -0
  157. package/src/components/dialog-edit-project-v2.tsx +156 -0
  158. package/src/components/dialog-edit-project.tsx +170 -0
  159. package/src/components/dialog-fork.tsx +104 -0
  160. package/src/components/dialog-manage-models.tsx +265 -0
  161. package/src/components/dialog-release-notes.tsx +144 -0
  162. package/src/components/dialog-select-directory-v2.css +107 -0
  163. package/src/components/dialog-select-directory-v2.tsx +378 -0
  164. package/src/components/dialog-select-directory.tsx +191 -0
  165. package/src/components/dialog-select-file.tsx +189 -0
  166. package/src/components/dialog-select-mcp.tsx +92 -0
  167. package/src/components/dialog-select-model-search.test.ts +19 -0
  168. package/src/components/dialog-select-model-search.ts +18 -0
  169. package/src/components/dialog-select-model-unpaid-v2.stories.tsx +55 -0
  170. package/src/components/dialog-select-model-unpaid-v2.tsx +175 -0
  171. package/src/components/dialog-select-model-unpaid.tsx +147 -0
  172. package/src/components/dialog-select-model.tsx +549 -0
  173. package/src/components/dialog-select-server.tsx +722 -0
  174. package/src/components/dialog-settings.tsx +94 -0
  175. package/src/components/dialog-usage-exceeded.tsx +44 -0
  176. package/src/components/directory-picker-domain.test.ts +248 -0
  177. package/src/components/directory-picker-domain.ts +407 -0
  178. package/src/components/directory-picker-policy.ts +7 -0
  179. package/src/components/directory-picker.test.ts +21 -0
  180. package/src/components/directory-picker.tsx +45 -0
  181. package/src/components/edit-project.ts +122 -0
  182. package/src/components/file-tree-v2-model.test.ts +74 -0
  183. package/src/components/file-tree-v2-model.ts +107 -0
  184. package/src/components/file-tree-v2.tsx +299 -0
  185. package/src/components/file-tree.test.ts +80 -0
  186. package/src/components/file-tree.tsx +509 -0
  187. package/src/components/help-button.tsx +148 -0
  188. package/src/components/link.tsx +26 -0
  189. package/src/components/model-tooltip.tsx +124 -0
  190. package/src/components/pierre-tree.test.ts +23 -0
  191. package/src/components/prompt-input/attachments.test.ts +108 -0
  192. package/src/components/prompt-input/attachments.ts +228 -0
  193. package/src/components/prompt-input/build-request-parts.test.ts +396 -0
  194. package/src/components/prompt-input/build-request-parts.ts +213 -0
  195. package/src/components/prompt-input/context-items.tsx +98 -0
  196. package/src/components/prompt-input/contracts.ts +57 -0
  197. package/src/components/prompt-input/drag-overlay.tsx +25 -0
  198. package/src/components/prompt-input/editor-dom.test.ts +99 -0
  199. package/src/components/prompt-input/editor-dom.ts +148 -0
  200. package/src/components/prompt-input/files.ts +98 -0
  201. package/src/components/prompt-input/history-store.ts +47 -0
  202. package/src/components/prompt-input/history.test.ts +153 -0
  203. package/src/components/prompt-input/history.ts +256 -0
  204. package/src/components/prompt-input/image-attachments.css +43 -0
  205. package/src/components/prompt-input/image-attachments.tsx +168 -0
  206. package/src/components/prompt-input/paste.ts +24 -0
  207. package/src/components/prompt-input/placeholder.test.ts +48 -0
  208. package/src/components/prompt-input/placeholder.ts +20 -0
  209. package/src/components/prompt-input/slash-popover.tsx +376 -0
  210. package/src/components/prompt-input/submission-state.ts +33 -0
  211. package/src/components/prompt-input/submit.test.ts +598 -0
  212. package/src/components/prompt-input/submit.ts +634 -0
  213. package/src/components/prompt-input/transient-state.ts +46 -0
  214. package/src/components/prompt-input-v2.tsx +592 -0
  215. package/src/components/prompt-input.stories.tsx +223 -0
  216. package/src/components/prompt-input.tsx +1790 -0
  217. package/src/components/prompt-project-selector.tsx +593 -0
  218. package/src/components/prompt-workspace-selector.tsx +128 -0
  219. package/src/components/server/server-row-menu.tsx +96 -0
  220. package/src/components/server/server-row.tsx +127 -0
  221. package/src/components/session/index.ts +7 -0
  222. package/src/components/session/open-in-app-v2.tsx +98 -0
  223. package/src/components/session/open-in-app.tsx +229 -0
  224. package/src/components/session/session-context-breakdown.test.ts +61 -0
  225. package/src/components/session/session-context-breakdown.ts +132 -0
  226. package/src/components/session/session-context-format.ts +20 -0
  227. package/src/components/session/session-context-metrics.test.ts +99 -0
  228. package/src/components/session/session-context-metrics.ts +65 -0
  229. package/src/components/session/session-context-tab.tsx +343 -0
  230. package/src/components/session/session-header.tsx +568 -0
  231. package/src/components/session/session-new-design-view.tsx +16 -0
  232. package/src/components/session/session-new-view.tsx +91 -0
  233. package/src/components/session/session-sortable-tab-v2.tsx +74 -0
  234. package/src/components/session/session-sortable-tab.tsx +78 -0
  235. package/src/components/session/session-sortable-terminal-tab-v2.tsx +284 -0
  236. package/src/components/session/session-sortable-terminal-tab.tsx +193 -0
  237. package/src/components/session-context-usage.tsx +169 -0
  238. package/src/components/settings-dialog.tsx +43 -0
  239. package/src/components/settings-general.tsx +794 -0
  240. package/src/components/settings-keybinds.tsx +553 -0
  241. package/src/components/settings-list.tsx +5 -0
  242. package/src/components/settings-models.tsx +149 -0
  243. package/src/components/settings-providers.tsx +264 -0
  244. package/src/components/settings-server-picker.tsx +106 -0
  245. package/src/components/settings-servers.tsx +33 -0
  246. package/src/components/settings-v2/dialog-server-v2.tsx +134 -0
  247. package/src/components/settings-v2/dialog-settings-v2.tsx +98 -0
  248. package/src/components/settings-v2/general.tsx +722 -0
  249. package/src/components/settings-v2/index.tsx +1 -0
  250. package/src/components/settings-v2/interface-transition.stories.tsx +76 -0
  251. package/src/components/settings-v2/interface-transition.tsx +54 -0
  252. package/src/components/settings-v2/models.tsx +138 -0
  253. package/src/components/settings-v2/parts/list.tsx +6 -0
  254. package/src/components/settings-v2/parts/row.tsx +20 -0
  255. package/src/components/settings-v2/providers.tsx +264 -0
  256. package/src/components/settings-v2/servers.tsx +139 -0
  257. package/src/components/settings-v2/settings-v2.css +675 -0
  258. package/src/components/status-popover-body.tsx +514 -0
  259. package/src/components/status-popover-indicator.test.ts +36 -0
  260. package/src/components/status-popover-indicator.ts +20 -0
  261. package/src/components/status-popover.tsx +190 -0
  262. package/src/components/terminal.tsx +759 -0
  263. package/src/components/titlebar-history.test.ts +63 -0
  264. package/src/components/titlebar-history.ts +57 -0
  265. package/src/components/titlebar-session-events.test.ts +32 -0
  266. package/src/components/titlebar-session-events.ts +35 -0
  267. package/src/components/titlebar-tab-gesture.test.ts +33 -0
  268. package/src/components/titlebar-tab-gesture.ts +17 -0
  269. package/src/components/titlebar-tab-nav.css +100 -0
  270. package/src/components/titlebar-tab-nav.tsx +429 -0
  271. package/src/components/titlebar-tab-popover.css +127 -0
  272. package/src/components/titlebar-tab-popover.tsx +92 -0
  273. package/src/components/titlebar-tab-strip.tsx +333 -0
  274. package/src/components/titlebar.css +77 -0
  275. package/src/components/titlebar.tsx +776 -0
  276. package/src/components/ui/drawer.tsx +119 -0
  277. package/src/components/updater-action.test.ts +26 -0
  278. package/src/components/updater-action.ts +51 -0
  279. package/src/components/virtual-scroll-element.test.ts +18 -0
  280. package/src/components/virtual-scroll-element.ts +4 -0
  281. package/src/components/windows-app-menu.tsx +131 -0
  282. package/src/constants/file-picker.ts +89 -0
  283. package/src/context/closed-tabs.ts +40 -0
  284. package/src/context/command-keybind.test.ts +69 -0
  285. package/src/context/command.test.ts +55 -0
  286. package/src/context/command.tsx +467 -0
  287. package/src/context/comments.test.ts +188 -0
  288. package/src/context/comments.tsx +261 -0
  289. package/src/context/directory-sync.ts +155 -0
  290. package/src/context/file/content-cache.ts +88 -0
  291. package/src/context/file/path.test.ts +360 -0
  292. package/src/context/file/path.ts +151 -0
  293. package/src/context/file/tree-store.ts +174 -0
  294. package/src/context/file/types.ts +41 -0
  295. package/src/context/file/view-cache.ts +147 -0
  296. package/src/context/file/watcher.test.ts +149 -0
  297. package/src/context/file/watcher.ts +53 -0
  298. package/src/context/file-content-eviction-accounting.test.ts +65 -0
  299. package/src/context/file.tsx +303 -0
  300. package/src/context/global-sync/bootstrap.test.ts +231 -0
  301. package/src/context/global-sync/bootstrap.ts +536 -0
  302. package/src/context/global-sync/child-store.test.ts +276 -0
  303. package/src/context/global-sync/child-store.ts +396 -0
  304. package/src/context/global-sync/event-reducer.test.ts +616 -0
  305. package/src/context/global-sync/event-reducer.ts +478 -0
  306. package/src/context/global-sync/eviction.ts +28 -0
  307. package/src/context/global-sync/home-session-index.test.ts +154 -0
  308. package/src/context/global-sync/home-session-index.ts +174 -0
  309. package/src/context/global-sync/mcp.test.ts +54 -0
  310. package/src/context/global-sync/mcp.ts +19 -0
  311. package/src/context/global-sync/queue.test.ts +46 -0
  312. package/src/context/global-sync/queue.ts +87 -0
  313. package/src/context/global-sync/session-cache.test.ts +104 -0
  314. package/src/context/global-sync/session-cache.ts +62 -0
  315. package/src/context/global-sync/session-load.ts +33 -0
  316. package/src/context/global-sync/session-trim.test.ts +59 -0
  317. package/src/context/global-sync/session-trim.ts +57 -0
  318. package/src/context/global-sync/types.ts +135 -0
  319. package/src/context/global-sync/utils.test.ts +133 -0
  320. package/src/context/global-sync/utils.ts +171 -0
  321. package/src/context/global.tsx +161 -0
  322. package/src/context/highlights.tsx +233 -0
  323. package/src/context/language.tsx +243 -0
  324. package/src/context/layout-helpers.ts +38 -0
  325. package/src/context/layout-scroll.test.ts +64 -0
  326. package/src/context/layout-scroll.ts +126 -0
  327. package/src/context/layout-tabs.test.ts +82 -0
  328. package/src/context/layout-tabs.ts +103 -0
  329. package/src/context/layout.test.ts +69 -0
  330. package/src/context/layout.tsx +1075 -0
  331. package/src/context/local-agent.test.ts +29 -0
  332. package/src/context/local-agent.ts +7 -0
  333. package/src/context/local.tsx +417 -0
  334. package/src/context/mcp.ts +19 -0
  335. package/src/context/model-variant.test.ts +86 -0
  336. package/src/context/model-variant.ts +52 -0
  337. package/src/context/models.tsx +173 -0
  338. package/src/context/notification.tsx +478 -0
  339. package/src/context/permission-auto-respond.test.ts +125 -0
  340. package/src/context/permission-auto-respond.ts +60 -0
  341. package/src/context/permission.tsx +484 -0
  342. package/src/context/platform.tsx +142 -0
  343. package/src/context/prompt-state.test.ts +29 -0
  344. package/src/context/prompt-state.ts +266 -0
  345. package/src/context/prompt.tsx +170 -0
  346. package/src/context/sdk.tsx +17 -0
  347. package/src/context/server-sdk.test.ts +196 -0
  348. package/src/context/server-sdk.tsx +444 -0
  349. package/src/context/server-session-v2-reducer.test.ts +156 -0
  350. package/src/context/server-session-v2-reducer.ts +502 -0
  351. package/src/context/server-session.test.ts +1638 -0
  352. package/src/context/server-session.ts +1425 -0
  353. package/src/context/server-sync.test.ts +231 -0
  354. package/src/context/server-sync.tsx +753 -0
  355. package/src/context/server.test.ts +245 -0
  356. package/src/context/server.tsx +360 -0
  357. package/src/context/settings.test.ts +79 -0
  358. package/src/context/settings.tsx +531 -0
  359. package/src/context/sync-optimistic.test.ts +123 -0
  360. package/src/context/sync.tsx +119 -0
  361. package/src/context/tab-memory.ts +36 -0
  362. package/src/context/tabs.test.ts +107 -0
  363. package/src/context/tabs.tsx +387 -0
  364. package/src/context/terminal-title.ts +24 -0
  365. package/src/context/terminal.test.ts +91 -0
  366. package/src/context/terminal.tsx +546 -0
  367. package/src/custom-elements.d.ts +1 -0
  368. package/src/desktop-menu.test.ts +13 -0
  369. package/src/desktop-menu.ts +223 -0
  370. package/src/entry.tsx +182 -0
  371. package/src/env.d.ts +31 -0
  372. package/src/hooks/provider-catalog.test.ts +59 -0
  373. package/src/hooks/provider-catalog.ts +27 -0
  374. package/src/hooks/use-providers.ts +71 -0
  375. package/src/i18n/ar.ts +1008 -0
  376. package/src/i18n/br.ts +1024 -0
  377. package/src/i18n/bs.ts +1100 -0
  378. package/src/i18n/da.ts +1092 -0
  379. package/src/i18n/de.ts +1037 -0
  380. package/src/i18n/en.ts +1102 -0
  381. package/src/i18n/es.ts +1108 -0
  382. package/src/i18n/fr.ts +1037 -0
  383. package/src/i18n/ja.ts +1016 -0
  384. package/src/i18n/ko.ts +1015 -0
  385. package/src/i18n/no.ts +1111 -0
  386. package/src/i18n/parity.test.ts +118 -0
  387. package/src/i18n/pl.ts +1024 -0
  388. package/src/i18n/ru.ts +1103 -0
  389. package/src/i18n/th.ts +1087 -0
  390. package/src/i18n/tr.ts +1106 -0
  391. package/src/i18n/uk.ts +1105 -0
  392. package/src/i18n/zh.ts +1078 -0
  393. package/src/i18n/zht.ts +1074 -0
  394. package/src/index.css +330 -0
  395. package/src/index.ts +30 -0
  396. package/src/pages/directory-layout.tsx +122 -0
  397. package/src/pages/error-description.test.ts +17 -0
  398. package/src/pages/error-description.ts +11 -0
  399. package/src/pages/error.tsx +372 -0
  400. package/src/pages/home/home-controller.ts +108 -0
  401. package/src/pages/home/home-projects-controller.tsx +128 -0
  402. package/src/pages/home/home-projects-view.tsx +615 -0
  403. package/src/pages/home/home-projects.tsx +40 -0
  404. package/src/pages/home/home-scroll-controller.ts +145 -0
  405. package/src/pages/home/home-session-search-controller.ts +114 -0
  406. package/src/pages/home/home-sessions-controller.tsx +314 -0
  407. package/src/pages/home/home-sessions-view.tsx +550 -0
  408. package/src/pages/home/home-sessions.tsx +48 -0
  409. package/src/pages/home/legacy-home.tsx +142 -0
  410. package/src/pages/home-session-archive.test.ts +51 -0
  411. package/src/pages/home-session-archive.ts +27 -0
  412. package/src/pages/home-session-open.test.ts +31 -0
  413. package/src/pages/home-session-open.ts +14 -0
  414. package/src/pages/home.tsx +50 -0
  415. package/src/pages/layout/deep-links.ts +50 -0
  416. package/src/pages/layout/helpers.test.ts +321 -0
  417. package/src/pages/layout/helpers.ts +150 -0
  418. package/src/pages/layout/inline-editor.tsx +126 -0
  419. package/src/pages/layout/project-avatar-state.ts +49 -0
  420. package/src/pages/layout/session-tab-avatar.tsx +60 -0
  421. package/src/pages/layout/sidebar-items.tsx +335 -0
  422. package/src/pages/layout/sidebar-project.tsx +377 -0
  423. package/src/pages/layout/sidebar-shell.tsx +125 -0
  424. package/src/pages/layout/sidebar-workspace.tsx +488 -0
  425. package/src/pages/layout-new.tsx +53 -0
  426. package/src/pages/layout.tsx +2441 -0
  427. package/src/pages/new-session.tsx +282 -0
  428. package/src/pages/session/composer/index.ts +4 -0
  429. package/src/pages/session/composer/prompt-model-selection.ts +133 -0
  430. package/src/pages/session/composer/session-composer-controls.ts +129 -0
  431. package/src/pages/session/composer/session-composer-region-controller.ts +145 -0
  432. package/src/pages/session/composer/session-composer-region.tsx +168 -0
  433. package/src/pages/session/composer/session-composer-state.test.ts +138 -0
  434. package/src/pages/session/composer/session-composer-state.ts +204 -0
  435. package/src/pages/session/composer/session-followup-dock.tsx +109 -0
  436. package/src/pages/session/composer/session-permission-dock.tsx +74 -0
  437. package/src/pages/session/composer/session-question-dock.tsx +640 -0
  438. package/src/pages/session/composer/session-request-tree.ts +52 -0
  439. package/src/pages/session/composer/session-revert-dock.stories.tsx +124 -0
  440. package/src/pages/session/composer/session-revert-dock.tsx +188 -0
  441. package/src/pages/session/composer/session-todo-dock.tsx +277 -0
  442. package/src/pages/session/composer/todo-panel-motion.stories.tsx +622 -0
  443. package/src/pages/session/file-tab-scroll.test.ts +40 -0
  444. package/src/pages/session/file-tab-scroll.ts +67 -0
  445. package/src/pages/session/file-tabs.tsx +800 -0
  446. package/src/pages/session/handoff.ts +36 -0
  447. package/src/pages/session/helpers.test.ts +214 -0
  448. package/src/pages/session/helpers.ts +209 -0
  449. package/src/pages/session/message-gesture.test.ts +62 -0
  450. package/src/pages/session/message-gesture.ts +21 -0
  451. package/src/pages/session/message-id-from-hash.ts +6 -0
  452. package/src/pages/session/new-session-layout.ts +2 -0
  453. package/src/pages/session/review-tab.tsx +172 -0
  454. package/src/pages/session/session-layout.ts +30 -0
  455. package/src/pages/session/session-lineage.ts +69 -0
  456. package/src/pages/session/session-model-helpers.test.ts +151 -0
  457. package/src/pages/session/session-model-helpers.ts +63 -0
  458. package/src/pages/session/session-ownership.ts +37 -0
  459. package/src/pages/session/session-panel-layout.test.ts +19 -0
  460. package/src/pages/session/session-panel-layout.ts +6 -0
  461. package/src/pages/session/session-panel-width.test.ts +52 -0
  462. package/src/pages/session/session-panel-width.ts +19 -0
  463. package/src/pages/session/session-side-panel.tsx +865 -0
  464. package/src/pages/session/terminal-label.ts +16 -0
  465. package/src/pages/session/terminal-panel-v2.tsx +352 -0
  466. package/src/pages/session/terminal-panel.test.ts +25 -0
  467. package/src/pages/session/terminal-panel.tsx +343 -0
  468. package/src/pages/session/timeline/measure.test.ts +30 -0
  469. package/src/pages/session/timeline/measure.ts +5 -0
  470. package/src/pages/session/timeline/message-timeline.tsx +1864 -0
  471. package/src/pages/session/timeline/model.test.ts +95 -0
  472. package/src/pages/session/timeline/model.ts +128 -0
  473. package/src/pages/session/timeline/observe-element-offset.test.ts +199 -0
  474. package/src/pages/session/timeline/observe-element-offset.ts +73 -0
  475. package/src/pages/session/timeline/projection.test.ts +96 -0
  476. package/src/pages/session/timeline/projection.ts +81 -0
  477. package/src/pages/session/timeline/row-reconciliation.ts +56 -0
  478. package/src/pages/session/timeline/rows-current.test.ts +170 -0
  479. package/src/pages/session/timeline/rows.ts +344 -0
  480. package/src/pages/session/timeline/summary-diffs.test.ts +42 -0
  481. package/src/pages/session/timeline/summary-diffs.ts +20 -0
  482. package/src/pages/session/timeline/timeline-row.ts +80 -0
  483. package/src/pages/session/timeline/virtual-items.ts +3 -0
  484. package/src/pages/session/usage-exceeded-dialogs.tsx +104 -0
  485. package/src/pages/session/use-composer-commands.tsx +83 -0
  486. package/src/pages/session/use-session-commands.tsx +606 -0
  487. package/src/pages/session/use-session-hash-scroll.test.ts +16 -0
  488. package/src/pages/session/use-session-hash-scroll.ts +202 -0
  489. package/src/pages/session/v2/review-diff-kinds.test.ts +70 -0
  490. package/src/pages/session/v2/review-diff-kinds.ts +62 -0
  491. package/src/pages/session/v2/review-panel-v2-state.ts +41 -0
  492. package/src/pages/session/v2/review-panel-v2.tsx +255 -0
  493. package/src/pages/session/v2/session-file-browser-tab.tsx +181 -0
  494. package/src/pages/session/v2/session-file-list-v2.tsx +166 -0
  495. package/src/pages/session.tsx +2385 -0
  496. package/src/sst-env.d.ts +12 -0
  497. package/src/theme-preload.test.ts +46 -0
  498. package/src/updater.ts +17 -0
  499. package/src/utils/agent.ts +44 -0
  500. package/src/utils/aim.ts +138 -0
  501. package/src/utils/base64.ts +10 -0
  502. package/src/utils/comment-note.ts +88 -0
  503. package/src/utils/diffs.test.ts +75 -0
  504. package/src/utils/diffs.ts +50 -0
  505. package/src/utils/file-manager.ts +24 -0
  506. package/src/utils/id.ts +93 -0
  507. package/src/utils/notification-click.test.ts +27 -0
  508. package/src/utils/notification-click.ts +13 -0
  509. package/src/utils/path-key.ts +24 -0
  510. package/src/utils/persist.test.ts +211 -0
  511. package/src/utils/persist.ts +661 -0
  512. package/src/utils/prompt.test.ts +44 -0
  513. package/src/utils/prompt.ts +203 -0
  514. package/src/utils/refcount.test.ts +49 -0
  515. package/src/utils/refcount.ts +32 -0
  516. package/src/utils/runtime-adapters.test.ts +64 -0
  517. package/src/utils/runtime-adapters.ts +39 -0
  518. package/src/utils/same.ts +6 -0
  519. package/src/utils/scoped-cache.test.ts +69 -0
  520. package/src/utils/scoped-cache.ts +104 -0
  521. package/src/utils/search-keydown.ts +116 -0
  522. package/src/utils/server-compat.test.ts +193 -0
  523. package/src/utils/server-compat.ts +512 -0
  524. package/src/utils/server-errors.test.ts +175 -0
  525. package/src/utils/server-errors.ts +109 -0
  526. package/src/utils/server-health.test.ts +178 -0
  527. package/src/utils/server-health.ts +172 -0
  528. package/src/utils/server-protocol.test.ts +40 -0
  529. package/src/utils/server-protocol.ts +35 -0
  530. package/src/utils/server-scope.test.ts +61 -0
  531. package/src/utils/server-scope.ts +73 -0
  532. package/src/utils/server.test.ts +23 -0
  533. package/src/utils/server.ts +62 -0
  534. package/src/utils/session-message.test.ts +214 -0
  535. package/src/utils/session-message.ts +355 -0
  536. package/src/utils/session-route.test.ts +71 -0
  537. package/src/utils/session-route.ts +39 -0
  538. package/src/utils/session-title.ts +7 -0
  539. package/src/utils/session.test.ts +94 -0
  540. package/src/utils/session.ts +37 -0
  541. package/src/utils/solid-dnd.tsx +49 -0
  542. package/src/utils/sound.ts +102 -0
  543. package/src/utils/terminal-websocket-url.test.ts +80 -0
  544. package/src/utils/terminal-websocket-url.ts +35 -0
  545. package/src/utils/terminal-writer.test.ts +64 -0
  546. package/src/utils/terminal-writer.ts +65 -0
  547. package/src/utils/time.ts +22 -0
  548. package/src/utils/toast.tsx +34 -0
  549. package/src/utils/uuid.test.ts +78 -0
  550. package/src/utils/uuid.ts +12 -0
  551. package/src/utils/worktree.test.ts +58 -0
  552. package/src/utils/worktree.ts +76 -0
  553. package/src/wsl/add-server-probes.ts +57 -0
  554. package/src/wsl/context.tsx +36 -0
  555. package/src/wsl/dialog-add-server.tsx +463 -0
  556. package/src/wsl/dialog-add-wsl-server.css +392 -0
  557. package/src/wsl/settings-model.test.ts +231 -0
  558. package/src/wsl/settings-model.ts +341 -0
  559. package/src/wsl/settings.tsx +173 -0
  560. package/src/wsl/types.ts +85 -0
  561. package/sst-env.d.ts +10 -0
  562. package/test-browser/command-palette.test.ts +78 -0
  563. package/test-browser/motion-spring.test.ts +20 -0
  564. package/test-browser/prompt-attachments.test.ts +90 -0
  565. package/test-browser/prompt-persistence.test.ts +71 -0
  566. package/test-browser/prompt-scope.test.ts +14 -0
  567. package/test-browser/prompt-submission-state.test.ts +73 -0
  568. package/test-browser/prompt-transient-state.test.ts +38 -0
  569. package/test-browser/review-panel-v2-state.test.ts +65 -0
  570. package/test-browser/session-lineage.test.ts +231 -0
  571. package/test-browser/session-ownership.test.ts +45 -0
  572. package/test-browser/solid-virtual.test.ts +112 -0
  573. package/test-browser/toast-owner.test.ts +26 -0
  574. package/tsconfig.json +26 -0
  575. package/vendor/opencode-ai-client-1.17.13.tgz +0 -0
  576. package/vite.config.ts +33 -0
  577. package/vite.js +48 -0
@@ -0,0 +1,642 @@
1
+ /**
2
+ * SerializeAddon - Serialize terminal buffer contents
3
+ *
4
+ * Port of xterm.js addon-serialize for ghostty-web.
5
+ * Enables serialization of terminal contents to a string that can
6
+ * be written back to restore terminal state.
7
+ *
8
+ * Usage:
9
+ * ```typescript
10
+ * const serializeAddon = new SerializeAddon();
11
+ * term.loadAddon(serializeAddon);
12
+ * const content = serializeAddon.serialize();
13
+ * ```
14
+ */
15
+
16
+ import type { ITerminalAddon, ITerminalCore, IBufferRange } from "ghostty-web"
17
+
18
+ // ============================================================================
19
+ // Buffer Types (matching ghostty-web internal interfaces)
20
+ // ============================================================================
21
+
22
+ interface IBuffer {
23
+ readonly type: "normal" | "alternate"
24
+ readonly cursorX: number
25
+ readonly cursorY: number
26
+ readonly viewportY: number
27
+ readonly baseY: number
28
+ readonly length: number
29
+ getLine(y: number): IBufferLine | undefined
30
+ getNullCell(): IBufferCell
31
+ }
32
+
33
+ interface IBufferLine {
34
+ readonly length: number
35
+ readonly isWrapped: boolean
36
+ getCell(x: number): IBufferCell | undefined
37
+ translateToString(trimRight?: boolean, startColumn?: number, endColumn?: number): string
38
+ }
39
+
40
+ interface IBufferCell {
41
+ getChars(): string
42
+ getCode(): number
43
+ getWidth(): number
44
+ getFgColorMode(): number
45
+ getBgColorMode(): number
46
+ getFgColor(): number
47
+ getBgColor(): number
48
+ isBold(): number
49
+ isItalic(): number
50
+ isUnderline(): number
51
+ isStrikethrough(): number
52
+ isBlink(): number
53
+ isInverse(): number
54
+ isInvisible(): number
55
+ isFaint(): number
56
+ isDim(): boolean
57
+ }
58
+
59
+ type TerminalBuffers = {
60
+ active?: IBuffer
61
+ normal?: IBuffer
62
+ alternate?: IBuffer
63
+ }
64
+
65
+ const isRecord = (value: unknown): value is Record<string, unknown> => {
66
+ return typeof value === "object" && value !== null
67
+ }
68
+
69
+ const isBuffer = (value: unknown): value is IBuffer => {
70
+ if (!isRecord(value)) return false
71
+ if (typeof value.length !== "number") return false
72
+ if (typeof value.cursorX !== "number") return false
73
+ if (typeof value.cursorY !== "number") return false
74
+ if (typeof value.baseY !== "number") return false
75
+ if (typeof value.viewportY !== "number") return false
76
+ if (typeof value.getLine !== "function") return false
77
+ if (typeof value.getNullCell !== "function") return false
78
+ return true
79
+ }
80
+
81
+ const getTerminalBuffers = (value: ITerminalCore): TerminalBuffers | undefined => {
82
+ if (!isRecord(value)) return
83
+ const raw = value.buffer
84
+ if (!isRecord(raw)) return
85
+ const active = isBuffer(raw.active) ? raw.active : undefined
86
+ const normal = isBuffer(raw.normal) ? raw.normal : undefined
87
+ const alternate = isBuffer(raw.alternate) ? raw.alternate : undefined
88
+ if (!active && !normal) return
89
+ return { active, normal, alternate }
90
+ }
91
+
92
+ const getTerminalMode = (value: ITerminalCore, mode: number) => {
93
+ if (!isRecord(value)) return false
94
+ const terminal = value.wasmTerm
95
+ if (!isRecord(terminal) || typeof terminal.getMode !== "function") return false
96
+ return terminal.getMode(mode) === true
97
+ }
98
+
99
+ // ============================================================================
100
+ // Types
101
+ // ============================================================================
102
+
103
+ export interface ISerializeOptions {
104
+ /**
105
+ * The row range to serialize. When an explicit range is specified, the cursor
106
+ * will get its final repositioning.
107
+ */
108
+ range?: ISerializeRange
109
+ /**
110
+ * The number of rows in the scrollback buffer to serialize, starting from
111
+ * the bottom of the scrollback buffer. When not specified, all available
112
+ * rows in the scrollback buffer will be serialized.
113
+ */
114
+ scrollback?: number
115
+ /**
116
+ * Whether to exclude the terminal modes from the serialization.
117
+ * Default: false
118
+ */
119
+ excludeModes?: boolean
120
+ /**
121
+ * Whether to exclude the alt buffer from the serialization.
122
+ * Default: false
123
+ */
124
+ excludeAltBuffer?: boolean
125
+ }
126
+
127
+ export interface ISerializeRange {
128
+ /**
129
+ * The line to start serializing (inclusive).
130
+ */
131
+ start: number
132
+ /**
133
+ * The line to end serializing (inclusive).
134
+ */
135
+ end: number
136
+ }
137
+
138
+ export interface IHTMLSerializeOptions {
139
+ /**
140
+ * The number of rows in the scrollback buffer to serialize, starting from
141
+ * the bottom of the scrollback buffer.
142
+ */
143
+ scrollback?: number
144
+ /**
145
+ * Whether to only serialize the selection.
146
+ * Default: false
147
+ */
148
+ onlySelection?: boolean
149
+ /**
150
+ * Whether to include the global background of the terminal.
151
+ * Default: false
152
+ */
153
+ includeGlobalBackground?: boolean
154
+ /**
155
+ * The range to serialize. This is prioritized over onlySelection.
156
+ */
157
+ range?: {
158
+ startLine: number
159
+ endLine: number
160
+ startCol: number
161
+ }
162
+ }
163
+
164
+ // ============================================================================
165
+ // Helper Functions
166
+ // ============================================================================
167
+
168
+ function constrain(value: number, low: number, high: number): number {
169
+ return Math.max(low, Math.min(value, high))
170
+ }
171
+
172
+ function equalFg(cell1: IBufferCell, cell2: IBufferCell): boolean {
173
+ return cell1.getFgColorMode() === cell2.getFgColorMode() && cell1.getFgColor() === cell2.getFgColor()
174
+ }
175
+
176
+ function equalBg(cell1: IBufferCell, cell2: IBufferCell): boolean {
177
+ return cell1.getBgColorMode() === cell2.getBgColorMode() && cell1.getBgColor() === cell2.getBgColor()
178
+ }
179
+
180
+ function equalFlags(cell1: IBufferCell, cell2: IBufferCell): boolean {
181
+ return (
182
+ !!cell1.isInverse() === !!cell2.isInverse() &&
183
+ !!cell1.isBold() === !!cell2.isBold() &&
184
+ !!cell1.isUnderline() === !!cell2.isUnderline() &&
185
+ !!cell1.isBlink() === !!cell2.isBlink() &&
186
+ !!cell1.isInvisible() === !!cell2.isInvisible() &&
187
+ !!cell1.isItalic() === !!cell2.isItalic() &&
188
+ !!cell1.isDim() === !!cell2.isDim() &&
189
+ !!cell1.isStrikethrough() === !!cell2.isStrikethrough()
190
+ )
191
+ }
192
+
193
+ // ============================================================================
194
+ // Base Serialize Handler
195
+ // ============================================================================
196
+
197
+ abstract class BaseSerializeHandler {
198
+ constructor(protected readonly _buffer: IBuffer) {}
199
+
200
+ public serialize(range: IBufferRange, excludeFinalCursorPosition?: boolean): string {
201
+ let oldCell = this._buffer.getNullCell()
202
+
203
+ const startRow = range.start.y
204
+ const endRow = range.end.y
205
+ const startColumn = range.start.x
206
+ const endColumn = range.end.x
207
+
208
+ this._beforeSerialize(endRow - startRow + 1, startRow, endRow)
209
+
210
+ for (let row = startRow; row <= endRow; row++) {
211
+ const line = this._buffer.getLine(row)
212
+ if (line) {
213
+ const startLineColumn = row === range.start.y ? startColumn : 0
214
+ const endLineColumn = Math.min(endColumn, line.length)
215
+
216
+ for (let col = startLineColumn; col < endLineColumn; col++) {
217
+ const c = line.getCell(col)
218
+ if (!c) {
219
+ continue
220
+ }
221
+ this._nextCell(c, oldCell, row, col)
222
+ oldCell = c
223
+ }
224
+ }
225
+ this._rowEnd(row, row === endRow)
226
+ }
227
+
228
+ this._afterSerialize()
229
+
230
+ return this._serializeString(excludeFinalCursorPosition)
231
+ }
232
+
233
+ protected _nextCell(_cell: IBufferCell, _oldCell: IBufferCell, _row: number, _col: number): void {}
234
+ protected _rowEnd(_row: number, _isLastRow: boolean): void {}
235
+ protected _beforeSerialize(_rows: number, _startRow: number, _endRow: number): void {}
236
+ protected _afterSerialize(): void {}
237
+ protected _serializeString(_excludeFinalCursorPosition?: boolean): string {
238
+ return ""
239
+ }
240
+ }
241
+
242
+ // ============================================================================
243
+ // String Serialize Handler
244
+ // ============================================================================
245
+
246
+ class StringSerializeHandler extends BaseSerializeHandler {
247
+ private _rowIndex: number = 0
248
+ private _allRows: string[] = []
249
+ private _allRowSeparators: string[] = []
250
+ private _currentRow: string = ""
251
+ private _nullCellCount: number = 0
252
+ private _cursorStyle: IBufferCell
253
+ private _firstRow: number = 0
254
+ private _lastCursorRow: number = 0
255
+ private _lastCursorCol: number = 0
256
+ private _lastContentCursorRow: number = 0
257
+ private _lastContentCursorCol: number = 0
258
+
259
+ constructor(
260
+ buffer: IBuffer,
261
+ private readonly _terminal: ITerminalCore,
262
+ ) {
263
+ super(buffer)
264
+ this._cursorStyle = this._buffer.getNullCell()
265
+ }
266
+
267
+ protected _beforeSerialize(rows: number, start: number, _end: number): void {
268
+ this._allRows = Array.from<string>({ length: rows })
269
+ this._allRowSeparators = Array.from<string>({ length: rows })
270
+ this._rowIndex = 0
271
+
272
+ this._currentRow = ""
273
+ this._nullCellCount = 0
274
+ this._cursorStyle = this._buffer.getNullCell()
275
+
276
+ this._lastContentCursorRow = start
277
+ this._lastCursorRow = start
278
+ this._firstRow = start
279
+ }
280
+
281
+ protected _rowEnd(row: number, isLastRow: boolean): void {
282
+ let rowSeparator = ""
283
+
284
+ const nextLine = isLastRow ? undefined : this._buffer.getLine(row + 1)
285
+ const wrapped = !!nextLine?.isWrapped
286
+
287
+ if (this._nullCellCount > 0 && wrapped) {
288
+ this._currentRow += " ".repeat(this._nullCellCount)
289
+ }
290
+
291
+ this._nullCellCount = 0
292
+
293
+ if (!isLastRow && !wrapped) {
294
+ rowSeparator = "\r\n"
295
+ this._lastCursorRow = row + 1
296
+ this._lastCursorCol = 0
297
+ }
298
+
299
+ this._allRows[this._rowIndex] = this._currentRow
300
+ this._allRowSeparators[this._rowIndex++] = rowSeparator
301
+ this._currentRow = ""
302
+ this._nullCellCount = 0
303
+ }
304
+
305
+ private _diffStyle(cell: IBufferCell, oldCell: IBufferCell): number[] {
306
+ const sgrSeq: number[] = []
307
+ const fgChanged = !equalFg(cell, oldCell)
308
+ const bgChanged = !equalBg(cell, oldCell)
309
+ const flagsChanged = !equalFlags(cell, oldCell)
310
+
311
+ if (fgChanged || bgChanged || flagsChanged) {
312
+ if (this._isAttributeDefault(cell)) {
313
+ if (!this._isAttributeDefault(oldCell)) {
314
+ sgrSeq.push(0)
315
+ }
316
+ } else {
317
+ if (flagsChanged) {
318
+ if (!!cell.isInverse() !== !!oldCell.isInverse()) {
319
+ sgrSeq.push(cell.isInverse() ? 7 : 27)
320
+ }
321
+ if (!!cell.isBold() !== !!oldCell.isBold()) {
322
+ sgrSeq.push(cell.isBold() ? 1 : 22)
323
+ }
324
+ if (!!cell.isUnderline() !== !!oldCell.isUnderline()) {
325
+ sgrSeq.push(cell.isUnderline() ? 4 : 24)
326
+ }
327
+ if (!!cell.isBlink() !== !!oldCell.isBlink()) {
328
+ sgrSeq.push(cell.isBlink() ? 5 : 25)
329
+ }
330
+ if (!!cell.isInvisible() !== !!oldCell.isInvisible()) {
331
+ sgrSeq.push(cell.isInvisible() ? 8 : 28)
332
+ }
333
+ if (!!cell.isItalic() !== !!oldCell.isItalic()) {
334
+ sgrSeq.push(cell.isItalic() ? 3 : 23)
335
+ }
336
+ if (!!cell.isDim() !== !!oldCell.isDim()) {
337
+ sgrSeq.push(cell.isDim() ? 2 : 22)
338
+ }
339
+ if (!!cell.isStrikethrough() !== !!oldCell.isStrikethrough()) {
340
+ sgrSeq.push(cell.isStrikethrough() ? 9 : 29)
341
+ }
342
+ }
343
+ if (fgChanged) {
344
+ const color = cell.getFgColor()
345
+ const mode = cell.getFgColorMode()
346
+ if (mode === 2 || mode === 3 || mode === -1) {
347
+ sgrSeq.push(38, 2, (color >>> 16) & 0xff, (color >>> 8) & 0xff, color & 0xff)
348
+ } else if (mode === 1) {
349
+ // Palette
350
+ if (color >= 16) {
351
+ sgrSeq.push(38, 5, color)
352
+ } else {
353
+ sgrSeq.push(color & 8 ? 90 + (color & 7) : 30 + (color & 7))
354
+ }
355
+ } else {
356
+ sgrSeq.push(39)
357
+ }
358
+ }
359
+ if (bgChanged) {
360
+ const color = cell.getBgColor()
361
+ const mode = cell.getBgColorMode()
362
+ if (mode === 2 || mode === 3 || mode === -1) {
363
+ sgrSeq.push(48, 2, (color >>> 16) & 0xff, (color >>> 8) & 0xff, color & 0xff)
364
+ } else if (mode === 1) {
365
+ // Palette
366
+ if (color >= 16) {
367
+ sgrSeq.push(48, 5, color)
368
+ } else {
369
+ sgrSeq.push(color & 8 ? 100 + (color & 7) : 40 + (color & 7))
370
+ }
371
+ } else {
372
+ sgrSeq.push(49)
373
+ }
374
+ }
375
+ }
376
+ }
377
+
378
+ return sgrSeq
379
+ }
380
+
381
+ private _isAttributeDefault(cell: IBufferCell): boolean {
382
+ const mode = cell.getFgColorMode()
383
+ const bgMode = cell.getBgColorMode()
384
+
385
+ if (mode === 0 && bgMode === 0) {
386
+ return (
387
+ !cell.isBold() &&
388
+ !cell.isItalic() &&
389
+ !cell.isUnderline() &&
390
+ !cell.isBlink() &&
391
+ !cell.isInverse() &&
392
+ !cell.isInvisible() &&
393
+ !cell.isDim() &&
394
+ !cell.isStrikethrough()
395
+ )
396
+ }
397
+
398
+ const fgColor = cell.getFgColor()
399
+ const bgColor = cell.getBgColor()
400
+ const nullCell = this._buffer.getNullCell()
401
+ const nullFg = nullCell.getFgColor()
402
+ const nullBg = nullCell.getBgColor()
403
+
404
+ return (
405
+ fgColor === nullFg &&
406
+ bgColor === nullBg &&
407
+ !cell.isBold() &&
408
+ !cell.isItalic() &&
409
+ !cell.isUnderline() &&
410
+ !cell.isBlink() &&
411
+ !cell.isInverse() &&
412
+ !cell.isInvisible() &&
413
+ !cell.isDim() &&
414
+ !cell.isStrikethrough()
415
+ )
416
+ }
417
+
418
+ protected _nextCell(cell: IBufferCell, _oldCell: IBufferCell, row: number, col: number): void {
419
+ const isPlaceHolderCell = cell.getWidth() === 0
420
+
421
+ if (isPlaceHolderCell) {
422
+ return
423
+ }
424
+
425
+ const codepoint = cell.getCode()
426
+ const isInvalidCodepoint = codepoint > 0x10ffff || (codepoint >= 0xd800 && codepoint <= 0xdfff)
427
+ const isGarbage = isInvalidCodepoint || (codepoint >= 0xf000 && cell.getWidth() === 1)
428
+ const isEmptyCell = codepoint === 0 || cell.getChars() === "" || isGarbage
429
+
430
+ const sgrSeq = this._diffStyle(cell, this._cursorStyle)
431
+
432
+ const styleChanged = sgrSeq.length > 0
433
+
434
+ if (styleChanged) {
435
+ if (this._nullCellCount > 0) {
436
+ this._currentRow += " ".repeat(this._nullCellCount)
437
+ this._nullCellCount = 0
438
+ }
439
+
440
+ this._lastContentCursorRow = this._lastCursorRow = row
441
+ this._lastContentCursorCol = this._lastCursorCol = col
442
+
443
+ this._currentRow += `\u001b[${sgrSeq.join(";")}m`
444
+
445
+ const line = this._buffer.getLine(row)
446
+ const cellFromLine = line?.getCell(col)
447
+ if (cellFromLine) {
448
+ this._cursorStyle = cellFromLine
449
+ }
450
+ }
451
+
452
+ if (isEmptyCell) {
453
+ this._nullCellCount += cell.getWidth()
454
+ } else {
455
+ if (this._nullCellCount > 0) {
456
+ this._currentRow += " ".repeat(this._nullCellCount)
457
+ this._nullCellCount = 0
458
+ }
459
+
460
+ this._currentRow += cell.getChars()
461
+
462
+ this._lastContentCursorRow = this._lastCursorRow = row
463
+ this._lastContentCursorCol = this._lastCursorCol = col + cell.getWidth()
464
+ }
465
+ }
466
+
467
+ protected _serializeString(excludeFinalCursorPosition?: boolean): string {
468
+ let rowEnd = this._allRows.length
469
+
470
+ if (this._buffer.length - this._firstRow <= this._terminal.rows) {
471
+ rowEnd = this._lastContentCursorRow + 1 - this._firstRow
472
+ this._lastCursorCol = this._lastContentCursorCol
473
+ this._lastCursorRow = this._lastContentCursorRow
474
+ }
475
+
476
+ let content = ""
477
+
478
+ for (let i = 0; i < rowEnd; i++) {
479
+ content += this._allRows[i]
480
+ if (i + 1 < rowEnd) {
481
+ content += this._allRowSeparators[i]
482
+ }
483
+ }
484
+
485
+ if (excludeFinalCursorPosition) return content
486
+
487
+ const absoluteCursorRow = (this._buffer.baseY ?? 0) + this._buffer.cursorY
488
+ const cursorRow = constrain(absoluteCursorRow - this._firstRow + 1, 1, Number.MAX_SAFE_INTEGER)
489
+ const cursorCol = this._buffer.cursorX + 1
490
+ content += `\u001b[${cursorRow};${cursorCol}H`
491
+
492
+ const line = this._buffer.getLine(absoluteCursorRow)
493
+ const cell = line?.getCell(this._buffer.cursorX)
494
+ const style = (() => {
495
+ if (!cell) return this._buffer.getNullCell()
496
+ if (cell.getWidth() !== 0) return cell
497
+ if (this._buffer.cursorX > 0) return line?.getCell(this._buffer.cursorX - 1) ?? cell
498
+ return cell
499
+ })()
500
+
501
+ const sgrSeq = this._diffStyle(style, this._cursorStyle)
502
+ if (sgrSeq.length) content += `\u001b[${sgrSeq.join(";")}m`
503
+
504
+ return content
505
+ }
506
+ }
507
+
508
+ // ============================================================================
509
+ // SerializeAddon Class
510
+ // ============================================================================
511
+
512
+ export class SerializeAddon implements ITerminalAddon {
513
+ private _terminal?: ITerminalCore
514
+
515
+ /**
516
+ * Activate the addon (called by Terminal.loadAddon)
517
+ */
518
+ public activate(terminal: ITerminalCore): void {
519
+ this._terminal = terminal
520
+ }
521
+
522
+ /**
523
+ * Dispose the addon and clean up resources
524
+ */
525
+ public dispose(): void {
526
+ this._terminal = undefined
527
+ }
528
+
529
+ /**
530
+ * Serializes terminal rows into a string that can be written back to the
531
+ * terminal to restore the state. The cursor will also be positioned to the
532
+ * correct cell.
533
+ *
534
+ * @param options Custom options to allow control over what gets serialized.
535
+ */
536
+ public serialize(options?: ISerializeOptions): string {
537
+ if (!this._terminal) {
538
+ throw new Error("Cannot use addon until it has been loaded")
539
+ }
540
+
541
+ const buffer = getTerminalBuffers(this._terminal)
542
+
543
+ if (!buffer) {
544
+ return ""
545
+ }
546
+
547
+ const normalBuffer = buffer.normal ?? buffer.active
548
+ const altBuffer = buffer.alternate
549
+
550
+ if (!normalBuffer) {
551
+ return ""
552
+ }
553
+
554
+ let content = !options?.excludeModes && getTerminalMode(this._terminal, 2031) ? "\u001b[?2031h" : ""
555
+ content += options?.range
556
+ ? this._serializeBufferByRange(normalBuffer, options.range, true)
557
+ : this._serializeBufferByScrollback(normalBuffer, options?.scrollback)
558
+
559
+ if (!options?.excludeAltBuffer && buffer.active?.type === "alternate" && altBuffer) {
560
+ const alternateContent = this._serializeBufferByScrollback(altBuffer, undefined)
561
+ content += `\u001b[?1049h\u001b[H${alternateContent}`
562
+ }
563
+
564
+ return content
565
+ }
566
+
567
+ /**
568
+ * Serializes terminal content as plain text (no escape sequences)
569
+ * @param options Custom options to allow control over what gets serialized.
570
+ */
571
+ public serializeAsText(options?: { scrollback?: number; trimWhitespace?: boolean }): string {
572
+ if (!this._terminal) {
573
+ throw new Error("Cannot use addon until it has been loaded")
574
+ }
575
+
576
+ const buffer = getTerminalBuffers(this._terminal)
577
+
578
+ if (!buffer) {
579
+ return ""
580
+ }
581
+
582
+ const activeBuffer = buffer.active ?? buffer.normal
583
+ if (!activeBuffer) {
584
+ return ""
585
+ }
586
+
587
+ const maxRows = activeBuffer.length
588
+ const scrollback = options?.scrollback
589
+ const correctRows = scrollback === undefined ? maxRows : constrain(scrollback + this._terminal.rows, 0, maxRows)
590
+
591
+ const startRow = maxRows - correctRows
592
+ const endRow = maxRows - 1
593
+ const lines: string[] = []
594
+
595
+ for (let row = startRow; row <= endRow; row++) {
596
+ const line = activeBuffer.getLine(row)
597
+ if (line) {
598
+ const text = line.translateToString(options?.trimWhitespace ?? true)
599
+ lines.push(text)
600
+ }
601
+ }
602
+
603
+ // Trim trailing empty lines if requested
604
+ if (options?.trimWhitespace) {
605
+ while (lines.length > 0 && lines[lines.length - 1] === "") {
606
+ lines.pop()
607
+ }
608
+ }
609
+
610
+ return lines.join("\n")
611
+ }
612
+
613
+ private _serializeBufferByScrollback(buffer: IBuffer, scrollback?: number): string {
614
+ const maxRows = buffer.length
615
+ const rows = this._terminal?.rows ?? 24
616
+ const correctRows = scrollback === undefined ? maxRows : constrain(scrollback + rows, 0, maxRows)
617
+ return this._serializeBufferByRange(
618
+ buffer,
619
+ {
620
+ start: maxRows - correctRows,
621
+ end: maxRows - 1,
622
+ },
623
+ false,
624
+ )
625
+ }
626
+
627
+ private _serializeBufferByRange(
628
+ buffer: IBuffer,
629
+ range: ISerializeRange,
630
+ excludeFinalCursorPosition: boolean,
631
+ ): string {
632
+ const handler = new StringSerializeHandler(buffer, this._terminal!)
633
+ const cols = this._terminal?.cols ?? 80
634
+ return handler.serialize(
635
+ {
636
+ start: { x: 0, y: range.start },
637
+ end: { x: cols, y: range.end },
638
+ },
639
+ excludeFinalCursorPosition,
640
+ )
641
+ }
642
+ }