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
@@ -1,17 +1,52 @@
1
1
  // ═══════════════════════════════════════════
2
2
  // SynaBun Neural Interface — Loading Overlay
3
- // Health check, server start, server-offline retry, command copy
3
+ // Health check, server start, server-offline retry, command copy,
4
+ // and the restart the user asked for (the same screen, one more reason)
4
5
  // ═══════════════════════════════════════════
5
6
 
6
7
  import { fetchHealth, startHealth } from './api.js';
7
- import { emit } from './state.js';
8
+ import { emit, on } from './state.js';
8
9
  import { t } from './i18n.js';
10
+ import {
11
+ START_LIMITS, START_STORAGE, createStartBridge, fetchStartBeacon, isMacPlatform, isWindowsPlatform,
12
+ manualStartCommands, openStartLink, startBeaconTarget, startButtonUsable, startStatusView,
13
+ } from './start-bridge.js';
14
+ // Imported with the app, not when it is needed: once the server is down
15
+ // nothing more can be fetched, and that is exactly when the overlay wants it.
16
+ import { createMascot } from './synabun-mascot.js';
9
17
 
10
18
  const $ = (id) => document.getElementById(id);
11
19
 
12
20
  // ── Internal refs (resolved once on init) ──
13
21
  let _statusDot = null;
14
22
  let _initCallback = null;
23
+ // The server-offline state: watches for the server and owns the Start button.
24
+ let _offlineBridge = null;
25
+ // The title the overlay was given for "offline" (a start replaces it while it runs).
26
+ let _offlineTitle = '';
27
+ // The mascot rig in the overlay, mounted the first time the server is offline.
28
+ let _mascot = null;
29
+ // The overlay was put over the running app for a restart. Everything the page
30
+ // held (sockets, terminals, panel sessions) was the old server's, so once the
31
+ // server is back the page loads again instead of carrying on.
32
+ let _restartShown = false;
33
+ // The tab's title from before the restart, for when it is refused.
34
+ let _restartTitle = null;
35
+
36
+ // What the mascot does for each thing the page knows about a start
37
+ // (startStatusView's `mascot`), as poses of the shared rig.
38
+ const MASCOT_POSE = {
39
+ asleep: 'sleep',
40
+ waking: 'wait',
41
+ working: 'think',
42
+ ready: 'success',
43
+ failed: 'error',
44
+ stalled: 'offline',
45
+ };
46
+
47
+ function stored(key) {
48
+ try { return localStorage.getItem(key); } catch { return null; }
49
+ }
15
50
 
16
51
  // ═══════════════════════════════════════════
17
52
  // PUBLIC API
