synabun 2.0.0-beta → 2.0.5

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 (374) hide show
  1. package/CHANGELOG.md +65 -0
  2. package/NOTICE +5 -0
  3. package/README.md +1 -1
  4. package/THIRD-PARTY-LICENSES.md +51 -0
  5. package/benchmarks/fixtures/browser/README.md +1 -1
  6. package/benchmarks/fixtures/desktop/README.md +1 -1
  7. package/docs/whatsapp.md +2 -2
  8. package/hooks/claude-code/pre-compact.mjs +11 -0
  9. package/hooks/claude-code/prompt-submit.mjs +60 -184
  10. package/hooks/claude-code/session-start.mjs +73 -16
  11. package/hooks/claude-code/shared.mjs +20 -0
  12. package/hooks/claude-code/stop.mjs +5 -5
  13. package/launcher.mjs +40 -0
  14. package/lib/claude-hooks.js +61 -8
  15. package/lib/client-config-repair.js +4 -3
  16. package/lib/data-home-migration.js +4 -0
  17. package/lib/desktop-pwa.js +206 -0
  18. package/lib/external-tools.js +221 -0
  19. package/lib/first-launch-protection.js +5 -0
  20. package/lib/packaged-runtime.js +110 -0
  21. package/lib/start-launcher.js +1396 -0
  22. package/mcp-server/package-lock.json +2 -2
  23. package/mcp-server/package.json +1 -3
  24. package/neural-interface/i18n/en.json +1540 -154
  25. package/neural-interface/i18n/pt-BR.json +1538 -153
  26. package/neural-interface/lib/assistant-api.js +62 -6
  27. package/neural-interface/lib/assistant-brains/claude.js +4 -0
  28. package/neural-interface/lib/assistant-catalog.js +179 -27
  29. package/neural-interface/lib/assistant-config.js +31 -2
  30. package/neural-interface/lib/assistant-dispatch.js +62 -13
  31. package/neural-interface/lib/assistant-envelope.js +9 -6
  32. package/neural-interface/lib/assistant-hidden-models.js +393 -66
  33. package/neural-interface/lib/assistant-memory.js +24 -10
  34. package/neural-interface/lib/assistant-persona.js +6 -2
  35. package/neural-interface/lib/assistant-pricing.js +21 -6
  36. package/neural-interface/lib/assistant-runtime.js +181 -12
  37. package/neural-interface/lib/assistant-task-prompt.js +13 -3
  38. package/neural-interface/lib/assistant-test-density.js +142 -0
  39. package/neural-interface/lib/assistant-user-context-budgets.js +123 -0
  40. package/neural-interface/lib/assistant-user-context.js +908 -0
  41. package/neural-interface/lib/assistant-user-knowledge.js +328 -0
  42. package/neural-interface/lib/assistant-user-learn.js +404 -0
  43. package/neural-interface/lib/assistant-user-threads.js +226 -0
  44. package/neural-interface/lib/assistant-user-writer.js +181 -0
  45. package/neural-interface/lib/augmented-path.js +23 -3
  46. package/neural-interface/lib/browser-background-tab.js +134 -0
  47. package/neural-interface/lib/browser-language.js +83 -0
  48. package/neural-interface/lib/browser-screenshot.js +678 -61
  49. package/neural-interface/lib/claude-agent-bridge.js +87 -153
  50. package/neural-interface/lib/claude-executable.js +241 -0
  51. package/neural-interface/lib/claude-model-catalog.js +82 -93
  52. package/neural-interface/lib/claude-temporary.js +46 -7
  53. package/neural-interface/lib/cloudflared/install.js +533 -0
  54. package/neural-interface/lib/cloudflared/manager.js +545 -0
  55. package/neural-interface/lib/cloudflared/pins.js +115 -0
  56. package/neural-interface/lib/cloudflared/pins.json +63 -0
  57. package/neural-interface/lib/cloudflared/tunnel.js +265 -0
  58. package/neural-interface/lib/editor-completion-endpoint.js +264 -0
  59. package/neural-interface/lib/editor-completion-opencode.js +436 -0
  60. package/neural-interface/lib/editor-completion.js +714 -0
  61. package/neural-interface/lib/editor-config.js +149 -0
  62. package/neural-interface/lib/editor-files-api.js +268 -0
  63. package/neural-interface/lib/editor-git-api.js +134 -0
  64. package/neural-interface/lib/file-explorer-api.js +1039 -0
  65. package/neural-interface/lib/file-explorer-system.js +355 -0
  66. package/neural-interface/lib/file-explorer-trash.ps1 +85 -0
  67. package/neural-interface/lib/file-explorer-watch.js +239 -0
  68. package/neural-interface/lib/git-diff-api.js +103 -0
  69. package/neural-interface/lib/git-read.js +202 -0
  70. package/neural-interface/lib/greeting/api.js +89 -0
  71. package/neural-interface/lib/greeting/budgets.js +90 -0
  72. package/neural-interface/lib/greeting/engine.js +461 -0
  73. package/neural-interface/lib/greeting/facts.js +320 -0
  74. package/neural-interface/lib/greeting/sources.js +252 -0
  75. package/neural-interface/lib/greeting/writer.js +139 -0
  76. package/neural-interface/lib/http-guards.js +189 -13
  77. package/neural-interface/lib/machine-names.js +88 -0
  78. package/neural-interface/lib/memory-api.js +31 -7
  79. package/neural-interface/lib/native-binary-runtime.js +69 -337
  80. package/neural-interface/lib/native-loop-providers.js +40 -49
  81. package/neural-interface/lib/offline-page.js +54 -0
  82. package/neural-interface/lib/plugin-installer.js +4 -2
  83. package/neural-interface/lib/project-storage.js +5 -7
  84. package/neural-interface/lib/request-gate.js +589 -0
  85. package/neural-interface/lib/session-title-generator.js +5 -0
  86. package/neural-interface/lib/synabun-update-plan.js +19 -0
  87. package/neural-interface/lib/ui-blob-api.js +108 -0
  88. package/neural-interface/lib/ui-blob-store.js +68 -0
  89. package/neural-interface/package-lock.json +701 -253
  90. package/neural-interface/package.json +13 -2
  91. package/neural-interface/public/install-app.html +34 -0
  92. package/neural-interface/public/manifest.json +2 -0
  93. package/neural-interface/public/offline.html +402 -66
  94. package/neural-interface/public/onboarding.html +264 -147
  95. package/neural-interface/public/shared/agent-runtime-options.js +22 -6
  96. package/neural-interface/public/shared/assistant/asst-automation.js +286 -0
  97. package/neural-interface/public/shared/assistant/asst-brain-picker.js +312 -62
  98. package/neural-interface/public/shared/assistant/asst-composer.js +11 -1
  99. package/neural-interface/public/shared/assistant/asst-mascot.js +26 -64
  100. package/neural-interface/public/shared/assistant/asst-menu.js +12 -8
  101. package/neural-interface/public/shared/assistant/asst-panel.js +150 -28
  102. package/neural-interface/public/shared/assistant/asst-rack-styles.js +0 -3
  103. package/neural-interface/public/shared/assistant/asst-render.js +219 -67
  104. package/neural-interface/public/shared/assistant/asst-route-models.js +108 -0
  105. package/neural-interface/public/shared/assistant/asst-route.js +23 -329
  106. package/neural-interface/public/shared/assistant/asst-routes-editor.js +495 -0
  107. package/neural-interface/public/shared/assistant/asst-sanitize.js +8 -1
  108. package/neural-interface/public/shared/assistant/asst-sidepanel.js +31 -9
  109. package/neural-interface/public/shared/assistant/asst-state.js +185 -1
  110. package/neural-interface/public/shared/assistant/asst-styles.js +162 -6
  111. package/neural-interface/public/shared/blob-store.js +111 -22
  112. package/neural-interface/public/shared/cdx/cdx-panel.js +32 -11
  113. package/neural-interface/public/shared/cdx/cdx-render.js +25 -89
  114. package/neural-interface/public/shared/cdx/cdx-styles.js +36 -0
  115. package/neural-interface/public/shared/cdx/cdx-tabs.js +87 -4
  116. package/neural-interface/public/shared/command-runner-model.js +30 -0
  117. package/neural-interface/public/shared/constants.js +6 -1
  118. package/neural-interface/public/shared/cp/cp-rehydrate.js +2 -1
  119. package/neural-interface/public/shared/cp/cp-styles.js +15 -0
  120. package/neural-interface/public/shared/editor/editor-ai-logic.js +409 -0
  121. package/neural-interface/public/shared/editor/editor-ai.js +619 -0
  122. package/neural-interface/public/shared/editor/editor-commands.js +212 -0
  123. package/neural-interface/public/shared/editor/editor-compare.js +103 -0
  124. package/neural-interface/public/shared/editor/editor-completion-list.js +172 -0
  125. package/neural-interface/public/shared/editor/editor-doc.js +257 -0
  126. package/neural-interface/public/shared/editor/editor-git-logic.js +131 -0
  127. package/neural-interface/public/shared/editor/editor-git.js +95 -0
  128. package/neural-interface/public/shared/editor/editor-languages.js +156 -0
  129. package/neural-interface/public/shared/editor/editor-lint.js +229 -0
  130. package/neural-interface/public/shared/editor/editor-loader.js +51 -0
  131. package/neural-interface/public/shared/editor/editor-markdown.js +90 -0
  132. package/neural-interface/public/shared/editor/editor-menu.js +233 -0
  133. package/neural-interface/public/shared/editor/editor-minimap.js +371 -0
  134. package/neural-interface/public/shared/editor/editor-palette-logic.js +192 -0
  135. package/neural-interface/public/shared/editor/editor-palette.js +125 -0
  136. package/neural-interface/public/shared/editor/editor-panel-logic.js +288 -0
  137. package/neural-interface/public/shared/editor/editor-panel.js +2099 -0
  138. package/neural-interface/public/shared/editor/editor-prefs.js +224 -0
  139. package/neural-interface/public/shared/editor/editor-safety.js +500 -0
  140. package/neural-interface/public/shared/editor/editor-search.js +352 -0
  141. package/neural-interface/public/shared/editor/editor-statusbar.js +144 -0
  142. package/neural-interface/public/shared/editor/editor-tabs-logic.js +331 -0
  143. package/neural-interface/public/shared/editor/editor-tabs.js +233 -0
  144. package/neural-interface/public/shared/editor/editor-theme.js +244 -0
  145. package/neural-interface/public/shared/editor/editor-view.js +332 -0
  146. package/neural-interface/public/shared/explorer/fx-api.js +216 -0
  147. package/neural-interface/public/shared/explorer/fx-links.js +204 -0
  148. package/neural-interface/public/shared/explorer/fx-logic.js +537 -0
  149. package/neural-interface/public/shared/explorer/fx-main.js +865 -0
  150. package/neural-interface/public/shared/explorer/fx-names.js +87 -0
  151. package/neural-interface/public/shared/explorer/fx-ops.js +370 -0
  152. package/neural-interface/public/shared/explorer/fx-popups.js +490 -0
  153. package/neural-interface/public/shared/explorer/fx-tree.js +790 -0
  154. package/neural-interface/public/shared/file-links.js +339 -0
  155. package/neural-interface/public/shared/greeting/greeting-card.js +178 -0
  156. package/neural-interface/public/shared/greeting/greeting-view.js +148 -0
  157. package/neural-interface/public/shared/html-shell.js +169 -170
  158. package/neural-interface/public/shared/ocp-v2/ocp-v2-childpanel.js +4 -0
  159. package/neural-interface/public/shared/ocp-v2/ocp-v2-greeting.js +47 -0
  160. package/neural-interface/public/shared/ocp-v2/ocp-v2-panel.js +99 -17
  161. package/neural-interface/public/shared/ocp-v2/ocp-v2-render.js +7 -1
  162. package/neural-interface/public/shared/ocp-v2/ocp-v2-send.js +5 -0
  163. package/neural-interface/public/shared/ocp-v2/ocp-v2-styles.js +40 -0
  164. package/neural-interface/public/shared/ocp-v2/ocp-v2-ws.js +3 -2
  165. package/neural-interface/public/shared/pwa-install-page.js +62 -0
  166. package/neural-interface/public/shared/pwa-install.css +12 -0
  167. package/neural-interface/public/shared/pwa-install.js +127 -0
  168. package/neural-interface/public/shared/settings/settings-code-editor-pane.js +412 -0
  169. package/neural-interface/public/shared/settings/settings-code-editor.js +328 -0
  170. package/neural-interface/public/shared/settings/settings-greeting.js +133 -0
  171. package/neural-interface/public/shared/settings/settings-ia.js +18 -4
  172. package/neural-interface/public/shared/settings/settings-rules.js +160 -0
  173. package/neural-interface/public/shared/settings/settings-user-context.js +154 -0
  174. package/neural-interface/public/shared/start-bridge.js +802 -0
  175. package/neural-interface/public/shared/styleguide/sg-colors.js +5 -2
  176. package/neural-interface/public/shared/styleguide/sg-import-export.js +4 -3
  177. package/neural-interface/public/shared/styleguide/sg-kit.js +4 -1
  178. package/neural-interface/public/shared/styleguide/sg-panel.js +9 -1
  179. package/neural-interface/public/shared/styleguide/sg-typography.js +1 -0
  180. package/neural-interface/public/shared/styles.css +2219 -1117
  181. package/neural-interface/public/shared/synabun-mascot.js +20 -56
  182. package/neural-interface/public/shared/term-ready.js +307 -0
  183. package/neural-interface/public/shared/tunnel-view.js +167 -0
  184. package/neural-interface/public/shared/ui-claude-panel.js +160 -19
  185. package/neural-interface/public/shared/ui-command-runner.js +251 -468
  186. package/neural-interface/public/shared/ui-detail.js +15 -0
  187. package/neural-interface/public/shared/ui-explorer.js +22 -0
  188. package/neural-interface/public/shared/ui-file-explorer.js +481 -2496
  189. package/neural-interface/public/shared/ui-file-links.js +103 -0
  190. package/neural-interface/public/shared/ui-help.js +26 -0
  191. package/neural-interface/public/shared/ui-invite.js +126 -116
  192. package/neural-interface/public/shared/ui-judgments.js +1 -1
  193. package/neural-interface/public/shared/ui-keybinds.js +4 -0
  194. package/neural-interface/public/shared/ui-loading.js +367 -12
  195. package/neural-interface/public/shared/ui-menubar.js +21 -55
  196. package/neural-interface/public/shared/ui-native-loop-router-state.js +11 -0
  197. package/neural-interface/public/shared/ui-native-loop-router.js +15 -2
  198. package/neural-interface/public/shared/ui-navbar.js +45 -21
  199. package/neural-interface/public/shared/ui-settings.js +491 -547
  200. package/neural-interface/public/shared/ui-sidepanel-runtime.js +2 -1
  201. package/neural-interface/public/shared/ui-sync.js +16 -0
  202. package/neural-interface/public/shared/ui-terminal.js +153 -149
  203. package/neural-interface/public/shared/ui-tunnel.js +130 -0
  204. package/neural-interface/public/shared/ui-tutorial-steps.js +6 -2
  205. package/neural-interface/public/shared/ui-whiteboard.js +12 -0
  206. package/neural-interface/public/shared/ui-worker-end.js +245 -0
  207. package/neural-interface/public/shared/ui-workspaces.js +41 -27
  208. package/neural-interface/public/shared/worker-end.js +251 -0
  209. package/neural-interface/public/sw.js +3 -1
  210. package/neural-interface/public/variant/2d/main.js +2 -1
  211. package/neural-interface/public/variant/3d/main.js +2 -1
  212. package/neural-interface/public/vendor/codemirror/chunks/apl-CSDKT72Z.js +1 -0
  213. package/neural-interface/public/vendor/codemirror/chunks/asciiarmor-JYYPU5BM.js +1 -0
  214. package/neural-interface/public/vendor/codemirror/chunks/asn1-NRWCZDA6.js +1 -0
  215. package/neural-interface/public/vendor/codemirror/chunks/asterisk-5OU6D4ZQ.js +1 -0
  216. package/neural-interface/public/vendor/codemirror/chunks/brainfuck-ID4N4SYC.js +1 -0
  217. package/neural-interface/public/vendor/codemirror/chunks/chunk-4RZWGCS2.js +1 -0
  218. package/neural-interface/public/vendor/codemirror/chunks/chunk-ACTJM3JR.js +11 -0
  219. package/neural-interface/public/vendor/codemirror/chunks/chunk-S6BMAXLK.js +1 -0
  220. package/neural-interface/public/vendor/codemirror/chunks/chunk-TO2RDOHQ.js +3 -0
  221. package/neural-interface/public/vendor/codemirror/chunks/chunk-US2GMZZQ.js +13 -0
  222. package/neural-interface/public/vendor/codemirror/chunks/chunk-WYYO5ZAJ.js +1 -0
  223. package/neural-interface/public/vendor/codemirror/chunks/chunk-XALZTDJC.js +1 -0
  224. package/neural-interface/public/vendor/codemirror/chunks/chunk-YRNUTRDV.js +1 -0
  225. package/neural-interface/public/vendor/codemirror/chunks/chunk-Z3QOKH62.js +1 -0
  226. package/neural-interface/public/vendor/codemirror/chunks/clike-OWQBSOGL.js +1 -0
  227. package/neural-interface/public/vendor/codemirror/chunks/clojure-KV75UQWC.js +1 -0
  228. package/neural-interface/public/vendor/codemirror/chunks/cmake-GTEP5Y3C.js +1 -0
  229. package/neural-interface/public/vendor/codemirror/chunks/cobol-WCZ7QEGJ.js +1 -0
  230. package/neural-interface/public/vendor/codemirror/chunks/coffeescript-TGLBOT6B.js +1 -0
  231. package/neural-interface/public/vendor/codemirror/chunks/commonlisp-WVVI432V.js +1 -0
  232. package/neural-interface/public/vendor/codemirror/chunks/crystal-S6R6HLKQ.js +1 -0
  233. package/neural-interface/public/vendor/codemirror/chunks/css-PDA2RINP.js +1 -0
  234. package/neural-interface/public/vendor/codemirror/chunks/cypher-HL5QPL4C.js +1 -0
  235. package/neural-interface/public/vendor/codemirror/chunks/d-HHJH2HAJ.js +1 -0
  236. package/neural-interface/public/vendor/codemirror/chunks/diff-MN3A35BY.js +1 -0
  237. package/neural-interface/public/vendor/codemirror/chunks/dist-25SQLBVS.js +3 -0
  238. package/neural-interface/public/vendor/codemirror/chunks/dist-27ZGGJJS.js +1 -0
  239. package/neural-interface/public/vendor/codemirror/chunks/dist-4E52OJIF.js +1 -0
  240. package/neural-interface/public/vendor/codemirror/chunks/dist-6FT73XDC.js +2 -0
  241. package/neural-interface/public/vendor/codemirror/chunks/dist-BE6YGRMW.js +1 -0
  242. package/neural-interface/public/vendor/codemirror/chunks/dist-CJSGKW2O.js +1 -0
  243. package/neural-interface/public/vendor/codemirror/chunks/dist-CRPH2WLI.js +1 -0
  244. package/neural-interface/public/vendor/codemirror/chunks/dist-CTG7JEQN.js +1 -0
  245. package/neural-interface/public/vendor/codemirror/chunks/dist-I3FEVELX.js +1 -0
  246. package/neural-interface/public/vendor/codemirror/chunks/dist-IM5BBSWZ.js +1 -0
  247. package/neural-interface/public/vendor/codemirror/chunks/dist-KERUKTIF.js +1 -0
  248. package/neural-interface/public/vendor/codemirror/chunks/dist-LXTMWZAM.js +1 -0
  249. package/neural-interface/public/vendor/codemirror/chunks/dist-MABASSET.js +1 -0
  250. package/neural-interface/public/vendor/codemirror/chunks/dist-MJVBUZLL.js +3 -0
  251. package/neural-interface/public/vendor/codemirror/chunks/dist-MNB3WZVU.js +1 -0
  252. package/neural-interface/public/vendor/codemirror/chunks/dist-QYWXOE3U.js +7 -0
  253. package/neural-interface/public/vendor/codemirror/chunks/dist-TEBOFWLZ.js +1 -0
  254. package/neural-interface/public/vendor/codemirror/chunks/dist-UNXFROM3.js +1 -0
  255. package/neural-interface/public/vendor/codemirror/chunks/dist-XL2SLQO6.js +1 -0
  256. package/neural-interface/public/vendor/codemirror/chunks/dist-XO42Y3AP.js +6 -0
  257. package/neural-interface/public/vendor/codemirror/chunks/dist-Y2WWDWJK.js +1 -0
  258. package/neural-interface/public/vendor/codemirror/chunks/dist-YFEZKWMU.js +1 -0
  259. package/neural-interface/public/vendor/codemirror/chunks/dist-ZY7SWQO6.js +1 -0
  260. package/neural-interface/public/vendor/codemirror/chunks/dockerfile-3Y4R4NCH.js +1 -0
  261. package/neural-interface/public/vendor/codemirror/chunks/dtd-5343GJ4P.js +1 -0
  262. package/neural-interface/public/vendor/codemirror/chunks/dylan-FI2CXSW2.js +1 -0
  263. package/neural-interface/public/vendor/codemirror/chunks/ebnf-GNQ77YWA.js +1 -0
  264. package/neural-interface/public/vendor/codemirror/chunks/ecl-MRPUAE2L.js +1 -0
  265. package/neural-interface/public/vendor/codemirror/chunks/eiffel-DZROES27.js +1 -0
  266. package/neural-interface/public/vendor/codemirror/chunks/elm-4KICRFKQ.js +1 -0
  267. package/neural-interface/public/vendor/codemirror/chunks/erlang-3IHYC263.js +1 -0
  268. package/neural-interface/public/vendor/codemirror/chunks/factor-C6D3E5UK.js +1 -0
  269. package/neural-interface/public/vendor/codemirror/chunks/fcl-VYTWUJMI.js +1 -0
  270. package/neural-interface/public/vendor/codemirror/chunks/forth-ZBO2EH5M.js +1 -0
  271. package/neural-interface/public/vendor/codemirror/chunks/fortran-5NDCRWIK.js +1 -0
  272. package/neural-interface/public/vendor/codemirror/chunks/gas-SKRMOXFP.js +1 -0
  273. package/neural-interface/public/vendor/codemirror/chunks/gherkin-L45YXZLW.js +1 -0
  274. package/neural-interface/public/vendor/codemirror/chunks/groovy-GLPTLN44.js +1 -0
  275. package/neural-interface/public/vendor/codemirror/chunks/haskell-E7NF7P4Y.js +1 -0
  276. package/neural-interface/public/vendor/codemirror/chunks/haxe-K2NVDOB6.js +1 -0
  277. package/neural-interface/public/vendor/codemirror/chunks/http-Q6DRQIJB.js +1 -0
  278. package/neural-interface/public/vendor/codemirror/chunks/idl-MZYNJVLN.js +1 -0
  279. package/neural-interface/public/vendor/codemirror/chunks/javascript-6YRYK2HR.js +1 -0
  280. package/neural-interface/public/vendor/codemirror/chunks/julia-HXEL2T5G.js +1 -0
  281. package/neural-interface/public/vendor/codemirror/chunks/livescript-MV7CI2JZ.js +1 -0
  282. package/neural-interface/public/vendor/codemirror/chunks/lua-E36NHOOD.js +1 -0
  283. package/neural-interface/public/vendor/codemirror/chunks/mathematica-3XOZZCMC.js +1 -0
  284. package/neural-interface/public/vendor/codemirror/chunks/mbox-ODYGFKS7.js +1 -0
  285. package/neural-interface/public/vendor/codemirror/chunks/mirc-WB43AWF5.js +1 -0
  286. package/neural-interface/public/vendor/codemirror/chunks/mllike-ZT6ET7ZH.js +1 -0
  287. package/neural-interface/public/vendor/codemirror/chunks/modelica-LLMZPFGA.js +1 -0
  288. package/neural-interface/public/vendor/codemirror/chunks/mscgen-2OPNNYFH.js +1 -0
  289. package/neural-interface/public/vendor/codemirror/chunks/mumps-6P3HWJH6.js +1 -0
  290. package/neural-interface/public/vendor/codemirror/chunks/nginx-TCGKQTRW.js +1 -0
  291. package/neural-interface/public/vendor/codemirror/chunks/nsis-ATIGIXTJ.js +1 -0
  292. package/neural-interface/public/vendor/codemirror/chunks/ntriples-5PLBI5OV.js +1 -0
  293. package/neural-interface/public/vendor/codemirror/chunks/octave-QF2S3NL6.js +1 -0
  294. package/neural-interface/public/vendor/codemirror/chunks/oz-JFIVFFH2.js +1 -0
  295. package/neural-interface/public/vendor/codemirror/chunks/pascal-JG6SW76V.js +1 -0
  296. package/neural-interface/public/vendor/codemirror/chunks/perl-4B3IWVVO.js +1 -0
  297. package/neural-interface/public/vendor/codemirror/chunks/pig-ABEBH4MI.js +1 -0
  298. package/neural-interface/public/vendor/codemirror/chunks/powershell-H32XE7TL.js +1 -0
  299. package/neural-interface/public/vendor/codemirror/chunks/properties-NN644MMA.js +1 -0
  300. package/neural-interface/public/vendor/codemirror/chunks/protobuf-YKWQK2J3.js +1 -0
  301. package/neural-interface/public/vendor/codemirror/chunks/pug-RIYXEKFW.js +1 -0
  302. package/neural-interface/public/vendor/codemirror/chunks/puppet-JZQ5WPEV.js +1 -0
  303. package/neural-interface/public/vendor/codemirror/chunks/python-EBCGGHAC.js +1 -0
  304. package/neural-interface/public/vendor/codemirror/chunks/q-5UMH2YQY.js +1 -0
  305. package/neural-interface/public/vendor/codemirror/chunks/r-7XMGG453.js +1 -0
  306. package/neural-interface/public/vendor/codemirror/chunks/rpm-IEV7TSLR.js +1 -0
  307. package/neural-interface/public/vendor/codemirror/chunks/ruby-T4SPFPNM.js +1 -0
  308. package/neural-interface/public/vendor/codemirror/chunks/sas-GEDX67ET.js +1 -0
  309. package/neural-interface/public/vendor/codemirror/chunks/scheme-FTLOWKPC.js +1 -0
  310. package/neural-interface/public/vendor/codemirror/chunks/shell-HUFRKFSJ.js +1 -0
  311. package/neural-interface/public/vendor/codemirror/chunks/sieve-P5QUDVYO.js +1 -0
  312. package/neural-interface/public/vendor/codemirror/chunks/smalltalk-2SORJNQI.js +1 -0
  313. package/neural-interface/public/vendor/codemirror/chunks/solr-M22TGAR5.js +1 -0
  314. package/neural-interface/public/vendor/codemirror/chunks/sparql-4M3KL752.js +1 -0
  315. package/neural-interface/public/vendor/codemirror/chunks/spreadsheet-3K23LA4N.js +1 -0
  316. package/neural-interface/public/vendor/codemirror/chunks/sql-VHRFMUBI.js +1 -0
  317. package/neural-interface/public/vendor/codemirror/chunks/stex-QZDAQUY2.js +1 -0
  318. package/neural-interface/public/vendor/codemirror/chunks/stylus-7ZDKAKBD.js +1 -0
  319. package/neural-interface/public/vendor/codemirror/chunks/swift-WCLCLGMR.js +1 -0
  320. package/neural-interface/public/vendor/codemirror/chunks/tcl-JLMU6WOK.js +1 -0
  321. package/neural-interface/public/vendor/codemirror/chunks/textile-4CHDKZ4T.js +1 -0
  322. package/neural-interface/public/vendor/codemirror/chunks/tiddlywiki-DKMCZZQ4.js +1 -0
  323. package/neural-interface/public/vendor/codemirror/chunks/tiki-F4JIG36Q.js +1 -0
  324. package/neural-interface/public/vendor/codemirror/chunks/toml-BHMHTIM2.js +1 -0
  325. package/neural-interface/public/vendor/codemirror/chunks/troff-YQ2KV5NT.js +1 -0
  326. package/neural-interface/public/vendor/codemirror/chunks/ttcn-ZHRCVFJY.js +1 -0
  327. package/neural-interface/public/vendor/codemirror/chunks/ttcn-cfg-KJOVTY4X.js +1 -0
  328. package/neural-interface/public/vendor/codemirror/chunks/turtle-535P5N6I.js +1 -0
  329. package/neural-interface/public/vendor/codemirror/chunks/vb-76UMQHOT.js +1 -0
  330. package/neural-interface/public/vendor/codemirror/chunks/vbscript-CGNDTK6Z.js +1 -0
  331. package/neural-interface/public/vendor/codemirror/chunks/velocity-VSJDO5T5.js +1 -0
  332. package/neural-interface/public/vendor/codemirror/chunks/verilog-4T6ETXIP.js +1 -0
  333. package/neural-interface/public/vendor/codemirror/chunks/vhdl-T7FNAXUN.js +1 -0
  334. package/neural-interface/public/vendor/codemirror/chunks/webidl-P6MJSJ6J.js +1 -0
  335. package/neural-interface/public/vendor/codemirror/chunks/xquery-UOVILRGN.js +1 -0
  336. package/neural-interface/public/vendor/codemirror/chunks/yacas-CV2APROL.js +1 -0
  337. package/neural-interface/public/vendor/codemirror/chunks/z80-3KFNVM4M.js +1 -0
  338. package/neural-interface/public/vendor/codemirror/codemirror-6.43.14-75f05e67.min.js +2 -0
  339. package/neural-interface/public/vendor/codemirror/manifest.json +60 -0
  340. package/neural-interface/scripts/build-vendor.mjs +78 -2
  341. package/neural-interface/scripts/rebuild-pty.js +15 -12
  342. package/neural-interface/scripts/vendor-codemirror-entry.js +121 -0
  343. package/neural-interface/server.js +1092 -1212
  344. package/neural-interface/templates/rulesets/README.md +1 -1
  345. package/package.json +13 -7
  346. package/postinstall.js +11 -81
  347. package/setup.js +111 -6
  348. package/benchmarks/browser-live-results.json +0 -79
  349. package/benchmarks/browser-v2-results.json +0 -258
  350. package/docs/browser-v2.md +0 -109
  351. package/docs/hook-integrity.md +0 -89
  352. package/docs/judgments.md +0 -182
  353. package/docs/style-guide.md +0 -246
  354. package/neural-interface/lib/codex-capabilities.d.ts +0 -41
  355. package/neural-interface/public/category-logos/claude.svg +0 -1
  356. package/neural-interface/public/category-logos/gemini-color.svg +0 -1
  357. package/neural-interface/public/category-logos/openai.svg +0 -1
  358. package/neural-interface/public/category-logos/synabun.png +0 -0
  359. package/neural-interface/public/claude.svg +0 -1
  360. package/neural-interface/public/gallery.svg +0 -6
  361. package/neural-interface/public/leonardoai.svg +0 -1
  362. package/neural-interface/public/mcp.svg +0 -4
  363. package/neural-interface/public/opencode-logo-dark-square.svg +0 -1
  364. package/neural-interface/public/pin-svgrepo-com.svg +0 -25
  365. package/neural-interface/public/sessionicon.svg +0 -8
  366. package/neural-interface/public/toolbar-album.svg +0 -8
  367. package/neural-interface/public/toolbar-question.svg +0 -6
  368. package/neural-interface/public/toolbar-sessions.svg +0 -6
  369. package/neural-interface/scripts/codex-protocol-fixture.mjs +0 -83
  370. package/scripts/audit-claude-hook-usage.mjs +0 -12
  371. package/scripts/audit-memory-duplicates.mjs +0 -44
  372. package/scripts/measure-claude-hook-overhead.mjs +0 -77
  373. package/scripts/recover-user-config.mjs +0 -254
  374. package/skills/synabun/synabunicon.png +0 -0
