@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,515 @@
1
+ # CreateEffect Simplification Implementation Spec
2
+
3
+ Reduce reactive misuse across `packages/app`.
4
+
5
+ ---
6
+
7
+ ## Context
8
+
9
+ This work targets `packages/app/src`, which currently has 101 `createEffect` calls across 37 files.
10
+
11
+ The biggest clusters are `pages/session.tsx` (19), `pages/layout.tsx` (13), `pages/session/file-tabs.tsx` (6), and several context providers that mirror one store into another.
12
+
13
+ Key issues from the audit:
14
+
15
+ - Derived state is being written through effects instead of computed directly
16
+ - Session and file resets are handled by watch-and-clear effects instead of keyed state boundaries
17
+ - User-driven actions are hidden inside reactive effects
18
+ - Context layers mirror and hydrate child stores with multiple sync effects
19
+ - Several areas repeat the same imperative trigger pattern in multiple effects
20
+
21
+ Keep the implementation focused on removing unnecessary effects, not on broad UI redesign.
22
+
23
+ ## Goals
24
+
25
+ - Cut high-churn `createEffect` usage in the hottest files first
26
+ - Replace effect-driven derived state with reactive derivation
27
+ - Replace reset-on-key effects with keyed ownership boundaries
28
+ - Move event-driven work to direct actions and write paths
29
+ - Remove mirrored store hydration where a single source of truth can exist
30
+ - Leave necessary external sync effects in place, but make them narrower and clearer
31
+
32
+ ## Non-Goals
33
+
34
+ - Do not rewrite unrelated component structure just to reduce the count
35
+ - Do not change product behavior, navigation flow, or persisted data shape unless required for a cleaner write boundary
36
+ - Do not remove effects that bridge to DOM, editors, polling, or external APIs unless there is a clearly safer equivalent
37
+ - Do not attempt a repo-wide cleanup outside `packages/app`
38
+
39
+ ## Effect Taxonomy And Replacement Rules
40
+
41
+ Use these rules during implementation.
42
+
43
+ ### Prefer `createMemo`
44
+
45
+ Use `createMemo` when the target value is pure derived state from other signals or stores.
46
+
47
+ Do this when an effect only reads reactive inputs and writes another reactive value that could be computed instead.
48
+
49
+ Apply this to:
50
+
51
+ - `packages/app/src/pages/session.tsx:141`
52
+ - `packages/app/src/pages/layout.tsx:557`
53
+ - `packages/app/src/components/terminal.tsx:261`
54
+ - `packages/app/src/components/session/session-header.tsx:309`
55
+
56
+ Rules:
57
+
58
+ - If no external system is touched, do not use `createEffect`
59
+ - Derive once, then read the memo where needed
60
+ - If normalization is required, prefer normalizing at the write boundary before falling back to a memo
61
+
62
+ ### Prefer Keyed Remounts
63
+
64
+ Use keyed remounts when local UI state should reset because an identity changed.
65
+
66
+ Do this with `sessionKey`, `scope()`, or another stable identity instead of watching the key and manually clearing signals.
67
+
68
+ Apply this to:
69
+
70
+ - `packages/app/src/pages/session.tsx:325`
71
+ - `packages/app/src/pages/session.tsx:336`
72
+ - `packages/app/src/pages/session.tsx:477`
73
+ - `packages/app/src/pages/session.tsx:869`
74
+ - `packages/app/src/pages/session.tsx:963`
75
+ - `packages/app/src/pages/session/message-timeline.tsx:149`
76
+ - `packages/app/src/context/file.tsx:100`
77
+
78
+ Rules:
79
+
80
+ - If the desired behavior is "new identity, fresh local state," key the owner subtree
81
+ - Keep state local to the keyed boundary so teardown and recreation handle the reset naturally
82
+
83
+ ### Prefer Event Handlers And Actions
84
+
85
+ Use direct handlers, store actions, and async command functions when work happens because a user clicked, selected, reloaded, or navigated.
86
+
87
+ Do this when an effect is just watching for a flag change, command token, or event-bus signal to trigger imperative logic.
88
+
89
+ Apply this to:
90
+
91
+ - `packages/app/src/pages/layout.tsx:484`
92
+ - `packages/app/src/pages/layout.tsx:652`
93
+ - `packages/app/src/pages/layout.tsx:776`
94
+ - `packages/app/src/pages/layout.tsx:1489`
95
+ - `packages/app/src/pages/layout.tsx:1519`
96
+ - `packages/app/src/components/file-tree.tsx:328`
97
+ - `packages/app/src/pages/session/terminal-panel.tsx:55`
98
+ - `packages/app/src/context/global-sync.tsx:148`
99
+ - Duplicated trigger sets in:
100
+ - `packages/app/src/pages/session/review-tab.tsx:122`
101
+ - `packages/app/src/pages/session/review-tab.tsx:130`
102
+ - `packages/app/src/pages/session/review-tab.tsx:138`
103
+ - `packages/app/src/pages/session/file-tabs.tsx:367`
104
+ - `packages/app/src/pages/session/file-tabs.tsx:378`
105
+ - `packages/app/src/pages/session/file-tabs.tsx:389`
106
+ - `packages/app/src/pages/session/use-session-hash-scroll.ts:144`
107
+ - `packages/app/src/pages/session/use-session-hash-scroll.ts:149`
108
+ - `packages/app/src/pages/session/use-session-hash-scroll.ts:167`
109
+
110
+ Rules:
111
+
112
+ - If the trigger is user intent, call the action at the source of that intent
113
+ - If the same imperative work is triggered from multiple places, extract one function and call it directly
114
+
115
+ ### Prefer `onMount` And `onCleanup`
116
+
117
+ Use `onMount` and `onCleanup` for lifecycle-only setup and teardown.
118
+
119
+ This is the right fit for subscriptions, one-time wiring, timers, and imperative integration that should not rerun for ordinary reactive changes.
120
+
121
+ Use this when:
122
+
123
+ - Setup should happen once per owner lifecycle
124
+ - Cleanup should always pair with teardown
125
+ - The work is not conceptually derived state
126
+
127
+ ### Keep `createEffect` When It Is A Real Bridge
128
+
129
+ Keep `createEffect` when it synchronizes reactive data to an external imperative sink.
130
+
131
+ Examples that should remain, though they may be narrowed or split:
132
+
133
+ - DOM/editor sync in `packages/app/src/components/prompt-input.tsx:690`
134
+ - Scroll sync in `packages/app/src/pages/session.tsx:685`
135
+ - Scroll/hash sync in `packages/app/src/pages/session/use-session-hash-scroll.ts:149`
136
+ - External sync in:
137
+ - `packages/app/src/context/language.tsx:207`
138
+ - `packages/app/src/context/settings.tsx:110`
139
+ - `packages/app/src/context/sdk.tsx:26`
140
+ - Polling in:
141
+ - `packages/app/src/components/status-popover.tsx:59`
142
+ - `packages/app/src/components/dialog-select-server.tsx:273`
143
+
144
+ Rules:
145
+
146
+ - Keep the effect single-purpose
147
+ - Make dependencies explicit and narrow
148
+ - Avoid writing back into the same reactive graph unless absolutely required
149
+
150
+ ## Implementation Plan
151
+
152
+ ### Phase 0: Classification Pass
153
+
154
+ Before changing code, tag each targeted effect as one of: derive, reset, event, lifecycle, or external bridge.
155
+
156
+ Acceptance criteria:
157
+
158
+ - Every targeted effect in this spec is tagged with a replacement strategy before refactoring starts
159
+ - Shared helpers to be introduced are identified up front to avoid repeating patterns
160
+
161
+ ### Phase 1: Derived-State Cleanup
162
+
163
+ Tackle highest-value, lowest-risk derived-state cleanup first.
164
+
165
+ Priority items:
166
+
167
+ - Normalize tabs at write boundaries and remove `packages/app/src/pages/session.tsx:141`
168
+ - Stop syncing `workspaceOrder` in `packages/app/src/pages/layout.tsx:557`
169
+ - Make prompt slash filtering reactive so `packages/app/src/components/prompt-input.tsx:652` can be removed
170
+ - Replace other obvious derived-state effects in terminal and session header
171
+
172
+ Acceptance criteria:
173
+
174
+ - No behavior change in tab ordering, prompt filtering, terminal display, or header state
175
+ - Targeted derived-state effects are deleted, not just moved
176
+
177
+ ### Phase 2: Keyed Reset Cleanup
178
+
179
+ Replace reset-on-key effects with keyed ownership boundaries.
180
+
181
+ Priority items:
182
+
183
+ - Key session-scoped UI and state by `sessionKey`
184
+ - Key file-scoped state by `scope()`
185
+ - Remove manual clear-and-reseed effects in session and file context
186
+
187
+ Acceptance criteria:
188
+
189
+ - Switching session or file scope recreates the intended local state cleanly
190
+ - No stale state leaks across session or scope changes
191
+ - Target reset effects are deleted
192
+
193
+ ### Phase 3: Event-Driven Work Extraction
194
+
195
+ Move event-driven work out of reactive effects.
196
+
197
+ Priority items:
198
+
199
+ - Replace `globalStore.reload` effect dispatching with direct calls
200
+ - Split mixed-responsibility effect in `packages/app/src/pages/layout.tsx:1489`
201
+ - Collapse duplicated imperative trigger triplets into single functions
202
+ - Move file-tree and terminal-panel imperative work to explicit handlers
203
+
204
+ Acceptance criteria:
205
+
206
+ - User-triggered behavior still fires exactly once per intended action
207
+ - No effect remains whose only job is to notice a command-like state and trigger an imperative function
208
+
209
+ ### Phase 4: Context Ownership Cleanup
210
+
211
+ Remove mirrored child-store hydration patterns.
212
+
213
+ Priority items:
214
+
215
+ - Remove child-store hydration mirrors in `packages/app/src/context/global-sync/child-store.ts:184`, `:190`, `:193`
216
+ - Simplify mirror logic in `packages/app/src/context/global-sync.tsx:130`, `:138`
217
+ - Revisit `packages/app/src/context/layout.tsx:424` if it still mirrors instead of deriving
218
+
219
+ Acceptance criteria:
220
+
221
+ - There is one clear source of truth for each synced value
222
+ - Child stores no longer need effect-based hydration to stay consistent
223
+ - Initialization and updates both work without manual mirror effects
224
+
225
+ ### Phase 5: Cleanup And Keeper Review
226
+
227
+ Clean up remaining targeted hotspots and narrow the effects that should stay.
228
+
229
+ Acceptance criteria:
230
+
231
+ - Remaining `createEffect` calls in touched files are all true bridges or clearly justified lifecycle sync
232
+ - Mixed-responsibility effects are split into smaller units where still needed
233
+
234
+ ## Detailed Work Items By Area
235
+
236
+ ### 1. Normalize Tab State
237
+
238
+ Files:
239
+
240
+ - `packages/app/src/pages/session.tsx:141`
241
+
242
+ Work:
243
+
244
+ - Move tab normalization into the functions that create, load, or update tab state
245
+ - Make readers consume already-normalized tab data
246
+ - Remove the effect that rewrites derived tab state after the fact
247
+
248
+ Rationale:
249
+
250
+ - Tabs should become valid when written, not be repaired later
251
+ - This removes a feedback loop and makes state easier to trust
252
+
253
+ Acceptance criteria:
254
+
255
+ - The effect at `packages/app/src/pages/session.tsx:141` is removed
256
+ - Newly created and restored tabs are normalized before they enter local state
257
+ - Tab rendering still matches current behavior for valid and edge-case inputs
258
+
259
+ ### 2. Key Session-Owned State
260
+
261
+ Files:
262
+
263
+ - `packages/app/src/pages/session.tsx:325`
264
+ - `packages/app/src/pages/session.tsx:336`
265
+ - `packages/app/src/pages/session.tsx:477`
266
+ - `packages/app/src/pages/session.tsx:869`
267
+ - `packages/app/src/pages/session.tsx:963`
268
+ - `packages/app/src/pages/session/message-timeline.tsx:149`
269
+
270
+ Work:
271
+
272
+ - Identify state that should reset when `sessionKey` changes
273
+ - Move that state under a keyed subtree or keyed owner boundary
274
+ - Remove effects that watch `sessionKey` just to clear local state, refs, or temporary UI flags
275
+
276
+ Rationale:
277
+
278
+ - Session identity already defines the lifetime of this UI state
279
+ - Keyed ownership makes reset behavior automatic and easier to reason about
280
+
281
+ Acceptance criteria:
282
+
283
+ - The targeted reset effects are removed
284
+ - Changing sessions resets only the intended session-local state
285
+ - Scroll and editor state that should persist are not accidentally reset
286
+
287
+ ### 3. Derive Workspace Order
288
+
289
+ Files:
290
+
291
+ - `packages/app/src/pages/layout.tsx:557`
292
+
293
+ Work:
294
+
295
+ - Stop writing `workspaceOrder` from live workspace data in an effect
296
+ - Represent user overrides separately from live workspace data
297
+ - Compute effective order from current data plus overrides with a memo or pure helper
298
+
299
+ Rationale:
300
+
301
+ - Persisted user intent and live source data should not mirror each other through an effect
302
+ - A computed effective order avoids drift and racey resync behavior
303
+
304
+ Acceptance criteria:
305
+
306
+ - The effect at `packages/app/src/pages/layout.tsx:557` is removed
307
+ - Workspace order updates correctly when workspaces appear, disappear, or are reordered by the user
308
+ - User overrides persist without requiring a sync-back effect
309
+
310
+ ### 4. Remove Child-Store Mirrors
311
+
312
+ Files:
313
+
314
+ - `packages/app/src/context/global-sync.tsx:130`
315
+ - `packages/app/src/context/global-sync.tsx:138`
316
+ - `packages/app/src/context/global-sync.tsx:148`
317
+ - `packages/app/src/context/global-sync/child-store.ts:184`
318
+ - `packages/app/src/context/global-sync/child-store.ts:190`
319
+ - `packages/app/src/context/global-sync/child-store.ts:193`
320
+ - `packages/app/src/context/layout.tsx:424`
321
+
322
+ Work:
323
+
324
+ - Trace the actual ownership of global and child store values
325
+ - Replace hydration and mirror effects with explicit initialization and direct updates
326
+ - Remove the `globalStore.reload` event-bus pattern and call the needed reload paths directly
327
+
328
+ Rationale:
329
+
330
+ - Mirrors make it hard to tell which state is authoritative
331
+ - Event-bus style state toggles hide control flow and create accidental reruns
332
+
333
+ Acceptance criteria:
334
+
335
+ - Child store hydration no longer depends on effect-based copying
336
+ - Reload work can be followed from the event source to the handler without a reactive relay
337
+ - State remains correct on first load, child creation, and subsequent updates
338
+
339
+ ### 5. Key File-Scoped State
340
+
341
+ Files:
342
+
343
+ - `packages/app/src/context/file.tsx:100`
344
+
345
+ Work:
346
+
347
+ - Move file-scoped local state under a boundary keyed by `scope()`
348
+ - Remove any effect that watches `scope()` only to reset file-local state
349
+
350
+ Rationale:
351
+
352
+ - File scope changes are identity changes
353
+ - Keyed ownership gives a cleaner reset than manual clear logic
354
+
355
+ Acceptance criteria:
356
+
357
+ - The effect at `packages/app/src/context/file.tsx:100` is removed
358
+ - Switching scopes resets only scope-local state
359
+ - No previous-scope data appears after a scope change
360
+
361
+ ### 6. Split Layout Side Effects
362
+
363
+ Files:
364
+
365
+ - `packages/app/src/pages/layout.tsx:1489`
366
+ - Related event-driven effects near `packages/app/src/pages/layout.tsx:484`, `:652`, `:776`, `:1519`
367
+
368
+ Work:
369
+
370
+ - Break the mixed-responsibility effect at `:1489` into direct actions and smaller bridge effects only where required
371
+ - Move user-triggered branches into the actual command or handler that causes them
372
+ - Remove any branch that only exists because one effect is handling unrelated concerns
373
+
374
+ Rationale:
375
+
376
+ - Mixed effects hide cause and make reruns hard to predict
377
+ - Smaller units reduce accidental coupling and make future cleanup safer
378
+
379
+ Acceptance criteria:
380
+
381
+ - The effect at `packages/app/src/pages/layout.tsx:1489` no longer mixes unrelated responsibilities
382
+ - Event-driven branches execute from direct handlers
383
+ - Remaining effects in this area each have one clear external sync purpose
384
+
385
+ ### 7. Remove Duplicate Triggers
386
+
387
+ Files:
388
+
389
+ - `packages/app/src/pages/session/review-tab.tsx:122`
390
+ - `packages/app/src/pages/session/review-tab.tsx:130`
391
+ - `packages/app/src/pages/session/review-tab.tsx:138`
392
+ - `packages/app/src/pages/session/file-tabs.tsx:367`
393
+ - `packages/app/src/pages/session/file-tabs.tsx:378`
394
+ - `packages/app/src/pages/session/file-tabs.tsx:389`
395
+ - `packages/app/src/pages/session/use-session-hash-scroll.ts:144`
396
+ - `packages/app/src/pages/session/use-session-hash-scroll.ts:149`
397
+ - `packages/app/src/pages/session/use-session-hash-scroll.ts:167`
398
+
399
+ Work:
400
+
401
+ - Extract one explicit imperative function per behavior
402
+ - Call that function from each source event instead of replicating the same effect pattern multiple times
403
+ - Preserve the scroll-sync effect that is truly syncing with the DOM, but remove duplicate trigger scaffolding around it
404
+
405
+ Rationale:
406
+
407
+ - Duplicate triggers make it easy to miss a case or fire twice
408
+ - One named action is easier to test and reason about
409
+
410
+ Acceptance criteria:
411
+
412
+ - Repeated imperative effect triplets are collapsed into shared functions
413
+ - Scroll behavior still works, including hash-based navigation
414
+ - No duplicate firing is introduced
415
+
416
+ ### 8. Make Prompt Filtering Reactive
417
+
418
+ Files:
419
+
420
+ - `packages/app/src/components/prompt-input.tsx:652`
421
+ - Keep `packages/app/src/components/prompt-input.tsx:690` as needed
422
+
423
+ Work:
424
+
425
+ - Convert slash filtering into a pure reactive derivation from the current input and candidate command list
426
+ - Keep only the editor or DOM bridge effect if it is still needed for imperative syncing
427
+
428
+ Rationale:
429
+
430
+ - Filtering is classic derived state
431
+ - It should not need an effect if it can be computed from current inputs
432
+
433
+ Acceptance criteria:
434
+
435
+ - The effect at `packages/app/src/components/prompt-input.tsx:652` is removed
436
+ - Filtered slash-command results update correctly as the input changes
437
+ - The editor sync effect at `:690` still behaves correctly
438
+
439
+ ### 9. Clean Up Smaller Derived-State Cases
440
+
441
+ Files:
442
+
443
+ - `packages/app/src/components/terminal.tsx:261`
444
+ - `packages/app/src/components/session/session-header.tsx:309`
445
+
446
+ Work:
447
+
448
+ - Replace effect-written local state with memos or inline derivation
449
+ - Remove intermediate setters when the value can be computed directly
450
+
451
+ Rationale:
452
+
453
+ - These are low-risk wins that reinforce the same pattern
454
+ - They also help keep follow-up cleanup consistent
455
+
456
+ Acceptance criteria:
457
+
458
+ - Targeted effects are removed
459
+ - UI output remains unchanged under the same inputs
460
+
461
+ ## Verification And Regression Checks
462
+
463
+ Run focused checks after each phase, not only at the end.
464
+
465
+ ### Suggested Verification
466
+
467
+ - Switch between sessions rapidly and confirm local session UI resets only where intended
468
+ - Open, close, and reorder tabs and confirm order and normalization remain stable
469
+ - Change workspaces, reload workspace data, and verify effective ordering is correct
470
+ - Change file scope and confirm stale file state does not bleed across scopes
471
+ - Trigger layout actions that previously depended on effects and confirm they still fire once
472
+ - Use slash commands in the prompt and verify filtering updates as you type
473
+ - Test review tab, file tab, and hash-scroll flows for duplicate or missing triggers
474
+ - Verify global sync initialization, reload, and child-store creation paths
475
+
476
+ ### Regression Checks
477
+
478
+ - No accidental infinite reruns
479
+ - No double-firing network or command actions
480
+ - No lost cleanup for listeners, timers, or scroll handlers
481
+ - No preserved stale state after identity changes
482
+ - No removed effect that was actually bridging to DOM or an external API
483
+
484
+ If available, add or update tests around pure helpers introduced during this cleanup.
485
+
486
+ Favor tests for derived ordering, normalization, and action extraction, since those are easiest to lock down.
487
+
488
+ ## Definition Of Done
489
+
490
+ This work is done when all of the following are true:
491
+
492
+ - The highest-leverage targets in this spec are implemented
493
+ - Each removed effect has been replaced by a clearer pattern: memo, keyed boundary, direct action, or lifecycle hook
494
+ - The "should remain" effects still exist only where they serve a real external sync purpose
495
+ - Touched files have fewer mixed-responsibility effects and clearer ownership of state
496
+ - Manual verification covers session switching, file scope changes, workspace ordering, prompt filtering, and reload flows
497
+ - No behavior regressions are found in the targeted areas
498
+
499
+ A reduced raw `createEffect` count is helpful, but it is not the main success metric.
500
+
501
+ The main success metric is clearer ownership and fewer effect-driven state repairs.
502
+
503
+ ## Risks And Rollout Notes
504
+
505
+ Main risks:
506
+
507
+ - Keyed remounts can reset too much if state boundaries are drawn too high
508
+ - Store mirror removal can break initialization order if ownership is not mapped first
509
+ - Moving event work out of effects can accidentally skip triggers that were previously implicit
510
+
511
+ Rollout notes:
512
+
513
+ - Land in small phases, with each phase keeping the app behaviorally stable
514
+ - Prefer isolated PRs by phase or by file cluster, especially for context-store changes
515
+ - Review each remaining effect in touched files and leave it only if it clearly bridges to something external
@@ -0,0 +1,13 @@
1
+ - Prioritize stability, then simplicity, then measurement overhead.
2
+ - Use Playwright for scenario control, isolation, and completion checks.
3
+ - Use Chrome Performance traces for generic browser profiling.
4
+ - Use Electron `contentTracing` for packaged multi-process profiling.
5
+ - Keep custom probes only for product-specific measurements.
6
+ - Do not duplicate measurements across the harness, probes, and traces.
7
+ - Run benchmarks serially to avoid cross-test contention.
8
+ - Run benchmarks against production builds.
9
+ - Keep detailed profiling opt-in when it changes workload behavior.
10
+ - Preserve raw diagnostic data or use lossless representations.
11
+ - Do not enforce machine-dependent performance thresholds.
12
+ - Assert scenario completion and metric collection only.
13
+ - Keep normal test discovery free of manual benchmarks.
@@ -0,0 +1,79 @@
1
+ # Manual app performance suite
2
+
3
+ The app's high-volume performance diagnostics live under `packages/app/e2e/performance` and are excluded from normal local and CI Playwright discovery. The benchmark config builds the app and serves the production bundle before running scenarios serially.
4
+
5
+ Run the suite explicitly from `packages/app`:
6
+
7
+ ```sh
8
+ bun run test:bench
9
+ ```
10
+
11
+ PowerShell:
12
+
13
+ ```powershell
14
+ $env:PLAYWRIGHT_WORKERS = "1"
15
+ bun run test:bench
16
+ ```
17
+
18
+ The suite contains:
19
+
20
+ - cold and hot session-tab timing
21
+ - home-session click timing split between content and titlebar-tab paint
22
+ - single-session tab close timing through stable home restoration
23
+ - cached session repaint and mutation tracing
24
+ - streaming timeline throughput, RAF-gap, long-task, geometry, and remount diagnostics
25
+
26
+ All benchmarks import the shared `benchmark` fixture. Pages created through Playwright's `page` fixture automatically capture main-frame navigation history and emit a Chrome trace when `OPENCODE_PERFORMANCE_TRACE_DIR` is set. Benchmarks that need isolated browser contexts use `withBenchmarkPage`, which owns the context and the same diagnostics lifecycle.
27
+
28
+ New benchmarks should look like normal Playwright tests:
29
+
30
+ ```ts
31
+ import { benchmark, expect } from "../benchmark"
32
+
33
+ benchmark("measures one interaction", async ({ page, report }) => {
34
+ // Only scenario-specific setup and interaction belong here.
35
+ report({ durationMs: 42 })
36
+ })
37
+ ```
38
+
39
+ The fixture requires every benchmark to call `report()`, automatically names and closes traces, captures navigation history, attaches that history when a test fails, and emits metrics as a consistent `BENCHMARK` JSON line.
40
+
41
+ ```text
42
+ BENCHMARK {"name":"...","context":{"project":"chromium","platform":"darwin"},"metrics":{...}}
43
+ ```
44
+
45
+ Every observed page also emits `BENCHMARK_PAGE` with the same run ID, navigation history, and optional trace path before the final status-bearing `BENCHMARK` record. Chrome traces are browser-wide page-lifetime diagnostics; scenario metrics use narrower explicitly named observation windows.
46
+
47
+ This follows the stack's own guidance: [Electron recommends repeated Chrome DevTools and Chrome Tracing measurement](https://www.electronjs.org/docs/latest/tutorial/performance), [Chrome DevTools recommends Performance recordings for runtime work](https://developer.chrome.com/docs/devtools/performance), and [Playwright uses traces for test debugging rather than renderer profiling](https://playwright.dev/docs/trace-viewer).
48
+
49
+ These Playwright benchmarks profile the shared app renderer in Chromium. A future packaged Electron benchmark that needs main-process and multi-process attribution should use Electron's official [`contentTracing`](https://www.electronjs.org/docs/latest/api/content-tracing/) API rather than extending this renderer harness with bespoke process instrumentation.
50
+
51
+ CPU and high-volume visual profiling are disabled by default. Set `TIMELINE_CPU_PROFILE=1` to enable both, or additionally set `TIMELINE_VISUAL_PROFILE=0` for CPU-only profiling.
52
+
53
+ The streaming scenario's 30x CPU throttle is a deterministic stress profile, not a simulated end-user device.
54
+
55
+ Benchmarks do not assert machine-dependent performance budgets. Streaming processes 160 deltas by default and reports renderer-observed completion time, throughput, RAF callback-gap distributions, frame-budget equivalents, and long tasks through final geometry settlement. Delta count and delivery batch are included in result context when overridden. These are main-thread callback diagnostics, not compositor presentation or dropped-frame measurements. Visual-only and geometry metrics are `null` when their probes are disabled. Tab metrics describe sampled DOM observations. Assertions verify scenario and metric collection completion. Repeated repaint states are run-length grouped, but every original observation timestamp is retained alongside raw mutation batches and layout shifts.
56
+
57
+ Committed smoke and regression tests continue to own correctness coverage for pagination, tab paint, context resize, collapse state, and composer spacing.
58
+
59
+ ## Chrome traces
60
+
61
+ Set `OPENCODE_PERFORMANCE_TRACE_DIR` to emit a standard Chrome DevTools trace for every benchmark page automatically:
62
+
63
+ ```sh
64
+ OPENCODE_PERFORMANCE_TRACE_DIR=/tmp/opencode-performance-traces \
65
+ bunx playwright test --config e2e/performance/playwright.config.ts \
66
+ timeline/session-tab-switch-benchmark.spec.ts
67
+ ```
68
+
69
+ The emitted JSON is a standard Chrome trace and can be loaded directly into the Chrome DevTools Performance panel. `devtools-tracing` can optionally inspect it from the command line without adding package scripts or dependencies:
70
+
71
+ Trace capture mirrors [Puppeteer's official tracing defaults and lifecycle](https://pptr.dev/api/puppeteer.tracing), using Chrome's `ReturnAsStream` transfer mode and failing when Chromium reports trace data loss.
72
+
73
+ ```sh
74
+ bunx devtools-tracing stats <trace-path-from-BENCHMARK_PAGE>
75
+ ```
76
+
77
+ INP analysis requires a trace with a supported navigation/interaction insight. Selector statistics require a trace captured with `OPENCODE_PERFORMANCE_SELECTOR_TRACE=1`.
78
+
79
+ `e2e/performance/playwright.uncapped.config.ts` disables Chromium frame-rate limiting for explicit uncapped diagnostics. Native product benchmarks should use the default Playwright configuration.