@@ -38,18 +73,32 @@ export function showLoadingError(title, sub, canStart, serverOffline) {
38
73
  const $sub = $('loading-sub');
39
74
  if ($text) $text.textContent = title;
40
75
  if ($sub) $sub.textContent = sub;
76
+ if (serverOffline) _offlineTitle = title;
41
77
 
78
+ // Offline, the button asks the OS to start the server (synabun://start); it
79
+ // is left out only when that launcher is known not to be registered.
80
+ const offlineStart = !!serverOffline && startButtonUsable(stored(START_STORAGE.launcher));
42
81
  const $action = $('loading-action');
43
- if (canStart) {
82
+ const $btn = $('loading-action-btn');
83
+ if (canStart || offlineStart) {
44
84
  if ($action) $action.style.display = 'block';
85
+ if ($btn) $btn.disabled = false;
45
86
  const $label = $('loading-action-label');
46
- if ($label) $label.textContent = t('common.start');
87
+ if ($label) $label.textContent = serverOffline ? t('loading.startServer') : t('common.start');
47
88
  } else {
48
89
  if ($action) $action.style.display = 'none';
49
90
  }
50
91
 
51
- const $status = $('loading-action-status');
52
- if ($status) $status.textContent = '';
92
+ clearStartStatus();
93
+ clearRestartLook();
94
+
95
+ if (serverOffline) {
96
+ const $hint = $('loading-server-cmd-hint');
97
+ if ($hint) $hint.textContent = offlineStart ? t('loading.runCommandOr') : t('loading.runCommandSetup');
98
+ watchForServer();
99
+ } else {
100
+ stopWatchingForServer();
101
+ }
53
102
  }
54
103
 
55
104
  /**
@@ -78,6 +127,7 @@ function resetLoadingToConnecting() {
78
127
  if (!$overlay) return;
79
128
 
80
129
  $overlay.classList.remove('error', 'server-offline');
130
+ stopWatchingForServer();
81
131
 
82
132
  const $text = $('loading-text');
83
133
  const $sub = $('loading-sub');
@@ -88,6 +138,8 @@ function resetLoadingToConnecting() {
88
138
  if ($sub) $sub.textContent = t('loading.initializingNeural');
89
139
  if ($action) $action.style.display = 'none';
90
140
  if ($actionStatus) $actionStatus.textContent = '';
141
+ clearStartStatus();
142
+ clearRestartLook();
91
143
  if (_statusDot) _statusDot.classList.remove('error');
92
144
  }
93
145
 
@@ -95,6 +147,24 @@ function resetLoadingToConnecting() {
95
147
  // HEALTH CHECK (called during init)
96
148
  // ═══════════════════════════════════════════
97
149
 
150
+ /**
151
+ * Keep what a health answer says about this install for the moment the server
152
+ * is down and cannot be asked: where it lives, how it was installed, and
153
+ * whether the Start Server launcher is registered.
154
+ */
155
+ export function rememberHealth(health) {
156
+ if (!health) return;
157
+ try {
158
+ if (health.projectDir) {
159
+ localStorage.setItem('synabun-project-dir', health.projectDir);
160
+ }
161
+ if (health.install) localStorage.setItem(START_STORAGE.install, health.install);
162
+ if (health.startLauncher) localStorage.setItem(START_STORAGE.launcher, health.startLauncher);
163
+ if (health.entry) localStorage.setItem(START_STORAGE.entry, health.entry);
164
+ } catch {}
165
+ if (health.projectDir) updateCmdText(health.projectDir);
166
+ }
167
+
98
168
  /**
99
169
  * Run the health check pre-flight. If the database is unhealthy,
100
170
  * show the appropriate loading error. Returns true if healthy (or
@@ -104,9 +174,7 @@ function resetLoadingToConnecting() {
104
174
  export async function checkHealth() {
105
175
  try {
106
176
  const health = await fetchHealth();
107
- if (health.projectDir) {
108
- localStorage.setItem('synabun-project-dir', health.projectDir);
109
- }
177
+ rememberHealth(health);
110
178
  if (!health.ok) {
111
179
  const messages = {
112
180
  db_missing: [t('loading.health.databaseUnreachable.title'), health.detail || t('loading.health.databaseUnreachable.sub')],
@@ -132,7 +200,286 @@ export async function checkHealth() {
132
200
  */
133
201
  function updateCmdText(projectDir) {
134
202
  const cmdEl = $('loading-cmd-text');
135
- if (cmdEl) cmdEl.textContent = `cd "${projectDir}" ; npm start`;
203
+ if (!cmdEl) return;
204
+ const commands = manualStartCommands({
205
+ projectDir,
206
+ install: stored(START_STORAGE.install),
207
+ entry: stored(START_STORAGE.entry),
208
+ windows: isWindowsPlatform(navigator),
209
+ });
210
+ // One box: the command for this install (the checkout's when it is not known).
211
+ cmdEl.textContent = commands[commands.length - 1].command;
212
+ }
213
+
214
+ // ═══════════════════════════════════════════
215
+ // INTERNAL — Server offline: Start Server + reconnect
216
+ // ═══════════════════════════════════════════
217
+
218
+ function stopWatchingForServer() {
219
+ if (_offlineBridge) _offlineBridge.stop();
220
+ _offlineBridge = null;
221
+ poseMascot('asleep');
222
+ }
223
+
224
+ /** The lines a start writes under the button, emptied. */
225
+ function clearStartStatus() {
226
+ for (const id of ['loading-action-status', 'loading-action-elapsed', 'loading-action-hint', 'loading-action-log']) {
227
+ const el = $(id);
228
+ if (el) el.textContent = '';
229
+ }
230
+ const $trail = $('loading-action-trail');
231
+ if ($trail) $trail.replaceChildren();
232
+ }
233
+
234
+ /** What a restart changed about the overlay's look, undone. */
235
+ function clearRestartLook() {
236
+ const $overlay = $('loading-overlay');
237
+ if ($overlay) $overlay.classList.remove('over-app', 'restart-busy', 'restart-refused');
238
+ const btn = $('loading-action-btn');
239
+ if (btn) btn.style.display = '';
240
+ }
241
+
242
+ /**
243
+ * The overlay's mascot as the shared rig, so a start can be acted out. The
244
+ * markup's own still drawing stays as the fallback when the rig cannot mount.
245
+ */
246
+ function mountMascot() {
247
+ if (_mascot) return;
248
+ const host = $('loading-mascot');
249
+ if (!host) return;
250
+ try {
251
+ const still = host.querySelector('svg');
252
+ _mascot = createMascot(host, { width: 160, height: 80, pose: 'sleep', active: false });
253
+ if (still && still !== _mascot.el) still.style.display = 'none';
254
+ } catch {
255
+ _mascot = null;
256
+ }
257
+ }
258
+
259
+ /** 'asleep' is the still sleeping face (the overlay's own bob and z carry it); every other state plays its pose. */
260
+ function poseMascot(state) {
261
+ const host = $('loading-mascot');
262
+ if (host) host.setAttribute('data-state', state);
263
+ if (!_mascot) return;
264
+ try {
265
+ _mascot.setPose(MASCOT_POSE[state] || 'sleep');
266
+ _mascot.setActive(state !== 'asleep');
267
+ } catch {}
268
+ }
269
+
270
+ /**
271
+ * Draw one snapshot of the start: the sentence (a live region, written only
272
+ * when it changes), the page's own clock, and the steps the launcher reported
273
+ * with the time each one happened. Nothing here is estimated.
274
+ */
275
+ function renderStart(snap) {
276
+ const platform = isWindowsPlatform(navigator) ? 'windows' : isMacPlatform(navigator) ? 'mac' : 'linux';
277
+ const view = startStatusView(snap, { platform });
278
+ const say = (line) => (line ? t(`loading.start.${line.key}`, line.params) : '');
279
+ // A restart this page asked for or was told of, while it runs.
280
+ const restarting = !!snap.restart && (view.busy || view.state === 'online');
281
+
282
+ poseMascot(view.mascot);
283
+
284
+ const $overlay = $('loading-overlay');
285
+ if ($overlay) {
286
+ // While a restart runs there is nothing to press and no command to type;
287
+ // refused, the server is still there and only the way back is offered.
288
+ $overlay.classList.toggle('restart-busy', restarting);
289
+ $overlay.classList.toggle('restart-refused', view.state === 'refused');
290
+ }
291
+
292
+ const $text = $('loading-text');
293
+ if ($text) {
294
+ const title = view.heading ? t(`loading.start.${view.heading}`)
295
+ : view.mascot === 'waking' || view.mascot === 'working' ? t('loading.start.headingStarting')
296
+ : view.mascot === 'ready' ? t('loading.start.headingReady')
297
+ : view.mascot === 'failed' ? t('loading.start.headingFailed')
298
+ : _offlineTitle || t('loading.serverOffline');
299
+ if ($text.textContent !== title) $text.textContent = title;
300
+ }
301
+
302
+ // Under the title: whose restart this is, for as long as it is one.
303
+ const $sub = $('loading-sub');
304
+ if ($sub && _restartShown) {
305
+ const sub = view.state === 'refused' ? t('loading.restartSubRefused')
306
+ : restarting ? (snap.restart.asked ? t('loading.restartSubAsked') : t('loading.restartSubTold'))
307
+ : t('loading.serverNotRunning');
308
+ if ($sub.textContent !== sub) $sub.textContent = sub;
309
+ }
310
+
311
+ const btn = $('loading-action-btn');
312
+ const $label = $('loading-action-label');
313
+ if (btn) {
314
+ btn.disabled = view.busy || view.state === 'online';
315
+ // No dead button: when a restart did not bring the server back, Start is
316
+ // offered only if its launcher is there (the command below always is).
317
+ const startOffered = view.button === 'start' || view.button === 'retry';
318
+ btn.style.display = _restartShown && startOffered && !startButtonUsable(stored(START_STORAGE.launcher)) ? 'none' : '';
319
+ }
320
+ if ($label) {
321
+ $label.textContent = view.button === 'starting' ? t('loading.starting')
322
+ : view.button === 'retry' ? t('common.retry')
323
+ : view.button === 'back' ? t('loading.restartBack') : t('loading.startServer');
324
+ }
325
+
326
+ const $status = $('loading-action-status');
327
+ const line = say(view.headline);
328
+ if ($status && $status.textContent !== line) $status.textContent = line;
329
+
330
+ const $elapsed = $('loading-action-elapsed');
331
+ if ($elapsed) {
332
+ $elapsed.textContent = view.elapsed;
333
+ $elapsed.setAttribute('aria-label', view.elapsed ? `${t(`loading.start.${view.elapsedLabel}`)}: ${view.elapsed}` : '');
334
+ }
335
+
336
+ const $hint = $('loading-action-hint');
337
+ if ($hint) $hint.textContent = say(view.hint);
338
+
339
+ const $trail = $('loading-action-trail');
340
+ if ($trail) {
341
+ $trail.replaceChildren(...view.trail.map((row) => {
342
+ const item = document.createElement('li');
343
+ const what = document.createElement('span');
344
+ what.textContent = t(`loading.start.${row.key}`);
345
+ const when = document.createElement('span');
346
+ when.className = 'at';
347
+ when.textContent = row.time;
348
+ item.append(what, when);
349
+ return item;
350
+ }));
351
+ }
352
+
353
+ const $log = $('loading-action-log');
354
+ if ($log) $log.textContent = view.error?.log ? t('loading.start.failedLog', { path: view.error.log }) : '';
355
+ }
356
+
357
+ /**
358
+ * While the overlay says the server is offline: keep asking for it, and let
359
+ * the Start button hand the OS a synabun://start link. Reconnects by itself
360
+ * the moment the server answers, however it was started. While a start is
361
+ * under way it shows what the launcher itself reports (its beacon), never a
362
+ * guess: see start-bridge.js.
363
+ */
364
+ function watchForServer() {
365
+ if (_offlineBridge) return;
366
+ openBridge().watch();
367
+ }
368
+
369
+ /**
370
+ * Is the server answering, which server is it (`bootId`), and does it say it
371
+ * is on its way out for a restart. One that predates those two says neither.
372
+ */
373
+ async function probeServer() {
374
+ const health = await fetchHealth();
375
+ return {
376
+ up: !!health && health.ok !== false,
377
+ boot: typeof health?.bootId === 'string' ? health.bootId : '',
378
+ stopping: health?.restarting === true,
379
+ };
380
+ }
381
+
382
+ /** The bridge behind the overlay, for either reason it is up: the server is offline, or it is being restarted. */
383
+ function openBridge() {
384
+ mountMascot();
385
+ const beaconAt = startBeaconTarget(window.location);
386
+ const bridge = createStartBridge({
387
+ probe: probeServer,
388
+ launch: () => openStartLink({ document, location: window.location, userAgent: navigator.userAgent }),
389
+ beacon: beaconAt ? () => fetchStartBeacon(beaconAt) : null,
390
+ port: beaconAt ? beaconAt.port : 0,
391
+ onUpdate(snap) {
392
+ if (_offlineBridge !== bridge) return;
393
+ renderStart(snap);
394
+ },
395
+ onState(state) {
396
+ if (_offlineBridge !== bridge) return;
397
+ if (state !== 'online') return;
398
+ // Back from a restart: the page loads again, after the frame that says so.
399
+ if (_restartShown) {
400
+ setTimeout(() => window.location.reload(), START_LIMITS.reloadDelayMs);
401
+ return;
402
+ }
403
+ resetLoadingToConnecting();
404
+ emit('loading:retried');
405
+ if (_initCallback) _initCallback();
406
+ },
407
+ });
408
+ _offlineBridge = bridge;
409
+ return bridge;
410
+ }
411
+
412
+ // ═══════════════════════════════════════════
413
+ // Restart Server
414
+ // ═══════════════════════════════════════════
415
+
416
+ /**
417
+ * The user chose Restart Server. The screen changes here, in the click: the
418
+ * overlay is up and says a restart was asked for before the request has even
419
+ * left, and nothing on the way to that first frame waits for the network.
420
+ * From there it shows what is really known (start-bridge.js `restart()`): the
421
+ * server's answer, its own word that it is shutting down, the probe that finds
422
+ * it gone, the supervisor's report of bringing it back. The page loads again
423
+ * by itself when another server than the one that was asked answers.
424
+ */
425
+ export function requestServerRestart() {
426
+ return beginRestart({ request: postRestart });
427
+ }
428
+
429
+ /** Ask for the restart: { ok, boot } when the server says it will, { ok: false, status, message } when it will not. */
430
+ async function postRestart() {
431
+ const res = await fetch('/api/server/restart', { method: 'POST', signal: AbortSignal.timeout(10000) });
432
+ let body = null;
433
+ try { body = await res.json(); } catch {}
434
+ if (!res.ok || !body || body.ok === false) {
435
+ return { ok: false, status: res.status, message: String(body?.error || body?.message || '') };
436
+ }
437
+ return { ok: true, boot: typeof body.bootId === 'string' ? body.bootId : '' };
438
+ }
439
+
440
+ /**
441
+ * Show the restart: this window asked for it (`request`), or the server told
442
+ * every window that it is restarting (`announced`, over the sync socket).
443
+ */
444
+ function beginRestart({ request = null, announced = false, boot = '' } = {}) {
445
+ // Already on screen (this window asked, and here is the server saying so too).
446
+ if (_restartShown && _offlineBridge) {
447
+ _offlineBridge.restart({ announced, boot });
448
+ return false;
449
+ }
450
+ // The overlay is up because the server is offline: there is nothing to restart.
451
+ if (_offlineBridge) return false;
452
+ const $overlay = $('loading-overlay');
453
+ if (!$overlay) return false;
454
+
455
+ _restartShown = true;
456
+ _restartTitle = document.title;
457
+ document.title = t('loading.start.headingRestarting');
458
+ _offlineTitle = t('loading.serverOffline');
459
+ // `over-app`: no fade-in and above whatever is open. The screen is there in the frame of the click.
460
+ $overlay.classList.add('over-app', 'error', 'server-offline');
461
+ $overlay.classList.remove('hidden');
462
+ // Nothing behind the screen keeps the keyboard (a terminal, an editor).
463
+ try { document.activeElement?.blur?.(); } catch {}
464
+ if (_statusDot) _statusDot.classList.add('error');
465
+ const $action = $('loading-action');
466
+ if ($action) $action.style.display = 'block';
467
+ const $hint = $('loading-server-cmd-hint');
468
+ if ($hint) $hint.textContent = startButtonUsable(stored(START_STORAGE.launcher)) ? t('loading.runCommandOr') : t('loading.runCommandSetup');
469
+ const $retry = $('loading-retry-status');
470
+ if ($retry) $retry.textContent = '';
471
+ clearStartStatus();
472
+ return openBridge().restart({ request, announced, boot });
473
+ }
474
+
475
+ /** The restart did not happen and the server is still there: give the app back. */
476
+ function dismissRestart() {
477
+ resetLoadingToConnecting();
478
+ _restartShown = false;
479
+ if (_restartTitle !== null) document.title = _restartTitle;
480
+ _restartTitle = null;
481
+ const $overlay = $('loading-overlay');
482
+ if ($overlay) $overlay.classList.add('hidden');
136
483
  }
137
484
 
138
485
  // ═══════════════════════════════════════════
@@ -144,6 +491,11 @@ function updateCmdText(projectDir) {
144
491
  * Calls /api/health/start and re-triggers init on success.
145
492
  */
146
493
  async function handleStartAction() {
494
+ // A restart the server refused: the button is the way back to the app.
495
+ if (_offlineBridge && _offlineBridge.state === 'refused') { dismissRestart(); return; }
496
+ // Server offline: nothing to call, the OS starts it.
497
+ if (_offlineBridge) { _offlineBridge.start(); return; }
498
+
147
499
  const btn = $('loading-action-btn');
148
500
  const $status = $('loading-action-status');
149
501
  const $label = $('loading-action-label');
@@ -165,8 +517,6 @@ async function handleStartAction() {
165
517
  if ($label) $label.textContent = t('common.retry');
166
518
  }
167
519
  } catch (err) {
168
- // Server is unreachable — try protocol handler as fallback
169
- window.location.href = 'synabun://start';
170
520
  if ($status) $status.textContent = t('loading.somethingWrong');
171
521
  if (btn) btn.disabled = false;
172
522
  if ($label) $label.textContent = t('common.retry');
@@ -205,6 +555,8 @@ async function handleRetryConnection() {
205
555
  // fetchHealth uses AbortSignal.timeout(3000) internally
206
556
  if (health && health.ok !== false) {
207
557
  if ($status) $status.textContent = t('loading.connectedLoading');
558
+ // After a restart the page loads again (see openBridge).
559
+ if (_restartShown) { window.location.reload(); return; }
208
560
  resetLoadingToConnecting();
209
561
  emit('loading:retried');
210
562
  if (_initCallback) _initCallback();
@@ -252,4 +604,7 @@ export function initLoading({ onInit } = {}) {
252
604
  // ── Retry connection ──
253
605
  const retryBtn = $('loading-retry-btn');
254
606
  if (retryBtn) retryBtn.addEventListener('click', handleRetryConnection);
607
+
608
+ // ── The server says it is restarting (ui-sync.js): every window shows it ──
609
+ on('server:restarting', (msg) => beginRestart({ announced: true, boot: typeof msg?.bootId === 'string' ? msg.bootId : '' }));
255
610
  }
@@ -9,11 +9,13 @@ import { storage } from './storage.js';
9
9
  import { KEYS } from './constants.js';
10
10
  import { getMenuItems } from './registry.js';
11
11
  import { openHelp } from './ui-help.js';
12
+ import { armPwaInstallHandoff, initPwaInstall, installGuideUrl, isStandalone, requestPwaInstall } from './pwa-install.js';
12
13
  import { registerAction } from './ui-keybinds.js';
13
14
  import { isGuest, hasPermission } from './ui-sync.js';
14
15
  import { sendToPanel } from './ui-claude-panel.js';
15
16
  import { forceCheckToolUpdates, showUpdateToast, TOOL_LABELS } from './ui-tool-updates.js';
16
17
  import { forceCheckSynabunUpdate, openSynabunUpdateModal } from './ui-update.js';
18
+ import { requestServerRestart } from './ui-loading.js';
17
19
 
18
20
  const $ = (id) => document.getElementById(id);
19
21
 
@@ -83,7 +85,7 @@ export function initMenubar() {
83
85
  const dropdown = document.querySelector('.menubar-item.open .menubar-dropdown');
84
86
  if (!dropdown) return;
85
87
 
86
- const allItems = [...dropdown.querySelectorAll('.menu-item:not(.disabled)')];
88
+ const allItems = [...dropdown.querySelectorAll('.menu-item:not(.disabled):not([hidden])')];
87
89
  if (allItems.length === 0) return;
88
90
 
89
91
  const focused = dropdown.querySelector('.menu-item.focused');
@@ -368,6 +370,7 @@ function wireAutomationsMenu() {
368
370
  // ── Apps Menu (formerly Terminal) ──
369
371
 
370
372
  function wireTerminalMenu() {
373
+ let disarmInstallHandoff = () => {};
371
374
  const items = {
372
375
  'menu-terminal-assistant': () => emit('assistant:open'),
373
376
  'menu-terminal-claude': () => emit('terminal:open', { profile: 'claude-code' }),
@@ -413,45 +416,15 @@ function wireTerminalMenu() {
413
416
  'menu-command-runner': () => emit('command-runner:open'),
414
417
  'menu-terminal-link': () => emit('link:toggle'),
415
418
  'menu-terminal-toggle': () => emit('terminal:toggle'),
416
- 'menu-install-app': async () => {
417
- if (window._pwaInstallPrompt) {
418
- window._pwaInstallPrompt.prompt();
419
- const { outcome } = await window._pwaInstallPrompt.userChoice;
420
- if (outcome === 'accepted') {
421
- window._pwaInstallPrompt = null;
422
- const el = $('menu-install-app');
423
- if (el) el.style.display = 'none';
424
- }
425
- }
426
- },
427
- 'menu-restart-server': async () => {
428
- try {
429
- // Cache project path before server goes down
430
- try {
431
- const h = await fetch('/api/health');
432
- const hd = await h.json();
433
- if (hd.projectDir) localStorage.setItem('synabun-project-dir', hd.projectDir);
434
- } catch {}
435
- document.title = 'Restarting...';
436
- await fetch('/api/server/restart', { method: 'POST' });
437
-
438
- // Poll /api/health until the server has gone down AND come back up.
439
- // The everDown flag prevents premature reload during the brief window
440
- // where the old process is still serving requests during shutdown grace.
441
- const deadline = Date.now() + 15000;
442
- let everDown = false;
443
- while (Date.now() < deadline) {
444
- await new Promise(r => setTimeout(r, 250));
445
- try {
446
- const r = await fetch('/api/health', { cache: 'no-store' });
447
- if (!r.ok) { everDown = true; continue; }
448
- if (everDown) { location.reload(); return; }
449
- } catch { everDown = true; }
450
- }
451
- // Timeout safety net — reload anyway so the user isn't stranded.
452
- location.reload();
453
- } catch {}
419
+ 'menu-install-app': async () => {
420
+ disarmInstallHandoff(); // asked for here: this click opens the dialog, no later one does
421
+ const result = await requestPwaInstall();
422
+ if (result === 'instructions') location.assign(installGuideUrl());
454
423
  },
424
+ // The restart screen is drawn in this click, before anything is asked of
425
+ // the network; from there it follows the restart and loads the page again
426
+ // when the server is back (ui-loading.js requestServerRestart).
427
+ 'menu-restart-server': () => { requestServerRestart(); },
455
428
  'menu-stop-server': async () => {
456
429
  const previousTitle = document.title;
457
430
  try {
@@ -478,23 +451,16 @@ function wireTerminalMenu() {
478
451
  registerAction('toggle-terminal', () => emit('terminal:toggle'));
479
452
  registerAction('toggle-terminal-alt', () => emit('terminal:toggle'));
480
453
 
481
- // PWA install prompt — show "Install as App" when the browser supports it
482
- window.addEventListener('beforeinstallprompt', (e) => {
483
- e.preventDefault();
484
- window._pwaInstallPrompt = e;
454
+ initPwaInstall({ changed: value => {
485
455
  const el = $('menu-install-app');
486
- if (el) el.style.display = '';
487
- });
488
- window.addEventListener('appinstalled', () => {
489
- window._pwaInstallPrompt = null;
490
- const el = $('menu-install-app');
491
- if (el) el.style.display = 'none';
492
- });
493
- // Hide if already running as installed PWA
494
- if (window.matchMedia('(display-mode: standalone)').matches || navigator.standalone) {
495
- const el = $('menu-install-app');
496
- if (el) el.style.display = 'none';
497
- }
456
+ if (el) el.style.display = value === 'installed' ? 'none' : '';
457
+ } });
458
+ const installItem = $('menu-install-app');
459
+ if (installItem && !isStandalone()) installItem.style.display = '';
460
+ // Sent on by the launcher's guide page: the browser's install dialog opens from the first click or key press,
461
+ // for this one page load. The event it uses is spent, so the menu item goes to the guide page after that.
462
+ disarmInstallHandoff = armPwaInstallHandoff({ leave: event => !!installItem?.contains(event.target) });
463
+
498
464
  }
499
465
 
500
466
  // ── Variant menu items injection ──
@@ -92,3 +92,14 @@ export class LocalReleaseTracker {
92
92
  }
93
93
  }
94
94
  }
95
+
96
+ /**
97
+ * The saved list of dismissed runs (localStorage, `[{ runId, at }]`) without
98
+ * one run: what the agents dock's Focus leaves when it reopens a closed tab.
99
+ * Anything unreadable becomes an empty list.
100
+ */
101
+ export function withoutDismissed(raw, runId) {
102
+ let stored;
103
+ try { stored = JSON.parse(raw || '[]'); } catch { stored = []; }
104
+ return JSON.stringify((Array.isArray(stored) ? stored : []).filter((entry) => entry?.runId && entry.runId !== runId));
105
+ }
@@ -7,6 +7,7 @@ import {
7
7
  isNewerRunDescriptor,
8
8
  LocalReleaseTracker,
9
9
  resolveRouteClaim,
10
+ withoutDismissed,
10
11
  } from './ui-native-loop-router-state.js';
11
12
 
12
13
  const DISMISSED_KEY = 'synabun-native-loop-dismissed';
@@ -48,6 +49,15 @@ function mergeDismissed(raw) {
48
49
 
49
50
  try { mergeDismissed(localStorage.getItem(DISMISSED_KEY)); } catch {}
50
51
 
52
+ // A closed tab's run is dismissed: nothing routes it back here by itself (a
53
+ // reconcile, a late event, another window's claim). Only the user asks for it
54
+ // again, with the agents dock's Focus (focusNativeLoopRun with `reopen`).
55
+ function undismiss(runId) {
56
+ if (!_dismissed.delete(runId)) return false;
57
+ try { localStorage.setItem(DISMISSED_KEY, withoutDismissed(localStorage.getItem(DISMISSED_KEY), runId)); } catch {}
58
+ return true;
59
+ }
60
+
51
61
  function persistDismissed() {
52
62
  try {
53
63
  const existing = JSON.parse(localStorage.getItem(DISMISSED_KEY) || '[]');
@@ -279,10 +289,13 @@ export function getNativeLoopRun(runId) {
279
289
  * Bring a dispatched run's sidepanel tab to the front. Attaches with
280
290
  * focus:true even when this window already owns the claim (the normal route
281
291
  * path only focuses on first attach). Falls back to a fetch when the run has
282
- * not been broadcast to this window yet.
292
+ * not been broadcast to this window yet. With `reopen` (the user's own Focus,
293
+ * in the agents dock) a run whose tab was closed opens again: a live run
294
+ * attaches as it would have, an ended one with its transcript and end card.
283
295
  */
284
- export async function focusNativeLoopRun(runId) {
296
+ export async function focusNativeLoopRun(runId, { reopen = false } = {}) {
285
297
  if (!runId || _suspended) return false;
298
+ if (reopen) undismiss(runId);
286
299
  let run = getNativeLoopRun(runId);
287
300
  if (!run) {
288
301
  try {