@@ -11,7 +11,8 @@ export function routeSidepanelEvent(event, data) {
11
11
  const host = sidepanelHost();
12
12
  if (!host) return false;
13
13
  if (hostedSidepanelId) {
14
- if (!['open-plan-editor', 'open-changelog-editor', 'browser:reconnect'].includes(event)) return false;
14
+ // The code editor lives in the main window: what opens it from a detached session goes there.
15
+ if (!['open-plan-editor', 'open-changelog-editor', 'open-file-editor', 'browser:reconnect'].includes(event)) return false;
15
16
  host.emitFromSession(hostedSidepanelId, event, data);
16
17
  return true;
17
18
  }
@@ -74,6 +74,18 @@ const SYNC_HANDLERS = {
74
74
  // Assistant → Models toggles (every provider): the Assistant's model menus,
75
75
  // Routes editor and Models manager reload their lists.
76
76
  'assistant:hidden-models-changed': (msg) => emit('sync:assistant:hidden-models-changed', msg),
77
+ // A provider lists other models than before (CLI update, account change, OpenCode
78
+ // provider connected): the same menus reload.
79
+ 'assistant:catalog-changed': (msg) => emit('sync:assistant:catalog-changed', msg),
80
+
81
+ // Settings → Automation behavior switched in another window: an open Settings follows.
82
+ 'assistant:user-context-changed': (msg) => emit('sync:assistant:user-context-changed', msg),
83
+
84
+ // Code editor preferences saved in another window (word wrap, font size…): the editor follows.
85
+ 'editor:config-changed': (msg) => emit('sync:editor:config-changed', msg),
86
+
87
+ // Settings → Greeting switched in another window: an open Settings follows.
88
+ 'greeting:changed': (msg) => emit('sync:greeting:changed', msg),
77
89
 
78
90
  // Card sync (relayed from other clients)
79
91
  'card:opened': (msg) => emit('sync:card:opened', msg),
@@ -178,6 +190,10 @@ const SYNC_HANDLERS = {
178
190
  // Skin change sync
179
191
  'skin:changed': (msg) => emit('sync:skin:changed', msg),
180
192
 
193
+ // The server says it is restarting, before it goes: every window shows the
194
+ // restart, not only the one that asked for it (ui-loading.js).
195
+ 'server:restarting': (msg) => emit('server:restarting', msg),
196
+
181
197
  // Session indexing progress
182
198
  'indexing:started': (msg) => emit('ws:message', msg),
183
199
  'indexing:session-started': (msg) => emit('ws:message', msg),
@@ -16,10 +16,11 @@ import { getWhiteboardElementById } from './ui-whiteboard.js';
16
16
  import { createFrameRenderer } from './utils.js';
17
17
  import { notify, NOTIF_TYPE } from './ui-notifications.js';
18
18
  import { getProviderMeta } from './provider-icons.js';
19
+ import { readinessKind, typeWhenReady } from './term-ready.js';
19
20
  import { initRendererManager, acquireRenderer, disposeRenderer, hasWebgl, noteFocus, adoptRenderer, rendererState, rendererStats } from './term-renderer.js';
20
21
  import { mountAssistant } from './assistant/asst-panel.js';
21
22
  import { getAssistantSession } from './assistant/asst-api.js';
22
- import { brainLabel, normalizeBrain, readStoredBrain } from './assistant/asst-state.js';
23
+ import { brainLabel, normalizeBrain, readStoredBrain, restoredSessionName, sessionNameGivenUp } from './assistant/asst-state.js';
23
24
  import { initAssistant, registerAssistantHost, openAssistant, assistantOwnerOf, fetchLiveAssistantSessions, isAssistantSessionLive } from './ui-assistant.js';
24
25
 
25
26
  const $ = (id) => document.getElementById(id);
@@ -1424,10 +1425,22 @@ function _accumReplay(sess, text) {
1424
1425
  if (sess._replayBuf.length > 16384) sess._replayBuf = sess._replayBuf.slice(-8192);
1425
1426
  }
1426
1427
 
1427
- function _notifyOutputWatchers(sess, text) {
1428
- if (!sess?._outputWatchers?.size || !text) return;
1428
+ // `replaced`: a snapshot just became the whole screen (connect / reconnect),
1429
+ // so `text` is everything there is rather than more of the stream.
1430
+ function _notifyOutputWatchers(sess, text, replaced = false) {
1431
+ if (!sess?._outputWatchers?.size) return;
1432
+ if (!text && !replaced) return;
1429
1433
  for (const w of [...sess._outputWatchers]) {
1430
- try { w(text); } catch {}
1434
+ try { w(text || '', replaced); } catch {}
1435
+ }
1436
+ }
1437
+
1438
+ // A socket of this session opened (first connect or a reconnect). The wait for
1439
+ // readiness in _sendOnceReady starts its clock here, not when it was asked.
1440
+ function _notifySocketOpen(sess) {
1441
+ if (!sess?._openWatchers?.size) return;
1442
+ for (const w of [...sess._openWatchers]) {
1443
+ try { w(); } catch {}
1431
1444
  }
1432
1445
  }
1433
1446
 
@@ -1500,7 +1513,7 @@ function _handleTermWsEvent(e, ctx) {
1500
1513
  else term?.write(msg.data);
1501
1514
  }
1502
1515
  if (NOTIF_TRACKED_CLI_PROFILES.has(profile)) _scheduleCliStatusCheck(sessionId);
1503
- _notifyOutputWatchers(sess, text);
1516
+ _notifyOutputWatchers(sess, text, !!wantsReset);
1504
1517
  }
1505
1518
  if (msg.type === 'exit') {
1506
1519
  if (sess) sess._exitReceived = true;
@@ -1538,6 +1551,7 @@ function _onTermWsOpen(sessionId, ws, fallback = null) {
1538
1551
  try { (s?._terminalWriter || fallback?._terminalWriter)?.reset?.(); } catch {}
1539
1552
  send({ type: 'hello', flow: 1 });
1540
1553
  if (document.hidden) send({ type: 'visibility', visible: false });
1554
+ _notifySocketOpen(s);
1541
1555
  const term = s?.term;
1542
1556
  if (!term) return;
1543
1557
  // Hidden container (restore, parked dock) → no resize now; _scheduleFit
@@ -3381,6 +3395,7 @@ async function reconnectBrowserSession(sessionId, liveData, saved) {
3381
3395
  ws, viewport, ro, renderer: null,
3382
3396
  dead: false, pinned: saved?.pinned || false,
3383
3397
  _userRenamed: saved?.userRenamed || false,
3398
+ _floatColor: saved?.floatColor || null,
3384
3399
  _isBrowser: true, _browserUrl: currentUrl, _browserTitle: liveData?.title || '',
3385
3400
  _browserCanvas: canvas, _browserCtx: ctx, _frameRenderer: frameRenderer, _visibilityHandler,
3386
3401
  _idleTimer: null, _screencastIdlePaused: false,
@@ -3452,7 +3467,7 @@ function _updateAssistantBrainChip(session) {
3452
3467
  chip.hidden = !label;
3453
3468
  }
3454
3469
 
3455
- /** After a tab/float rename, push the new label into the panel (marks it user-renamed). */
3470
+ /** After a tab/float rename, push the new label into the panel (marks it user-renamed; the panel writes it to the session record). */
3456
3471
  function _syncAssistantLabel(session) {
3457
3472
  if (!session?._isAssistant || !session._assistant) return;
3458
3473
  if (session._assistant.getTitle() !== session.label) session._assistant.setTitle(session.label);
@@ -3469,6 +3484,11 @@ function _mountAssistantSession(sessionId, { meta = null, saved = null, brain =
3469
3484
  if (existing) { if (!_restoringLayout) _focusAssistantTab(existing); return existing; }
3470
3485
 
3471
3486
  const resolvedBrain = normalizeBrain(brain || meta?.brain || saved?.brain || readStoredBrain(storage));
3487
+ // A renamed session comes back under its name: the session record's (a rename made in either
3488
+ // host, the one the Sessions menu lists), else the rename saved with this tab. A record whose
3489
+ // name was given up says so: the label and the renamed flag saved with a tab are stale.
3490
+ const named = restoredSessionName(meta, label);
3491
+ const givenUp = sessionNameGivenUp(meta);
3472
3492
  const viewport = document.createElement('div');
3473
3493
  viewport.className = 'term-viewport assistant-viewport';
3474
3494
  viewport.dataset.sessionId = sessionId;
@@ -3480,13 +3500,13 @@ function _mountAssistantSession(sessionId, { meta = null, saved = null, brain =
3480
3500
  id: sessionId,
3481
3501
  profile: 'assistant',
3482
3502
  cwd: resolvedBrain.project || null,
3483
- label: label || saved?.label || meta?.title || profileDef?.label || 'Assistant',
3503
+ label: named || (givenUp ? '' : saved?.label) || meta?.title || profileDef?.label || 'Assistant',
3484
3504
  term: null, fitAddon: null, searchAddon: null,
3485
3505
  ws: null, // no PTY socket — the panel owns /ws/assistant/:id
3486
3506
  viewport, ro: null, renderer: null,
3487
3507
  dead: false,
3488
3508
  pinned: saved?.pinned || false,
3489
- _userRenamed: saved?.userRenamed || !!label,
3509
+ _userRenamed: !givenUp && (saved?.userRenamed || !!named),
3490
3510
  _floatColor: saved?.floatColor || null,
3491
3511
  _isAssistant: true, // flag for tab-type-specific logic
3492
3512
  _assistant: null, // controller (set right after mount)
@@ -3510,6 +3530,19 @@ function _mountAssistantSession(sessionId, { meta = null, saved = null, brain =
3510
3530
  if (titleEl && titleEl.contentEditable !== 'true') titleEl.textContent = next;
3511
3531
  saveSessionRegistry();
3512
3532
  },
3533
+ // A rename the panel could not save: the tab and its saved label go back to the record's name.
3534
+ setName(text, renamed) {
3535
+ const s = live();
3536
+ const next = String(text || '').trim();
3537
+ if (!next) return;
3538
+ s.label = next;
3539
+ s._userRenamed = !!renamed;
3540
+ renderTabBar();
3541
+ const titleEl = _detachedTabs.get(sessionId)?.el?.querySelector('.term-float-tab-title');
3542
+ if (titleEl && titleEl.contentEditable !== 'true') titleEl.textContent = next;
3543
+ saveSessionRegistry();
3544
+ saveTerminalLayout();
3545
+ },
3513
3546
  setStatus(status) { setSessionStatusBadge(sessionId, status); },
3514
3547
  setBrain(b) {
3515
3548
  const s = live();
@@ -3524,7 +3557,7 @@ function _mountAssistantSession(sessionId, { meta = null, saved = null, brain =
3524
3557
  requestDetach() { const idx = _sessions.findIndex(s => s.id === sessionId); if (idx >= 0 && !_detachedTabs.has(sessionId)) detachTab(idx); },
3525
3558
  };
3526
3559
  session._assistant = mountAssistant(viewport, { sessionId, brain: resolvedBrain, host, session: meta, label: session.label, reattach });
3527
- if (session._userRenamed && session.label) session._assistant.setTitle(session.label);
3560
+ if (session._userRenamed && session.label) session._assistant.setTitle(session.label, { restored: true });
3528
3561
 
3529
3562
  renderTabBar();
3530
3563
  if (_restoringLayout) {
@@ -4488,13 +4521,29 @@ function _applyFloatColor(win, colorId) {
4488
4521
  win.style.setProperty('--fh', String(c.h));
4489
4522
  win.style.setProperty('--fs', c.s + '%');
4490
4523
  }
4491
- // Update color strip
4524
+ // Update color strip. Only the color: the `background` shorthand would reset
4525
+ // the stylesheet's background-clip, and the dot would fill its whole hit area.
4492
4526
  const strip = win.querySelector('.term-float-color-strip');
4493
4527
  if (strip) {
4494
- strip.style.background = c.s === 0 ? 'rgba(255,255,255,0.15)' : `hsl(${c.h}, ${Math.round(c.s * 0.5)}%, 30%)`;
4528
+ strip.style.backgroundColor = c.s === 0 ? 'rgba(255,255,255,0.15)' : `hsl(${c.h}, ${Math.round(c.s * 0.5)}%, 30%)`;
4495
4529
  }
4496
4530
  }
4497
4531
 
4532
+ /** Place the palette popup by the color button: above it, or below when there
4533
+ * is no room, with its first swatch over the dot and inside the viewport. */
4534
+ function _placeFloatColorPicker(picker, colorBtn) {
4535
+ const btn = colorBtn.getBoundingClientRect();
4536
+ const box = picker.getBoundingClientRect();
4537
+ const first = picker.firstElementChild?.getBoundingClientRect();
4538
+ const inset = first ? first.left + first.width / 2 - box.left : 0;
4539
+ const margin = 8, gap = 6;
4540
+ const left = btn.left + btn.width / 2 - inset;
4541
+ let top = btn.top - gap - box.height;
4542
+ if (top < margin) top = btn.bottom + gap;
4543
+ picker.style.left = Math.max(margin, Math.min(left, window.innerWidth - box.width - margin)) + 'px';
4544
+ picker.style.top = Math.max(margin, Math.min(top, window.innerHeight - box.height - margin)) + 'px';
4545
+ }
4546
+
4498
4547
  function getRightPanelReservedWidth(cs = getComputedStyle(document.documentElement)) {
4499
4548
  return parseFloat(cs.getPropertyValue('--right-panel-width'))
4500
4549
  || parseFloat(cs.getPropertyValue('--claude-panel-width'))
@@ -4813,19 +4862,18 @@ function detachTab(idx, opts = {}) {
4813
4862
  if (existing) { existing.remove(); return; }
4814
4863
  const picker = document.createElement('div');
4815
4864
  picker.className = 'float-color-picker';
4816
- // Position above the color strip button
4817
- const btnRect = colorBtn.getBoundingClientRect();
4818
- picker.style.left = btnRect.left + 'px';
4819
- picker.style.bottom = (window.innerHeight - btnRect.top + 6) + 'px';
4820
- picker.style.top = 'auto';
4865
+ // The session object may have been replaced by _pushSession after a
4866
+ // reconnection — read and write the color on the one in _sessions
4867
+ const live = () => _sessions.find(s => s.id === session.id) || session;
4821
4868
  FLOAT_COLORS.forEach(c => {
4822
4869
  const swatch = document.createElement('button');
4823
4870
  swatch.className = 'float-color-swatch';
4824
- if (c.id === (session._floatColor || 'blue')) swatch.classList.add('active');
4871
+ if (c.id === (live()._floatColor || 'blue')) swatch.classList.add('active');
4825
4872
  swatch.style.background = c.s === 0 ? 'rgba(255,255,255,0.15)' : `hsl(${c.h}, ${Math.round(c.s * 0.5)}%, 25%)`;
4826
4873
  swatch.addEventListener('click', (ev) => {
4827
4874
  ev.stopPropagation();
4828
4875
  session._floatColor = c.id;
4876
+ live()._floatColor = c.id;
4829
4877
  _applyFloatColor(win, c.id);
4830
4878
  saveSessionRegistry();
4831
4879
  saveTerminalLayout();
@@ -4834,8 +4882,10 @@ function detachTab(idx, opts = {}) {
4834
4882
  picker.appendChild(swatch);
4835
4883
  });
4836
4884
  document.body.appendChild(picker);
4837
- // Close on outside click
4885
+ _placeFloatColorPicker(picker, colorBtn);
4886
+ // Close on outside click (the dot closes it through the toggle above)
4838
4887
  const close = (ev) => {
4888
+ if (colorBtn.contains(ev.target) && picker.isConnected) return;
4839
4889
  if (!picker.contains(ev.target)) { picker.remove(); document.removeEventListener('mousedown', close); }
4840
4890
  };
4841
4891
  setTimeout(() => document.addEventListener('mousedown', close), 0);
@@ -5722,6 +5772,8 @@ export async function initTerminal() {
5722
5772
  window.addEventListener('keydown', (e) => {
5723
5773
  if (e.key !== 'Escape') return;
5724
5774
  const target = e.target;
5775
+ // The command library owns Escape for its dialogs, search and window.
5776
+ if (target?.closest?.('.command-runner-panel')) return;
5725
5777
  let session = null;
5726
5778
  if (target?.classList?.contains?.('xterm-helper-textarea')) {
5727
5779
  const sid = target.closest('.term-viewport')?.dataset?.sessionId;
@@ -6337,8 +6389,19 @@ async function launchDetached(profile, initialMessage, autoSubmit, opts = {}) {
6337
6389
  async function runCommandInNewTab({ command, cwd, label }) {
6338
6390
  if (!command) return;
6339
6391
  await _whenOpenIdle();
6340
- const session = await openSession('shell', cwd || null);
6341
- if (!session) return;
6392
+ let session = await openSession('shell', cwd || null);
6393
+ if (!session) {
6394
+ // Another open slipped in between the queue and this call (openSession
6395
+ // returns null instead of stacking): take one more turn rather than
6396
+ // dropping the command without a word.
6397
+ await _whenOpenIdle();
6398
+ session = await openSession('shell', cwd || null);
6399
+ }
6400
+ if (!session) {
6401
+ ensurePanel();
6402
+ showTermToast(`Could not open a terminal for ${label || 'this command'}. Try again`);
6403
+ return;
6404
+ }
6342
6405
  if (label) {
6343
6406
  session.label = label;
6344
6407
  renderTabBar();
@@ -6414,140 +6477,72 @@ async function attachDetached(terminalSessionId, profile, initialMessage, autoSu
6414
6477
  }
6415
6478
  }
6416
6479
 
6417
- /** Send a message to a session once the WebSocket is ready and CLI has booted.
6418
- * Watches terminal output for the CLI's input prompt (e.g. Claude Code's ">" or ❯)
6419
- * instead of using a blind timeout — prevents input from being swallowed by the
6420
- * shell (cmd.exe) before the CLI is ready. Falls back to 15s timeout.
6480
+ /** Type a message into a session once it can take it.
6481
+ * A shell tab (Command Runner) is ready when its line editor is: zsh, bash 5
6482
+ * and fish announce it (bracketed paste on), every other prompt is read off
6483
+ * the last line, and a prompt nothing recognises is taken once the output
6484
+ * goes quiet. A CLI keeps its own reading: watch for its input prompt (">" /
6485
+ * ❯) and fall back after 15 s, because a CLI swallows what is typed before it
6486
+ * is up. The rules, and the sequence wait → settle → type, are in term-ready.js.
6421
6487
  *
6422
- * KEY FIX: Also checks session._replayBuf for output that arrived BEFORE this
6423
- * function was called (e.g. replay data from reconnectSession's ws.onopen).
6424
- * Without this, the CLI prompt is missed when the PTY boots faster than the
6425
- * automation studio's browser-open + launchLoop HTTP round-trip. */
6488
+ * The wait lives on the session, not on one socket: a socket that fails
6489
+ * before it opens, or is swapped by a reconnect, changes nothing, and the
6490
+ * readiness window only starts once a socket of the session is open. Output
6491
+ * that arrived before this call is read from session._replayBuf, and the
6492
+ * terminal's own parser state covers a prompt that only came back inside a
6493
+ * snapshot. Resolves { ok, reason } once the message is typed (or could not be). */
6426
6494
  function _sendOnceReady(session, message, autoSubmit) {
6427
6495
  if (!session?.ws) {
6428
6496
  console.warn('[SynaBun] _sendOnceReady: no session.ws — aborting');
6429
- return;
6430
- }
6431
-
6432
- console.log('[SynaBun] _sendOnceReady: starting, ws.readyState =', session.ws.readyState, ', msg length =', message?.length, ', replayBuf length =', session._replayBuf?.length || 0);
6433
-
6434
- // Strip ANSI escape sequences so color codes around prompts don't block matching.
6435
- // ConPTY on Windows sends private-mode CSI like \x1b[?25h (show cursor) after prompts.
6436
- // The [\x20-\x3f]* range covers ?, !, >, = (ECMA-48 parameter bytes) plus digits/semicolons.
6437
- const ANSI_RE = /\x1b\[[\x20-\x3f]*[\x40-\x7e]|\x1b\][^\x07]*\x07|\x1b[()][AB012]/g;
6438
-
6439
- // Patterns that indicate a CLI is ready for input (matched against ANSI-stripped output)
6440
- // Claude Code: ">" at start of line or "❯", Codex/Gemini: similar prompts
6441
- const READY_PATTERNS = [
6442
- /^>\s*$/m, // Claude Code prompt: ">" on its own line
6443
- /\n>\s*$/, // ">" after newline at end of output
6444
- />\s*$/, // ">" at end of buffer (catch partial lines)
6445
- /\u276F/, // ❯ (some CLI prompts)
6446
- /\$ $/, // Shell prompt fallback
6447
- ];
6448
- const MAX_WAIT = 15000;
6449
- let _outputBuf = '';
6450
- let _sent = false;
6451
- let _listener = null;
6452
- let _fallbackTimer = null;
6453
-
6454
- function doSend() {
6455
- if (_sent) return;
6456
- _sent = true;
6457
- // Clean up watcher and timer
6458
- if (_listener) session._outputWatchers?.delete(_listener);
6459
- if (_fallbackTimer) clearTimeout(_fallbackTimer);
6460
- console.log('[SynaBun] _sendOnceReady: doSend triggered, ws.readyState =', session.ws?.readyState);
6461
- // Small delay after detecting ready — let the CLI fully settle
6462
- setTimeout(() => {
6463
- if (session.ws?.readyState !== WebSocket.OPEN) {
6464
- console.warn('[SynaBun] _sendOnceReady: WS not OPEN at send time, readyState =', session.ws?.readyState);
6465
- return;
6466
- }
6467
- // Chunk the input to avoid ConPTY input buffer overflow on Windows.
6468
- // Writing 1000+ chars in a single pty.write() can silently drop data.
6469
- const full = message + '\r';
6470
- const CHUNK = 256;
6471
- const DELAY = 30; // ms between chunks
6472
- console.log('[SynaBun] _sendOnceReady: sending prompt in chunks (' + message.length + ' chars, ' + Math.ceil(full.length / CHUNK) + ' chunks)');
6473
- for (let i = 0; i < full.length; i += CHUNK) {
6474
- const chunk = full.slice(i, i + CHUNK);
6475
- const delay = (i / CHUNK) * DELAY;
6476
- setTimeout(() => {
6477
- if (session.ws?.readyState === WebSocket.OPEN) {
6478
- session.ws.send(JSON.stringify({ type: 'input', data: chunk }));
6479
- }
6480
- }, delay);
6481
- }
6482
- if (autoSubmit) {
6483
- // Send a second Enter after all chunks + extra delay to auto-confirm Claude Code's prompt
6484
- const totalChunkTime = Math.ceil(full.length / CHUNK) * DELAY;
6485
- setTimeout(() => {
6486
- if (session.ws?.readyState === WebSocket.OPEN) {
6487
- console.log('[SynaBun] _sendOnceReady: sending auto-submit Enter');
6488
- session.ws.send(JSON.stringify({ type: 'input', data: '\r' }));
6489
- }
6490
- }, totalChunkTime + 4000);
6491
- }
6492
- }, 500);
6493
- }
6494
-
6495
- // Check if the CLI prompt already appeared in replay data that arrived
6496
- // before this function was called (the core race condition fix).
6497
- function checkReplayBuffer() {
6498
- if (session._replayBuf) {
6499
- const clean = session._replayBuf.replace(ANSI_RE, '');
6500
- if (READY_PATTERNS.some(p => p.test(clean))) {
6501
- console.log('[SynaBun] _sendOnceReady: ready pattern found in existing replay buffer — sending immediately');
6502
- doSend();
6503
- return true;
6497
+ return Promise.resolve({ ok: false, reason: 'no-socket' });
6498
+ }
6499
+ if (!message) return Promise.resolve({ ok: false, reason: 'empty' });
6500
+
6501
+ const kind = readinessKind(session.profile);
6502
+ const alive = () => !session.dead && !session._exitReceived && _sessions.includes(session);
6503
+
6504
+ return typeWhenReady({
6505
+ kind,
6506
+ text: message + '\r',
6507
+ socket: () => session.ws,
6508
+ alive,
6509
+ OPEN: WebSocket.OPEN,
6510
+ // The emulator's own state: true while the shell's line editor is reading.
6511
+ lineEditorActive: () => session.term?.modes?.bracketedPasteMode === true,
6512
+ // What already arrived (replay data lands before an attach calls this).
6513
+ buffered: session._replayBuf || '',
6514
+ subscribe: ({ output, opened, parsed }) => {
6515
+ (session._outputWatchers ||= new Set()).add(output);
6516
+ (session._openWatchers ||= new Set()).add(opened);
6517
+ // Bracketed paste can arrive inside a snapshot's escape data, where the
6518
+ // watcher only sees plain text: ask the parser after each write instead.
6519
+ let onParsed = null;
6520
+ if (kind === 'shell') {
6521
+ try { onParsed = session.term?.onWriteParsed?.(parsed) || null; } catch {}
6504
6522
  }
6523
+ return () => {
6524
+ session._outputWatchers?.delete(output);
6525
+ session._openWatchers?.delete(opened);
6526
+ try { onParsed?.dispose(); } catch {}
6527
+ };
6528
+ },
6529
+ onReady: (reason, waitedMs) => console.log(`[SynaBun] _sendOnceReady: ${kind} ready (${reason}) after ${waitedMs} ms`),
6530
+ }).then((result) => {
6531
+ if (!result.ok) {
6532
+ console.warn(`[SynaBun] _sendOnceReady: not delivered (${result.reason}, ${result.sent}/${result.chunks} chunks)`);
6533
+ if (alive()) showTermToast('The command did not reach the terminal. Run it again');
6534
+ return result;
6535
+ }
6536
+ if (autoSubmit) {
6537
+ // A second Enter confirms Claude Code's prompt once the paste settled
6538
+ setTimeout(() => {
6539
+ if (session.ws?.readyState === WebSocket.OPEN) {
6540
+ session.ws.send(JSON.stringify({ type: 'input', data: '\r' }));
6541
+ }
6542
+ }, 4000);
6505
6543
  }
6506
- return false;
6507
- }
6508
-
6509
- function onOutput(text) {
6510
- _outputBuf += text;
6511
- // Strip ANSI codes before matching — CLI prompts are colorized
6512
- const clean = _outputBuf.replace(ANSI_RE, '');
6513
- if (READY_PATTERNS.some(p => p.test(clean))) {
6514
- console.log('[SynaBun] _sendOnceReady: ready pattern matched in live output');
6515
- doSend();
6516
- }
6517
- }
6518
-
6519
- function attach() {
6520
- // Before watching future output, check what already arrived.
6521
- // The replay from ws.onopen fires synchronously before this function runs
6522
- // (reconnectSession awaits, replay lands in ws.onmessage, accumulates
6523
- // in session._replayBuf). If the CLI prompt is already there, send now.
6524
- if (checkReplayBuffer()) return;
6525
-
6526
- console.log('[SynaBun] _sendOnceReady: attach() — adding output watcher (no prompt in replay buffer yet)');
6527
- // Session-level output watcher (fed by _handleTermWsEvent) — unlike a raw
6528
- // ws 'message' listener, this survives _reconnectTerminalWs swapping session.ws.
6529
- _listener = onOutput;
6530
- (session._outputWatchers ||= new Set()).add(_listener);
6531
- // Fallback: if no prompt detected within MAX_WAIT, send anyway
6532
- _fallbackTimer = setTimeout(() => {
6533
- console.warn('[SynaBun] _sendOnceReady: CLI prompt not detected, sending after timeout');
6534
- doSend();
6535
- }, MAX_WAIT);
6536
- }
6537
-
6538
- if (session.ws.readyState === WebSocket.OPEN) attach();
6539
- else {
6540
- session.ws.addEventListener('open', attach, { once: true });
6541
- // Safety net: if the WS never opens, force-send after MAX_WAIT to avoid silent failure
6542
- setTimeout(() => {
6543
- if (!_sent) {
6544
- // One last check of the replay buffer before force-sending
6545
- if (checkReplayBuffer()) return;
6546
- console.warn('[SynaBun] _sendOnceReady: WS never opened — forcing send attempt');
6547
- doSend();
6548
- }
6549
- }, MAX_WAIT + 2000);
6550
- }
6544
+ return result;
6545
+ });
6551
6546
  }
6552
6547
 
6553
6548
  /** Snapshot terminal state for workspace save */
@@ -6566,6 +6561,7 @@ export function getTerminalSnapshot() {
6566
6561
  isDetached: _detachedTabs.has(s.id),
6567
6562
  userRenamed: s._userRenamed || false,
6568
6563
  claudeSessionId: s._claudeSessionId || null,
6564
+ floatColor: s._floatColor || null,
6569
6565
  assistantSessionId: s._isAssistant ? s.id : null,
6570
6566
  brain: s._isAssistant ? (s._brain || null) : null,
6571
6567
  })),
@@ -6622,12 +6618,20 @@ function applyTerminalLayout(snap) {
6622
6618
  }
6623
6619
  if (!session) continue;
6624
6620
 
6621
+ // The snapshot's color goes onto the session before detachTab reads it:
6622
+ // a session a workspace restore rebuilt has none and would get a random one
6623
+ const savedColor = FLOAT_COLORS.some(c => c.id === dt.floatColor) ? dt.floatColor : null;
6624
+ if (savedColor) session._floatColor = savedColor;
6625
+
6625
6626
  const idx = _sessions.indexOf(session);
6626
6627
  if (!_detachedTabs.has(session.id)) {
6627
6628
  detachTab(idx);
6628
6629
  }
6629
6630
  const tabState = _detachedTabs.get(session.id);
6630
6631
  if (tabState) {
6632
+ // Already floating (browser tabs detach while reconnecting): recolor it
6633
+ if (savedColor) _applyFloatColor(tabState.el, savedColor);
6634
+
6631
6635
  // Validate saved dimensions — enforce minimums and keep on screen
6632
6636
  const minW = 280, minH = 160;
6633
6637
  const w = Math.max(minW, dt.width || minW);
@@ -6745,7 +6749,7 @@ export async function restoreTerminalSnapshot(snap) {
6745
6749
  }
6746
6750
  if (liveIds.has(saved.id)) {
6747
6751
  const live = liveMap.get(saved.id);
6748
- const opts = { label: saved.label, pinned: saved.pinned, cwd: live?.cwd || null, userRenamed: saved.userRenamed, claudeSessionId: saved.claudeSessionId };
6752
+ const opts = { label: saved.label, pinned: saved.pinned, cwd: live?.cwd || null, userRenamed: saved.userRenamed, claudeSessionId: saved.claudeSessionId, floatColor: saved.floatColor };
6749
6753
  if (CLI_PROFILES.has(saved.profile)) {
6750
6754
  await reconnectSession(saved.id, saved.profile, opts);
6751
6755
  } else {
@@ -0,0 +1,130 @@
1
+ // ═══════════════════════════════════════════
2
+ // SynaBun Neural Interface — the public address: the page side
3
+ // ═══════════════════════════════════════════
4
+ //
5
+ // What the two cards that start the Cloudflare tunnel share (the Share menu's
6
+ // Connection block in ui-invite.js, Settings → Automations → Online access in
7
+ // ui-settings.js): asking the server, starting and stopping, asking again
8
+ // while something is on its way (a download, the wait for an address), and
9
+ // the reading of each answer in the page's language. What an answer means is
10
+ // tunnel-view.js; how it is drawn is each card's own.
11
+ //
12
+ // The requests are sent with fetch as they are: same origin, no body. The
13
+ // server takes a start or a stop from the app's own pages only
14
+ // (lib/http-guards.js ownerRouteGuard), and a refusal is shown in the card
15
+ // like any other failure, not as the app-wide "forbidden" notice.
16
+
17
+ import { getLocale, t } from './i18n.js';
18
+ import { tunnelView } from './tunnel-view.js';
19
+
20
+ const POLL_MS = 1000;
21
+ /** How long a card keeps asking by itself. A download or a start that takes longer is shown again on the next look. */
22
+ const POLL_LIMIT_MS = 20 * 60 * 1000;
23
+
24
+ const translate = (key, params) => t(`tunnel.${key}`, params);
25
+ function number(value, digits) {
26
+ try { return value.toLocaleString(getLocale(), { minimumFractionDigits: digits, maximumFractionDigits: digits }); } catch { return value.toFixed(digits); }
27
+ }
28
+
29
+ /** tunnel-view.js tunnelView() in the page's language. */
30
+ export function readTunnel(status) {
31
+ return tunnelView(status, { t: translate, number });
32
+ }
33
+
34
+ /** The sentence a card shows where it offers the remote MCP address and no access key exists. */
35
+ export function mcpKeyNeededText() {
36
+ return translate('mcpKeyNeeded');
37
+ }
38
+
39
+ async function ask(path, method = 'GET') {
40
+ const res = await fetch(path, { method });
41
+ let body = null;
42
+ try { body = await res.json(); } catch { body = null; }
43
+ return { status: res.status, ok: res.ok, body };
44
+ }
45
+
46
+ /**
47
+ * One card's hold on the tunnel.
48
+ *
49
+ * @param {object} [o]
50
+ * @param {(view: object, status: object | null) => void} [o.onChange] called with every new reading
51
+ * @param {() => boolean} [o.alive] false once the card is gone: the asking-again stops
52
+ * @returns {{ view: () => object, status: () => (object | null), show: (status: object) => object,
53
+ * refresh: () => Promise<object>, start: () => Promise<object>, stop: () => Promise<object>, dispose: () => void }}
54
+ */
55
+ export function createTunnelCard({ onChange = () => {}, alive = () => true } = {}) {
56
+ let status = null;
57
+ let timer = null;
58
+ let since = 0;
59
+ let disposed = false;
60
+
61
+ function schedule(view) {
62
+ clearTimeout(timer);
63
+ timer = null;
64
+ if (disposed || !view.poll || !alive()) { since = 0; return; }
65
+ if (!since) since = Date.now();
66
+ if (Date.now() - since > POLL_LIMIT_MS) return;
67
+ timer = setTimeout(refresh, POLL_MS);
68
+ }
69
+
70
+ /** Take an answer as the state of things, tell the card, and ask again later when it will change by itself. */
71
+ function show(next) {
72
+ status = next && typeof next === 'object' ? next : null;
73
+ const view = readTunnel(status);
74
+ if (!disposed) { try { onChange(view, status); } catch (error) { console.warn('[tunnel] The card could not be drawn:', error); } }
75
+ schedule(view);
76
+ return view;
77
+ }
78
+
79
+ async function refresh() {
80
+ if (disposed) return readTunnel(status);
81
+ try {
82
+ const got = await ask('/api/tunnel/status');
83
+ if (got.ok && got.body) return show(got.body);
84
+ } catch { /* the server is away for a moment: what is shown stays, and the next look asks again */ }
85
+ const view = readTunnel(status);
86
+ schedule(view);
87
+ return view;
88
+ }
89
+
90
+ /** A start or a stop that the server did not take: shown in the card, with what the card knew before. */
91
+ function refused(got, error) {
92
+ // A server from before the tunnel program came with SynaBun answers a start with 400 when no cloudflared is
93
+ // installed, and its words tell the user to install one. What is missing is a restart: the card says that.
94
+ if (got?.status === 400 && !status?.cloudflared) return show({ ok: true, available: false, running: false, starting: false, url: null });
95
+ const failure = got?.status === 403
96
+ ? { code: 'refused', message: got.body?.error || '' }
97
+ : { code: 'unknown', message: got?.body?.error || error?.message || '' };
98
+ return show({ ...(status || {}), ok: true, running: false, starting: false, url: null, state: 'failed', error: failure, cloudflared: status?.cloudflared || { state: 'missing' } });
99
+ }
100
+
101
+ async function send(path) {
102
+ let got = null;
103
+ try { got = await ask(path, 'POST'); } catch (error) { return refused(null, error); }
104
+ if (!got.ok || got.body?.ok === false) return refused(got);
105
+ return refresh();
106
+ }
107
+
108
+ return {
109
+ view: () => readTunnel(status),
110
+ status: () => status,
111
+ show,
112
+ refresh,
113
+ start: () => send('/api/tunnel/start'),
114
+ stop: () => send('/api/tunnel/stop'),
115
+ dispose() { disposed = true; clearTimeout(timer); timer = null; },
116
+ };
117
+ }
118
+
119
+ /**
120
+ * Put new markup into a card's block without disturbing the person using it: nothing happens when the markup is
121
+ * what is there already, and a button that had the keyboard's focus has it again afterwards (by its id).
122
+ */
123
+ export function drawInto(container, html) {
124
+ if (!container || container.__tunnelHtml === html) return false;
125
+ const focused = container.contains(document.activeElement) ? document.activeElement?.id : '';
126
+ container.innerHTML = html;
127
+ container.__tunnelHtml = html;
128
+ if (focused) { try { container.querySelector(`#${CSS.escape(focused)}`)?.focus({ preventScroll: true }); } catch { /* the button is gone with its state */ } }
129
+ return true;
130
+ }
@@ -82,11 +82,15 @@ const ASSISTANT_BUTTON = ['#topright-assistant-panel-btn', 'closed'];
82
82
  const ACTIVE_TAB = '#assistant-panel.open .asp-viewport.active';
83
83
  const ACTIVE_BAR = '#assistant-panel.open .asp-tab-bar.active';
84
84
 
85
+ // With the workspace tools hidden (the toolbar's toggle) their controls are off screen, and the step outlines the
86
+ // toggle that shows them.
87
+ const TOOLS_TOGGLE = ['#topright-tools-btn'];
88
+
85
89
  export const FEATURE_STEPS = [
86
- { id: 'explain-memory-explorer', chapter: 'memory', targets: [['#topright-memory-explorer-btn']] },
90
+ { id: 'explain-memory-explorer', chapter: 'memory', targets: [['#topright-memory-explorer-btn'], TOOLS_TOGGLE] },
87
91
  { id: 'explain-graph', chapter: 'memory', copy: { '2d': 'body2d' }, targets: [['[data-menu="graph"] > .menubar-label']] },
88
92
  { id: 'explain-focus', chapter: 'workspace', targets: [['#wb-toolbar'], ['#titlebar-viz-toggle', 'graph']] },
89
- { id: 'explain-workspaces', chapter: 'workspace', targets: [['#ws-indicator']] },
93
+ { id: 'explain-workspaces', chapter: 'workspace', targets: [['#ws-indicator'], TOOLS_TOGGLE] },
90
94
  { id: 'explain-terminals', chapter: 'tools', menu: 'apps', targets: [['#menu-terminal-claude', 'body', '#menu-terminal-opencode']] },
91
95
  { id: 'explain-browser', chapter: 'tools', menu: 'apps', targets: [['#menu-terminal-browser']] },
92
96
  { id: 'explain-side-panel', chapter: 'tools', targets: [['#topright-claude-panel-btn', 'body', '#topright-opencode-panel-btn']] },