synabun 2.0.0 → 2.0.7

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 (507) hide show
  1. package/CHANGELOG.md +97 -0
  2. package/NOTICE +5 -0
  3. package/README.md +2 -1
  4. package/THIRD-PARTY-LICENSES.md +59 -0
  5. package/benchmarks/fixtures/browser/README.md +1 -1
  6. package/benchmarks/fixtures/desktop/README.md +1 -1
  7. package/docs/whatsapp.md +153 -18
  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 +26 -3
  12. package/hooks/claude-code/stop.mjs +5 -5
  13. package/lib/child-path.js +303 -0
  14. package/lib/claude-hooks.js +2 -1
  15. package/lib/data-home-migration.js +2 -0
  16. package/lib/desktop-app.js +265 -0
  17. package/lib/external-tools.js +7 -3
  18. package/lib/log-redact.js +102 -0
  19. package/lib/packaged-runtime.js +138 -11
  20. package/lib/start-launcher.js +88 -1
  21. package/mcp-server/dist/http.d.ts +3 -1
  22. package/mcp-server/dist/http.d.ts.map +1 -1
  23. package/mcp-server/dist/http.js +15 -4
  24. package/mcp-server/dist/http.js.map +1 -1
  25. package/mcp-server/dist/index.d.ts.map +1 -1
  26. package/mcp-server/dist/index.js +9 -6
  27. package/mcp-server/dist/index.js.map +1 -1
  28. package/mcp-server/dist/services/budget-judgments.d.ts +108 -0
  29. package/mcp-server/dist/services/budget-judgments.d.ts.map +1 -0
  30. package/mcp-server/dist/services/budget-judgments.js +153 -0
  31. package/mcp-server/dist/services/budget-judgments.js.map +1 -0
  32. package/mcp-server/dist/services/codex-config-heal.d.ts +5 -1
  33. package/mcp-server/dist/services/codex-config-heal.d.ts.map +1 -1
  34. package/mcp-server/dist/services/codex-config-heal.js +41 -4
  35. package/mcp-server/dist/services/codex-config-heal.js.map +1 -1
  36. package/mcp-server/dist/services/identity.d.ts +11 -0
  37. package/mcp-server/dist/services/identity.d.ts.map +1 -1
  38. package/mcp-server/dist/services/identity.js +24 -0
  39. package/mcp-server/dist/services/identity.js.map +1 -1
  40. package/mcp-server/dist/services/neural-interface.d.ts +13 -0
  41. package/mcp-server/dist/services/neural-interface.d.ts.map +1 -1
  42. package/mcp-server/dist/services/neural-interface.js +34 -2
  43. package/mcp-server/dist/services/neural-interface.js.map +1 -1
  44. package/mcp-server/dist/services/typesafe-config.d.ts +1 -1
  45. package/mcp-server/dist/services/typesafe-config.d.ts.map +1 -1
  46. package/mcp-server/dist/services/typesafe-config.js +4 -1
  47. package/mcp-server/dist/services/typesafe-config.js.map +1 -1
  48. package/mcp-server/dist/tools/agents.d.ts +10 -7
  49. package/mcp-server/dist/tools/agents.d.ts.map +1 -1
  50. package/mcp-server/dist/tools/agents.js +34 -13
  51. package/mcp-server/dist/tools/agents.js.map +1 -1
  52. package/mcp-server/dist/tools/whatsapp-send-file.d.ts +17 -0
  53. package/mcp-server/dist/tools/whatsapp-send-file.d.ts.map +1 -0
  54. package/mcp-server/dist/tools/whatsapp-send-file.js +44 -0
  55. package/mcp-server/dist/tools/whatsapp-send-file.js.map +1 -0
  56. package/mcp-server/dist/tui.js +2 -1
  57. package/mcp-server/dist/tui.js.map +1 -1
  58. package/mcp-server/dist/version.d.ts +5 -0
  59. package/mcp-server/dist/version.d.ts.map +1 -0
  60. package/mcp-server/dist/version.js +21 -0
  61. package/mcp-server/dist/version.js.map +1 -0
  62. package/mcp-server/package-lock.json +2 -2
  63. package/mcp-server/package.json +1 -3
  64. package/mcp-server/run.mjs +15 -9
  65. package/mcp-server/src/http.ts +18 -3
  66. package/mcp-server/src/index.ts +9 -6
  67. package/mcp-server/src/services/budget-judgments.ts +183 -0
  68. package/mcp-server/src/services/codex-config-heal.ts +35 -4
  69. package/mcp-server/src/services/identity.ts +27 -0
  70. package/mcp-server/src/services/neural-interface.ts +34 -2
  71. package/mcp-server/src/services/typesafe-config.ts +4 -1
  72. package/mcp-server/src/tools/agents.ts +33 -14
  73. package/mcp-server/src/tools/whatsapp-send-file.ts +51 -0
  74. package/mcp-server/src/tui.ts +2 -1
  75. package/mcp-server/src/version.ts +22 -0
  76. package/neural-interface/i18n/en.json +2126 -209
  77. package/neural-interface/i18n/pt-BR.json +2123 -207
  78. package/neural-interface/lib/account-usage.js +163 -0
  79. package/neural-interface/lib/assistant-api.js +316 -57
  80. package/neural-interface/lib/assistant-brains/claude.js +19 -2
  81. package/neural-interface/lib/assistant-brains/codex.js +3 -0
  82. package/neural-interface/lib/assistant-brains/opencode.js +17 -4
  83. package/neural-interface/lib/assistant-budget-planner.js +390 -0
  84. package/neural-interface/lib/assistant-budget-split.js +408 -0
  85. package/neural-interface/lib/assistant-budget.js +94 -95
  86. package/neural-interface/lib/assistant-catalog.js +179 -27
  87. package/neural-interface/lib/assistant-config.js +91 -14
  88. package/neural-interface/lib/assistant-dispatch.js +494 -138
  89. package/neural-interface/lib/assistant-hidden-models.js +393 -66
  90. package/neural-interface/lib/assistant-jev.js +26 -3
  91. package/neural-interface/lib/assistant-memory.js +24 -10
  92. package/neural-interface/lib/assistant-persona.js +73 -12
  93. package/neural-interface/lib/assistant-plan-permissions.js +8 -1
  94. package/neural-interface/lib/assistant-pricing.js +21 -6
  95. package/neural-interface/lib/assistant-route-accounts.js +14 -0
  96. package/neural-interface/lib/assistant-route-gate.js +19 -3
  97. package/neural-interface/lib/assistant-router.js +373 -42
  98. package/neural-interface/lib/assistant-runtime.js +488 -59
  99. package/neural-interface/lib/assistant-task-prompt.js +13 -3
  100. package/neural-interface/lib/assistant-test-density.js +142 -0
  101. package/neural-interface/lib/assistant-user-context-budgets.js +123 -0
  102. package/neural-interface/lib/assistant-user-context.js +908 -0
  103. package/neural-interface/lib/assistant-user-knowledge.js +328 -0
  104. package/neural-interface/lib/assistant-user-learn.js +404 -0
  105. package/neural-interface/lib/assistant-user-threads.js +226 -0
  106. package/neural-interface/lib/assistant-user-writer.js +183 -0
  107. package/neural-interface/lib/augmented-path.js +317 -53
  108. package/neural-interface/lib/browser-background-tab.js +134 -0
  109. package/neural-interface/lib/browser-screenshot.js +678 -61
  110. package/neural-interface/lib/claude-accounts.js +342 -31
  111. package/neural-interface/lib/claude-agent-bridge.js +123 -9
  112. package/neural-interface/lib/claude-executable.js +6 -1
  113. package/neural-interface/lib/claude-model-catalog.js +33 -5
  114. package/neural-interface/lib/claude-panel-auth.js +221 -0
  115. package/neural-interface/lib/claude-profile-api-key.js +62 -0
  116. package/neural-interface/lib/claude-temporary.js +46 -7
  117. package/neural-interface/lib/cli-install-api.js +137 -0
  118. package/neural-interface/lib/cli-install.js +797 -0
  119. package/neural-interface/lib/cli-path.js +130 -0
  120. package/neural-interface/lib/cli-resolver.js +744 -0
  121. package/neural-interface/lib/cli-update-planner.js +143 -7
  122. package/neural-interface/lib/cloudflared/install.js +533 -0
  123. package/neural-interface/lib/cloudflared/manager.js +545 -0
  124. package/neural-interface/lib/cloudflared/pins.js +115 -0
  125. package/neural-interface/lib/cloudflared/pins.json +63 -0
  126. package/neural-interface/lib/cloudflared/tunnel.js +265 -0
  127. package/neural-interface/lib/codex-account-auth.js +178 -0
  128. package/neural-interface/lib/codex-accounts.js +489 -0
  129. package/neural-interface/lib/codex-capabilities.js +2 -1
  130. package/neural-interface/lib/codex-compat-rpc.js +8 -2
  131. package/neural-interface/lib/codex-mcp-approvals.js +5 -1
  132. package/neural-interface/lib/codex-panel-auth.js +62 -0
  133. package/neural-interface/lib/codex-session-settings.js +1 -1
  134. package/neural-interface/lib/codex-settings-schema.js +120 -0
  135. package/neural-interface/lib/desktop/config.js +3 -0
  136. package/neural-interface/lib/editor-completion-endpoint.js +264 -0
  137. package/neural-interface/lib/editor-completion-opencode.js +436 -0
  138. package/neural-interface/lib/editor-completion.js +714 -0
  139. package/neural-interface/lib/editor-config.js +149 -0
  140. package/neural-interface/lib/editor-files-api.js +268 -0
  141. package/neural-interface/lib/editor-git-api.js +134 -0
  142. package/neural-interface/lib/file-explorer-api.js +1039 -0
  143. package/neural-interface/lib/file-explorer-system.js +355 -0
  144. package/neural-interface/lib/file-explorer-trash.ps1 +85 -0
  145. package/neural-interface/lib/file-explorer-watch.js +239 -0
  146. package/neural-interface/lib/git-bash/bzip2.js +339 -0
  147. package/neural-interface/lib/git-bash/claude-settings.js +73 -0
  148. package/neural-interface/lib/git-bash/extract-worker.js +34 -0
  149. package/neural-interface/lib/git-bash/install.js +529 -0
  150. package/neural-interface/lib/git-bash/licenses/COPYING +360 -0
  151. package/neural-interface/lib/git-bash/licenses/LGPL-2.1 +511 -0
  152. package/neural-interface/lib/git-bash/manager.js +268 -0
  153. package/neural-interface/lib/git-bash/pins.js +206 -0
  154. package/neural-interface/lib/git-bash/pins.json +34 -0
  155. package/neural-interface/lib/git-bash/resolver.js +194 -0
  156. package/neural-interface/lib/git-diff-api.js +103 -0
  157. package/neural-interface/lib/git-read.js +234 -0
  158. package/neural-interface/lib/greeting/api.js +89 -0
  159. package/neural-interface/lib/greeting/budgets.js +90 -0
  160. package/neural-interface/lib/greeting/engine.js +461 -0
  161. package/neural-interface/lib/greeting/facts.js +320 -0
  162. package/neural-interface/lib/greeting/sources.js +252 -0
  163. package/neural-interface/lib/greeting/writer.js +139 -0
  164. package/neural-interface/lib/http-guards.js +189 -13
  165. package/neural-interface/lib/install-details.js +145 -0
  166. package/neural-interface/lib/machine-names.js +88 -0
  167. package/neural-interface/lib/mcp-installer.js +14 -2
  168. package/neural-interface/lib/memory-api.js +31 -7
  169. package/neural-interface/lib/native-loop-providers.js +26 -8
  170. package/neural-interface/lib/native-loop-runtime.js +9 -4
  171. package/neural-interface/lib/os-path.js +319 -0
  172. package/neural-interface/lib/plugin-installer.js +4 -2
  173. package/neural-interface/lib/project-storage.js +5 -7
  174. package/neural-interface/lib/provider-panel-api-key-auth.js +92 -0
  175. package/neural-interface/lib/provider-panel-terminal-auth.js +95 -0
  176. package/neural-interface/lib/pty-host/core.js +2 -1
  177. package/neural-interface/lib/pty-host/manager.js +27 -2
  178. package/neural-interface/lib/remote-policy.js +20 -11
  179. package/neural-interface/lib/request-gate.js +589 -0
  180. package/neural-interface/lib/session-title-generator.js +4 -3
  181. package/neural-interface/lib/system-about.js +340 -0
  182. package/neural-interface/lib/system-activity.js +66 -0
  183. package/neural-interface/lib/terminal-env.js +11 -3
  184. package/neural-interface/lib/terminal-path.js +55 -0
  185. package/neural-interface/lib/ui-blob-api.js +108 -0
  186. package/neural-interface/lib/ui-blob-store.js +68 -0
  187. package/neural-interface/lib/virtual-socket.js +12 -1
  188. package/neural-interface/lib/whatsapp/baileys-adapter.js +165 -45
  189. package/neural-interface/lib/whatsapp/bridge.js +78 -34
  190. package/neural-interface/lib/whatsapp/commands.js +13 -14
  191. package/neural-interface/lib/whatsapp/config.js +134 -20
  192. package/neural-interface/lib/whatsapp/fake-baileys.js +99 -14
  193. package/neural-interface/lib/whatsapp/host-core.js +168 -35
  194. package/neural-interface/lib/whatsapp/inbound.js +5 -3
  195. package/neural-interface/lib/whatsapp/local-file.js +524 -0
  196. package/neural-interface/lib/whatsapp/manager.js +92 -32
  197. package/neural-interface/lib/whatsapp/paths.js +11 -1
  198. package/neural-interface/lib/whatsapp/protocol.js +65 -5
  199. package/neural-interface/lib/whatsapp/redact.js +63 -0
  200. package/neural-interface/lib/whatsapp/service.js +343 -91
  201. package/neural-interface/lib/whatsapp/transcription.js +463 -0
  202. package/neural-interface/package-lock.json +701 -2
  203. package/neural-interface/package.json +13 -2
  204. package/neural-interface/public/index.html +2 -0
  205. package/neural-interface/public/index2d.html +2 -0
  206. package/neural-interface/public/install-app.html +9 -6
  207. package/neural-interface/public/offline.html +19 -3
  208. package/neural-interface/public/onboarding.html +107 -2
  209. package/neural-interface/public/shared/account-i18n.js +7 -0
  210. package/neural-interface/public/shared/account-menu.js +539 -0
  211. package/neural-interface/public/shared/account-usage.js +19 -0
  212. package/neural-interface/public/shared/account-view.js +335 -0
  213. package/neural-interface/public/shared/agent-runtime-options.js +50 -14
  214. package/neural-interface/public/shared/assistant/asst-accounts.js +182 -184
  215. package/neural-interface/public/shared/assistant/asst-api.js +32 -2
  216. package/neural-interface/public/shared/assistant/asst-automation.js +286 -0
  217. package/neural-interface/public/shared/assistant/asst-brain-picker.js +430 -81
  218. package/neural-interface/public/shared/assistant/asst-budget.js +259 -303
  219. package/neural-interface/public/shared/assistant/asst-composer.js +11 -1
  220. package/neural-interface/public/shared/assistant/asst-mascot.js +140 -61
  221. package/neural-interface/public/shared/assistant/asst-menu.js +27 -11
  222. package/neural-interface/public/shared/assistant/asst-panel.js +198 -42
  223. package/neural-interface/public/shared/assistant/asst-render.js +197 -23
  224. package/neural-interface/public/shared/assistant/asst-route-models.js +108 -0
  225. package/neural-interface/public/shared/assistant/asst-route.js +95 -343
  226. package/neural-interface/public/shared/assistant/asst-routes-editor.js +534 -0
  227. package/neural-interface/public/shared/assistant/asst-sanitize.js +8 -1
  228. package/neural-interface/public/shared/assistant/asst-sidepanel.js +31 -9
  229. package/neural-interface/public/shared/assistant/asst-slash.js +1 -1
  230. package/neural-interface/public/shared/assistant/asst-state.js +186 -2
  231. package/neural-interface/public/shared/assistant/asst-styles.js +247 -11
  232. package/neural-interface/public/shared/blob-store.js +111 -22
  233. package/neural-interface/public/shared/cdx/cdx-commands.js +4 -1
  234. package/neural-interface/public/shared/cdx/cdx-features.js +22 -14
  235. package/neural-interface/public/shared/cdx/cdx-icons.js +2 -0
  236. package/neural-interface/public/shared/cdx/cdx-panel.js +80 -118
  237. package/neural-interface/public/shared/cdx/cdx-render.js +25 -89
  238. package/neural-interface/public/shared/cdx/cdx-settings.js +231 -0
  239. package/neural-interface/public/shared/cdx/cdx-styles.js +164 -107
  240. package/neural-interface/public/shared/cdx/cdx-tabs.js +348 -668
  241. package/neural-interface/public/shared/cdx/cdx-ws.js +2 -0
  242. package/neural-interface/public/shared/claude-account-view.js +3 -0
  243. package/neural-interface/public/shared/cli-install-view.js +392 -0
  244. package/neural-interface/public/shared/cli-status.js +282 -22
  245. package/neural-interface/public/shared/constants.js +6 -1
  246. package/neural-interface/public/shared/cp/cp-events.js +2 -1
  247. package/neural-interface/public/shared/cp/cp-rehydrate.js +2 -1
  248. package/neural-interface/public/shared/cp/cp-styles.js +61 -0
  249. package/neural-interface/public/shared/desktop-bridge.js +39 -0
  250. package/neural-interface/public/shared/desktop-titlebar-page.js +59 -0
  251. package/neural-interface/public/shared/desktop-titlebar-view.js +122 -0
  252. package/neural-interface/public/shared/desktop-titlebar.js +153 -0
  253. package/neural-interface/public/shared/desktop-update-view.js +462 -0
  254. package/neural-interface/public/shared/editor/editor-ai-logic.js +409 -0
  255. package/neural-interface/public/shared/editor/editor-ai.js +619 -0
  256. package/neural-interface/public/shared/editor/editor-commands.js +212 -0
  257. package/neural-interface/public/shared/editor/editor-compare.js +103 -0
  258. package/neural-interface/public/shared/editor/editor-completion-list.js +172 -0
  259. package/neural-interface/public/shared/editor/editor-doc.js +257 -0
  260. package/neural-interface/public/shared/editor/editor-git-logic.js +131 -0
  261. package/neural-interface/public/shared/editor/editor-git.js +95 -0
  262. package/neural-interface/public/shared/editor/editor-languages.js +156 -0
  263. package/neural-interface/public/shared/editor/editor-lint.js +229 -0
  264. package/neural-interface/public/shared/editor/editor-loader.js +51 -0
  265. package/neural-interface/public/shared/editor/editor-markdown.js +90 -0
  266. package/neural-interface/public/shared/editor/editor-menu.js +233 -0
  267. package/neural-interface/public/shared/editor/editor-minimap.js +371 -0
  268. package/neural-interface/public/shared/editor/editor-palette-logic.js +192 -0
  269. package/neural-interface/public/shared/editor/editor-palette.js +125 -0
  270. package/neural-interface/public/shared/editor/editor-panel-logic.js +288 -0
  271. package/neural-interface/public/shared/editor/editor-panel.js +2099 -0
  272. package/neural-interface/public/shared/editor/editor-prefs.js +224 -0
  273. package/neural-interface/public/shared/editor/editor-safety.js +500 -0
  274. package/neural-interface/public/shared/editor/editor-search.js +352 -0
  275. package/neural-interface/public/shared/editor/editor-statusbar.js +144 -0
  276. package/neural-interface/public/shared/editor/editor-tabs-logic.js +331 -0
  277. package/neural-interface/public/shared/editor/editor-tabs.js +233 -0
  278. package/neural-interface/public/shared/editor/editor-theme.js +244 -0
  279. package/neural-interface/public/shared/editor/editor-view.js +332 -0
  280. package/neural-interface/public/shared/explorer/fx-api.js +216 -0
  281. package/neural-interface/public/shared/explorer/fx-links.js +204 -0
  282. package/neural-interface/public/shared/explorer/fx-logic.js +537 -0
  283. package/neural-interface/public/shared/explorer/fx-main.js +865 -0
  284. package/neural-interface/public/shared/explorer/fx-names.js +87 -0
  285. package/neural-interface/public/shared/explorer/fx-ops.js +370 -0
  286. package/neural-interface/public/shared/explorer/fx-popups.js +490 -0
  287. package/neural-interface/public/shared/explorer/fx-tree.js +790 -0
  288. package/neural-interface/public/shared/file-links.js +339 -0
  289. package/neural-interface/public/shared/git-bash-view.js +145 -0
  290. package/neural-interface/public/shared/greeting/greeting-card.js +178 -0
  291. package/neural-interface/public/shared/greeting/greeting-view.js +148 -0
  292. package/neural-interface/public/shared/html-shell.js +161 -165
  293. package/neural-interface/public/shared/i18n.js +19 -1
  294. package/neural-interface/public/shared/install-details-view.js +86 -0
  295. package/neural-interface/public/shared/ocp-v2/ocp-v2-childpanel.js +4 -0
  296. package/neural-interface/public/shared/ocp-v2/ocp-v2-greeting.js +47 -0
  297. package/neural-interface/public/shared/ocp-v2/ocp-v2-panel.js +131 -17
  298. package/neural-interface/public/shared/ocp-v2/ocp-v2-render.js +7 -1
  299. package/neural-interface/public/shared/ocp-v2/ocp-v2-send.js +22 -0
  300. package/neural-interface/public/shared/ocp-v2/ocp-v2-styles.js +158 -0
  301. package/neural-interface/public/shared/ocp-v2/ocp-v2-ws.js +3 -2
  302. package/neural-interface/public/shared/pwa-install-page.js +32 -2
  303. package/neural-interface/public/shared/pwa-install.css +3 -3
  304. package/neural-interface/public/shared/pwa-install.js +100 -1
  305. package/neural-interface/public/shared/settings/settings-about-pane.js +271 -0
  306. package/neural-interface/public/shared/settings/settings-about.js +254 -0
  307. package/neural-interface/public/shared/settings/settings-code-editor-pane.js +412 -0
  308. package/neural-interface/public/shared/settings/settings-code-editor.js +328 -0
  309. package/neural-interface/public/shared/settings/settings-greeting.js +133 -0
  310. package/neural-interface/public/shared/settings/settings-ia.js +33 -4
  311. package/neural-interface/public/shared/settings/settings-user-context.js +154 -0
  312. package/neural-interface/public/shared/showcase-privacy.js +489 -0
  313. package/neural-interface/public/shared/start-bridge.js +238 -19
  314. package/neural-interface/public/shared/storage.js +30 -3
  315. package/neural-interface/public/shared/styles.css +1710 -770
  316. package/neural-interface/public/shared/synabun-mascot.js +68 -58
  317. package/neural-interface/public/shared/tunnel-view.js +167 -0
  318. package/neural-interface/public/shared/ui-claude-panel.js +369 -229
  319. package/neural-interface/public/shared/ui-cli-banner.js +355 -0
  320. package/neural-interface/public/shared/ui-detail.js +15 -0
  321. package/neural-interface/public/shared/ui-explorer.js +22 -0
  322. package/neural-interface/public/shared/ui-file-explorer.js +481 -2496
  323. package/neural-interface/public/shared/ui-file-links.js +103 -0
  324. package/neural-interface/public/shared/ui-git-bash.js +124 -0
  325. package/neural-interface/public/shared/ui-help.js +26 -0
  326. package/neural-interface/public/shared/ui-invite.js +126 -116
  327. package/neural-interface/public/shared/ui-judgments.js +10 -1
  328. package/neural-interface/public/shared/ui-keybinds.js +4 -0
  329. package/neural-interface/public/shared/ui-loading.js +187 -19
  330. package/neural-interface/public/shared/ui-menubar.js +25 -33
  331. package/neural-interface/public/shared/ui-native-loop-router-state.js +11 -0
  332. package/neural-interface/public/shared/ui-native-loop-router.js +15 -2
  333. package/neural-interface/public/shared/ui-navbar.js +48 -22
  334. package/neural-interface/public/shared/ui-provider-auth.js +104 -0
  335. package/neural-interface/public/shared/ui-provider-terminal-auth.js +223 -0
  336. package/neural-interface/public/shared/ui-sessions.js +9 -3
  337. package/neural-interface/public/shared/ui-settings.js +509 -490
  338. package/neural-interface/public/shared/ui-sidepanel-runtime.js +2 -1
  339. package/neural-interface/public/shared/ui-sidepanel-sessions.js +3 -1
  340. package/neural-interface/public/shared/ui-sync.js +22 -0
  341. package/neural-interface/public/shared/ui-terminal.js +40 -5
  342. package/neural-interface/public/shared/ui-tool-updates.js +5 -0
  343. package/neural-interface/public/shared/ui-tunnel.js +130 -0
  344. package/neural-interface/public/shared/ui-tutorial-steps.js +6 -2
  345. package/neural-interface/public/shared/ui-update.js +594 -5
  346. package/neural-interface/public/shared/ui-whatsapp.js +21 -6
  347. package/neural-interface/public/shared/ui-whiteboard.js +12 -0
  348. package/neural-interface/public/shared/ui-worker-end.js +245 -0
  349. package/neural-interface/public/shared/ui-workspaces.js +41 -27
  350. package/neural-interface/public/shared/whatsapp/wa-view.js +51 -8
  351. package/neural-interface/public/shared/worker-end.js +253 -0
  352. package/neural-interface/public/sw.js +2 -2
  353. package/neural-interface/public/variant/2d/main.js +10 -1
  354. package/neural-interface/public/variant/3d/main.js +10 -1
  355. package/neural-interface/public/vendor/codemirror/chunks/apl-CSDKT72Z.js +1 -0
  356. package/neural-interface/public/vendor/codemirror/chunks/asciiarmor-JYYPU5BM.js +1 -0
  357. package/neural-interface/public/vendor/codemirror/chunks/asn1-NRWCZDA6.js +1 -0
  358. package/neural-interface/public/vendor/codemirror/chunks/asterisk-5OU6D4ZQ.js +1 -0
  359. package/neural-interface/public/vendor/codemirror/chunks/brainfuck-ID4N4SYC.js +1 -0
  360. package/neural-interface/public/vendor/codemirror/chunks/chunk-4RZWGCS2.js +1 -0
  361. package/neural-interface/public/vendor/codemirror/chunks/chunk-ACTJM3JR.js +11 -0
  362. package/neural-interface/public/vendor/codemirror/chunks/chunk-S6BMAXLK.js +1 -0
  363. package/neural-interface/public/vendor/codemirror/chunks/chunk-TO2RDOHQ.js +3 -0
  364. package/neural-interface/public/vendor/codemirror/chunks/chunk-US2GMZZQ.js +13 -0
  365. package/neural-interface/public/vendor/codemirror/chunks/chunk-WYYO5ZAJ.js +1 -0
  366. package/neural-interface/public/vendor/codemirror/chunks/chunk-XALZTDJC.js +1 -0
  367. package/neural-interface/public/vendor/codemirror/chunks/chunk-YRNUTRDV.js +1 -0
  368. package/neural-interface/public/vendor/codemirror/chunks/chunk-Z3QOKH62.js +1 -0
  369. package/neural-interface/public/vendor/codemirror/chunks/clike-OWQBSOGL.js +1 -0
  370. package/neural-interface/public/vendor/codemirror/chunks/clojure-KV75UQWC.js +1 -0
  371. package/neural-interface/public/vendor/codemirror/chunks/cmake-GTEP5Y3C.js +1 -0
  372. package/neural-interface/public/vendor/codemirror/chunks/cobol-WCZ7QEGJ.js +1 -0
  373. package/neural-interface/public/vendor/codemirror/chunks/coffeescript-TGLBOT6B.js +1 -0
  374. package/neural-interface/public/vendor/codemirror/chunks/commonlisp-WVVI432V.js +1 -0
  375. package/neural-interface/public/vendor/codemirror/chunks/crystal-S6R6HLKQ.js +1 -0
  376. package/neural-interface/public/vendor/codemirror/chunks/css-PDA2RINP.js +1 -0
  377. package/neural-interface/public/vendor/codemirror/chunks/cypher-HL5QPL4C.js +1 -0
  378. package/neural-interface/public/vendor/codemirror/chunks/d-HHJH2HAJ.js +1 -0
  379. package/neural-interface/public/vendor/codemirror/chunks/diff-MN3A35BY.js +1 -0
  380. package/neural-interface/public/vendor/codemirror/chunks/dist-25SQLBVS.js +3 -0
  381. package/neural-interface/public/vendor/codemirror/chunks/dist-27ZGGJJS.js +1 -0
  382. package/neural-interface/public/vendor/codemirror/chunks/dist-4E52OJIF.js +1 -0
  383. package/neural-interface/public/vendor/codemirror/chunks/dist-6FT73XDC.js +2 -0
  384. package/neural-interface/public/vendor/codemirror/chunks/dist-BE6YGRMW.js +1 -0
  385. package/neural-interface/public/vendor/codemirror/chunks/dist-CJSGKW2O.js +1 -0
  386. package/neural-interface/public/vendor/codemirror/chunks/dist-CRPH2WLI.js +1 -0
  387. package/neural-interface/public/vendor/codemirror/chunks/dist-CTG7JEQN.js +1 -0
  388. package/neural-interface/public/vendor/codemirror/chunks/dist-I3FEVELX.js +1 -0
  389. package/neural-interface/public/vendor/codemirror/chunks/dist-IM5BBSWZ.js +1 -0
  390. package/neural-interface/public/vendor/codemirror/chunks/dist-KERUKTIF.js +1 -0
  391. package/neural-interface/public/vendor/codemirror/chunks/dist-LXTMWZAM.js +1 -0
  392. package/neural-interface/public/vendor/codemirror/chunks/dist-MABASSET.js +1 -0
  393. package/neural-interface/public/vendor/codemirror/chunks/dist-MJVBUZLL.js +3 -0
  394. package/neural-interface/public/vendor/codemirror/chunks/dist-MNB3WZVU.js +1 -0
  395. package/neural-interface/public/vendor/codemirror/chunks/dist-QYWXOE3U.js +7 -0
  396. package/neural-interface/public/vendor/codemirror/chunks/dist-TEBOFWLZ.js +1 -0
  397. package/neural-interface/public/vendor/codemirror/chunks/dist-UNXFROM3.js +1 -0
  398. package/neural-interface/public/vendor/codemirror/chunks/dist-XL2SLQO6.js +1 -0
  399. package/neural-interface/public/vendor/codemirror/chunks/dist-XO42Y3AP.js +6 -0
  400. package/neural-interface/public/vendor/codemirror/chunks/dist-Y2WWDWJK.js +1 -0
  401. package/neural-interface/public/vendor/codemirror/chunks/dist-YFEZKWMU.js +1 -0
  402. package/neural-interface/public/vendor/codemirror/chunks/dist-ZY7SWQO6.js +1 -0
  403. package/neural-interface/public/vendor/codemirror/chunks/dockerfile-3Y4R4NCH.js +1 -0
  404. package/neural-interface/public/vendor/codemirror/chunks/dtd-5343GJ4P.js +1 -0
  405. package/neural-interface/public/vendor/codemirror/chunks/dylan-FI2CXSW2.js +1 -0
  406. package/neural-interface/public/vendor/codemirror/chunks/ebnf-GNQ77YWA.js +1 -0
  407. package/neural-interface/public/vendor/codemirror/chunks/ecl-MRPUAE2L.js +1 -0
  408. package/neural-interface/public/vendor/codemirror/chunks/eiffel-DZROES27.js +1 -0
  409. package/neural-interface/public/vendor/codemirror/chunks/elm-4KICRFKQ.js +1 -0
  410. package/neural-interface/public/vendor/codemirror/chunks/erlang-3IHYC263.js +1 -0
  411. package/neural-interface/public/vendor/codemirror/chunks/factor-C6D3E5UK.js +1 -0
  412. package/neural-interface/public/vendor/codemirror/chunks/fcl-VYTWUJMI.js +1 -0
  413. package/neural-interface/public/vendor/codemirror/chunks/forth-ZBO2EH5M.js +1 -0
  414. package/neural-interface/public/vendor/codemirror/chunks/fortran-5NDCRWIK.js +1 -0
  415. package/neural-interface/public/vendor/codemirror/chunks/gas-SKRMOXFP.js +1 -0
  416. package/neural-interface/public/vendor/codemirror/chunks/gherkin-L45YXZLW.js +1 -0
  417. package/neural-interface/public/vendor/codemirror/chunks/groovy-GLPTLN44.js +1 -0
  418. package/neural-interface/public/vendor/codemirror/chunks/haskell-E7NF7P4Y.js +1 -0
  419. package/neural-interface/public/vendor/codemirror/chunks/haxe-K2NVDOB6.js +1 -0
  420. package/neural-interface/public/vendor/codemirror/chunks/http-Q6DRQIJB.js +1 -0
  421. package/neural-interface/public/vendor/codemirror/chunks/idl-MZYNJVLN.js +1 -0
  422. package/neural-interface/public/vendor/codemirror/chunks/javascript-6YRYK2HR.js +1 -0
  423. package/neural-interface/public/vendor/codemirror/chunks/julia-HXEL2T5G.js +1 -0
  424. package/neural-interface/public/vendor/codemirror/chunks/livescript-MV7CI2JZ.js +1 -0
  425. package/neural-interface/public/vendor/codemirror/chunks/lua-E36NHOOD.js +1 -0
  426. package/neural-interface/public/vendor/codemirror/chunks/mathematica-3XOZZCMC.js +1 -0
  427. package/neural-interface/public/vendor/codemirror/chunks/mbox-ODYGFKS7.js +1 -0
  428. package/neural-interface/public/vendor/codemirror/chunks/mirc-WB43AWF5.js +1 -0
  429. package/neural-interface/public/vendor/codemirror/chunks/mllike-ZT6ET7ZH.js +1 -0
  430. package/neural-interface/public/vendor/codemirror/chunks/modelica-LLMZPFGA.js +1 -0
  431. package/neural-interface/public/vendor/codemirror/chunks/mscgen-2OPNNYFH.js +1 -0
  432. package/neural-interface/public/vendor/codemirror/chunks/mumps-6P3HWJH6.js +1 -0
  433. package/neural-interface/public/vendor/codemirror/chunks/nginx-TCGKQTRW.js +1 -0
  434. package/neural-interface/public/vendor/codemirror/chunks/nsis-ATIGIXTJ.js +1 -0
  435. package/neural-interface/public/vendor/codemirror/chunks/ntriples-5PLBI5OV.js +1 -0
  436. package/neural-interface/public/vendor/codemirror/chunks/octave-QF2S3NL6.js +1 -0
  437. package/neural-interface/public/vendor/codemirror/chunks/oz-JFIVFFH2.js +1 -0
  438. package/neural-interface/public/vendor/codemirror/chunks/pascal-JG6SW76V.js +1 -0
  439. package/neural-interface/public/vendor/codemirror/chunks/perl-4B3IWVVO.js +1 -0
  440. package/neural-interface/public/vendor/codemirror/chunks/pig-ABEBH4MI.js +1 -0
  441. package/neural-interface/public/vendor/codemirror/chunks/powershell-H32XE7TL.js +1 -0
  442. package/neural-interface/public/vendor/codemirror/chunks/properties-NN644MMA.js +1 -0
  443. package/neural-interface/public/vendor/codemirror/chunks/protobuf-YKWQK2J3.js +1 -0
  444. package/neural-interface/public/vendor/codemirror/chunks/pug-RIYXEKFW.js +1 -0
  445. package/neural-interface/public/vendor/codemirror/chunks/puppet-JZQ5WPEV.js +1 -0
  446. package/neural-interface/public/vendor/codemirror/chunks/python-EBCGGHAC.js +1 -0
  447. package/neural-interface/public/vendor/codemirror/chunks/q-5UMH2YQY.js +1 -0
  448. package/neural-interface/public/vendor/codemirror/chunks/r-7XMGG453.js +1 -0
  449. package/neural-interface/public/vendor/codemirror/chunks/rpm-IEV7TSLR.js +1 -0
  450. package/neural-interface/public/vendor/codemirror/chunks/ruby-T4SPFPNM.js +1 -0
  451. package/neural-interface/public/vendor/codemirror/chunks/sas-GEDX67ET.js +1 -0
  452. package/neural-interface/public/vendor/codemirror/chunks/scheme-FTLOWKPC.js +1 -0
  453. package/neural-interface/public/vendor/codemirror/chunks/shell-HUFRKFSJ.js +1 -0
  454. package/neural-interface/public/vendor/codemirror/chunks/sieve-P5QUDVYO.js +1 -0
  455. package/neural-interface/public/vendor/codemirror/chunks/smalltalk-2SORJNQI.js +1 -0
  456. package/neural-interface/public/vendor/codemirror/chunks/solr-M22TGAR5.js +1 -0
  457. package/neural-interface/public/vendor/codemirror/chunks/sparql-4M3KL752.js +1 -0
  458. package/neural-interface/public/vendor/codemirror/chunks/spreadsheet-3K23LA4N.js +1 -0
  459. package/neural-interface/public/vendor/codemirror/chunks/sql-VHRFMUBI.js +1 -0
  460. package/neural-interface/public/vendor/codemirror/chunks/stex-QZDAQUY2.js +1 -0
  461. package/neural-interface/public/vendor/codemirror/chunks/stylus-7ZDKAKBD.js +1 -0
  462. package/neural-interface/public/vendor/codemirror/chunks/swift-WCLCLGMR.js +1 -0
  463. package/neural-interface/public/vendor/codemirror/chunks/tcl-JLMU6WOK.js +1 -0
  464. package/neural-interface/public/vendor/codemirror/chunks/textile-4CHDKZ4T.js +1 -0
  465. package/neural-interface/public/vendor/codemirror/chunks/tiddlywiki-DKMCZZQ4.js +1 -0
  466. package/neural-interface/public/vendor/codemirror/chunks/tiki-F4JIG36Q.js +1 -0
  467. package/neural-interface/public/vendor/codemirror/chunks/toml-BHMHTIM2.js +1 -0
  468. package/neural-interface/public/vendor/codemirror/chunks/troff-YQ2KV5NT.js +1 -0
  469. package/neural-interface/public/vendor/codemirror/chunks/ttcn-ZHRCVFJY.js +1 -0
  470. package/neural-interface/public/vendor/codemirror/chunks/ttcn-cfg-KJOVTY4X.js +1 -0
  471. package/neural-interface/public/vendor/codemirror/chunks/turtle-535P5N6I.js +1 -0
  472. package/neural-interface/public/vendor/codemirror/chunks/vb-76UMQHOT.js +1 -0
  473. package/neural-interface/public/vendor/codemirror/chunks/vbscript-CGNDTK6Z.js +1 -0
  474. package/neural-interface/public/vendor/codemirror/chunks/velocity-VSJDO5T5.js +1 -0
  475. package/neural-interface/public/vendor/codemirror/chunks/verilog-4T6ETXIP.js +1 -0
  476. package/neural-interface/public/vendor/codemirror/chunks/vhdl-T7FNAXUN.js +1 -0
  477. package/neural-interface/public/vendor/codemirror/chunks/webidl-P6MJSJ6J.js +1 -0
  478. package/neural-interface/public/vendor/codemirror/chunks/xquery-UOVILRGN.js +1 -0
  479. package/neural-interface/public/vendor/codemirror/chunks/yacas-CV2APROL.js +1 -0
  480. package/neural-interface/public/vendor/codemirror/chunks/z80-3KFNVM4M.js +1 -0
  481. package/neural-interface/public/vendor/codemirror/codemirror-6.43.14-75f05e67.min.js +2 -0
  482. package/neural-interface/public/vendor/codemirror/manifest.json +60 -0
  483. package/neural-interface/scripts/build-vendor.mjs +78 -2
  484. package/neural-interface/scripts/vendor-codemirror-entry.js +121 -0
  485. package/neural-interface/server.js +2085 -1629
  486. package/neural-interface/templates/rulesets/README.md +1 -1
  487. package/package.json +5 -8
  488. package/setup.js +53 -15
  489. package/benchmarks/browser-live-results.json +0 -79
  490. package/benchmarks/browser-v2-results.json +0 -258
  491. package/neural-interface/lib/codex-capabilities.d.ts +0 -41
  492. package/neural-interface/public/category-logos/claude.svg +0 -1
  493. package/neural-interface/public/category-logos/gemini-color.svg +0 -1
  494. package/neural-interface/public/category-logos/openai.svg +0 -1
  495. package/neural-interface/public/category-logos/synabun.png +0 -0
  496. package/neural-interface/public/claude.svg +0 -1
  497. package/neural-interface/public/gallery.svg +0 -6
  498. package/neural-interface/public/leonardoai.svg +0 -1
  499. package/neural-interface/public/mcp.svg +0 -4
  500. package/neural-interface/public/opencode-logo-dark-square.svg +0 -1
  501. package/neural-interface/public/pin-svgrepo-com.svg +0 -25
  502. package/neural-interface/public/sessionicon.svg +0 -8
  503. package/neural-interface/public/toolbar-album.svg +0 -8
  504. package/neural-interface/public/toolbar-question.svg +0 -6
  505. package/neural-interface/public/toolbar-sessions.svg +0 -6
  506. package/neural-interface/scripts/codex-protocol-fixture.mjs +0 -83
  507. package/skills/synabun/synabunicon.png +0 -0
@@ -1097,6 +1097,46 @@ const CSS = `
1097
1097
  cursor: default;
1098
1098
  }
1099
1099
 
1100
+ /* A dispatched worker's end (ui-worker-end.js): the post-plan card's frame. Complete keeps its
1101
+ green; the other ends take the panel's own amber, error red or neutral. The title says the state. */
1102
+ .ocpv2-worker-end .ocpv2-post-plan-body { overflow-wrap: anywhere; }
1103
+ .ocpv2-worker-end .ocpv2-post-plan-btn:focus-visible { outline: 2px solid rgba(255,255,255,0.7); outline-offset: 2px; }
1104
+ .ocpv2-worker-end[data-tone="warn"] {
1105
+ border-color: rgba(232,180,80,0.32);
1106
+ background: rgba(232,180,80,0.04);
1107
+ box-shadow: 0 12px 32px rgba(0,0,0,0.22), 0 0 0 1px rgba(232,180,80,0.08) inset;
1108
+ }
1109
+ .ocpv2-worker-end[data-tone="warn"] .ocpv2-post-plan-header {
1110
+ color: rgba(255,200,120,0.95);
1111
+ background: rgba(232,180,80,0.14);
1112
+ border-color: rgba(232,180,80,0.28);
1113
+ }
1114
+ .ocpv2-worker-end[data-tone="err"] {
1115
+ border-color: rgba(220,100,100,0.32);
1116
+ background: rgba(220,100,100,0.05);
1117
+ box-shadow: 0 12px 32px rgba(0,0,0,0.22), 0 0 0 1px rgba(220,100,100,0.08) inset;
1118
+ }
1119
+ .ocpv2-worker-end[data-tone="err"] .ocpv2-post-plan-header {
1120
+ color: rgba(255,166,166,0.92);
1121
+ background: rgba(220,100,100,0.12);
1122
+ border-color: rgba(220,100,100,0.28);
1123
+ }
1124
+ .ocpv2-worker-end[data-tone="off"] {
1125
+ border-color: rgba(255,255,255,0.12);
1126
+ background: rgba(255,255,255,0.03);
1127
+ box-shadow: 0 12px 32px rgba(0,0,0,0.22);
1128
+ }
1129
+ .ocpv2-worker-end[data-tone="off"] .ocpv2-post-plan-header {
1130
+ color: rgba(255,255,255,0.7);
1131
+ background: rgba(255,255,255,0.06);
1132
+ border-color: rgba(255,255,255,0.12);
1133
+ }
1134
+ .ocpv2-worker-end:not([data-tone="ok"]) .ocpv2-post-plan-saved { color: rgba(255,255,255,0.45); }
1135
+ .ocpv2-worker-end:not([data-tone="ok"]) .ocpv2-post-plan-btn.primary {
1136
+ background: rgba(255,255,255,0.085);
1137
+ border-color: rgba(255,255,255,0.2);
1138
+ }
1139
+
1100
1140
  /* Stub shown in place of raw question-tool JSON while the AskUser card handles it */
1101
1141
  .ocpv2-part-question-stub {
1102
1142
  font-size: 10.5px;
@@ -3527,6 +3567,124 @@ const PARITY_CSS = `
3527
3567
  .ocpv2-help-name { flex-shrink: 0; color: rgba(232,224,220,0.95); font-family: 'JetBrains Mono', ui-monospace, monospace; }
3528
3568
  .ocpv2-help-text { flex: 1; min-width: 0; color: rgba(255,255,255,0.62); overflow-wrap: anywhere; }
3529
3569
  .ocpv2-help-row .ocpv2-session-item-meta { flex-shrink: 0; margin-left: 0; }
3570
+
3571
+ /* ── CLI banner (ui-cli-banner.js): OpenCode not installed / installing / failed to start ── */
3572
+ /* Mono like the rest of the panel; the panel's own amber, error red and green. */
3573
+ .ocp-cli-banner {
3574
+ flex-shrink: 0;
3575
+ display: grid;
3576
+ grid-template-columns: auto minmax(0, 1fr);
3577
+ gap: 10px;
3578
+ align-items: start;
3579
+ margin: 6px 8px 0;
3580
+ padding: 10px 12px;
3581
+ border: 1px solid rgba(232,180,80,0.32);
3582
+ border-radius: 8px;
3583
+ background: rgba(232,180,80,0.04);
3584
+ color: rgba(255,255,255,0.86);
3585
+ font-family: 'JetBrains Mono', ui-monospace, SFMono-Regular, monospace;
3586
+ font-size: 11px;
3587
+ line-height: 1.45;
3588
+ position: relative;
3589
+ z-index: 5;
3590
+ }
3591
+ .ocp-cli-banner [hidden] { display: none !important; }
3592
+ .ocp-cli-banner--broken, .ocp-cli-banner--failed { border-color: rgba(220,100,100,0.32); background: rgba(220,100,100,0.05); }
3593
+ .ocp-cli-banner--done { border-color: rgba(140,210,170,0.32); background: rgba(140,210,170,0.05); }
3594
+ .ocp-cli-banner-icon {
3595
+ width: 22px; height: 22px;
3596
+ display: flex; align-items: center; justify-content: center;
3597
+ border-radius: 50%;
3598
+ background: rgba(232,180,80,0.14);
3599
+ border: 1px solid rgba(232,180,80,0.28);
3600
+ color: rgba(255,200,120,0.95);
3601
+ font-weight: 700;
3602
+ }
3603
+ .ocp-cli-banner--broken .ocp-cli-banner-icon, .ocp-cli-banner--failed .ocp-cli-banner-icon { background: rgba(220,100,100,0.12); border-color: rgba(220,100,100,0.3); color: rgba(255,166,166,0.92); }
3604
+ .ocp-cli-banner--done .ocp-cli-banner-icon { background: rgba(140,210,170,0.14); border-color: rgba(140,210,170,0.28); color: rgba(180,230,200,0.92); }
3605
+ .ocp-cli-banner--installing .ocp-cli-banner-icon { animation: ocpv2-abort-sweep 1.2s ease-in-out infinite; }
3606
+ .ocp-cli-banner--nudge { animation: ocp-cli-nudge 0.5s ease; }
3607
+ @keyframes ocp-cli-nudge { 0%, 100% { transform: none; } 25% { transform: translateX(-3px); } 75% { transform: translateX(3px); } }
3608
+ .ocp-cli-banner-text { display: flex; flex-direction: column; gap: 5px; min-width: 0; }
3609
+ .ocp-cli-banner-title { font-size: 11.5px; font-weight: 600; color: rgba(255,200,120,0.95); }
3610
+ .ocp-cli-banner--broken .ocp-cli-banner-title, .ocp-cli-banner--failed .ocp-cli-banner-title { color: rgba(255,166,166,0.92); }
3611
+ .ocp-cli-banner--done .ocp-cli-banner-title { color: rgba(180,230,200,0.92); }
3612
+ .ocp-cli-banner-body, .ocp-cli-banner-note, .ocp-cli-banner-step { color: rgba(255,255,255,0.62); overflow-wrap: anywhere; }
3613
+ .ocp-cli-banner-command { display: flex; align-items: flex-start; gap: 6px; }
3614
+ .ocp-cli-banner-command code, .ocp-cli-banner-error code {
3615
+ flex: 1;
3616
+ min-width: 0;
3617
+ padding: 3px 6px;
3618
+ background: rgba(0,0,0,0.3);
3619
+ border: 1px solid rgba(255,255,255,0.06);
3620
+ border-radius: 4px;
3621
+ font: inherit;
3622
+ font-size: 10.5px;
3623
+ color: rgba(232,224,220,0.95);
3624
+ white-space: pre-wrap;
3625
+ word-break: break-all;
3626
+ user-select: text;
3627
+ }
3628
+ .ocp-cli-banner-error code { display: block; max-height: 96px; overflow: auto; color: rgba(255,166,166,0.92); }
3629
+ .ocp-cli-banner-copy {
3630
+ flex-shrink: 0;
3631
+ height: 22px;
3632
+ padding: 0 8px;
3633
+ border: 1px solid rgba(255,255,255,0.12);
3634
+ border-radius: 5px;
3635
+ background: transparent;
3636
+ color: rgba(255,255,255,0.62);
3637
+ font: inherit;
3638
+ font-size: 10.5px;
3639
+ cursor: pointer;
3640
+ }
3641
+ .ocp-cli-banner-copy:hover { color: rgba(252,246,240,0.98); border-color: rgba(232,224,220,0.3); }
3642
+ .ocp-cli-banner-progress { display: flex; flex-direction: column; gap: 4px; }
3643
+ .ocp-cli-banner-bar { height: 3px; border-radius: 2px; background: rgba(255,255,255,0.08); overflow: hidden; }
3644
+ .ocp-cli-banner-bar > span { display: block; height: 100%; width: 0; background: rgba(232,224,220,0.7); transition: width 0.2s ease; }
3645
+ .ocp-cli-banner-log {
3646
+ margin: 0;
3647
+ max-height: 84px;
3648
+ overflow: auto;
3649
+ padding: 4px 6px;
3650
+ background: rgba(0,0,0,0.25);
3651
+ border-radius: 4px;
3652
+ font: inherit;
3653
+ font-size: 10px;
3654
+ line-height: 1.45;
3655
+ color: rgba(255,255,255,0.5);
3656
+ white-space: pre-wrap;
3657
+ word-break: break-all;
3658
+ }
3659
+ .ocp-cli-banner-actions { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 2px; }
3660
+ .ocp-cli-banner-install, .ocp-cli-banner-link, .ocp-cli-banner-path, .ocp-cli-banner-recheck, .ocp-cli-banner-dismiss {
3661
+ display: inline-flex; align-items: center; gap: 4px;
3662
+ height: 22px;
3663
+ padding: 0 10px;
3664
+ font: inherit;
3665
+ font-size: 11.5px;
3666
+ font-weight: 500;
3667
+ line-height: 1;
3668
+ border-radius: 5px;
3669
+ border: 1px solid rgba(255,255,255,0.14);
3670
+ background: rgba(255,255,255,0.04);
3671
+ color: rgba(255,255,255,0.86);
3672
+ text-decoration: none;
3673
+ cursor: pointer;
3674
+ user-select: none;
3675
+ }
3676
+ .ocp-cli-banner-install { background: rgba(232,224,220,0.15); border-color: rgba(232,224,220,0.3); color: rgba(245,239,232,0.95); }
3677
+ .ocp-cli-banner-install:hover:not(:disabled) { background: rgba(232,224,220,0.25); border-color: rgba(232,224,220,0.45); }
3678
+ .ocp-cli-banner-link:hover, .ocp-cli-banner-path:hover:not(:disabled), .ocp-cli-banner-recheck:hover:not(:disabled), .ocp-cli-banner-dismiss:hover { background: rgba(255,255,255,0.08); border-color: rgba(255,255,255,0.26); }
3679
+ .ocp-cli-banner-install:disabled, .ocp-cli-banner-path:disabled, .ocp-cli-banner-recheck:disabled { opacity: 0.5; cursor: default; }
3680
+ .ocp-cli-banner button:focus-visible, .ocp-cli-banner a:focus-visible { outline: 2px solid rgba(255,255,255,0.7); outline-offset: 2px; }
3681
+ @media (prefers-reduced-motion: reduce) {
3682
+ .ocp-cli-banner--installing .ocp-cli-banner-icon, .ocp-cli-banner--nudge { animation: none; }
3683
+ .ocp-cli-banner--nudge { box-shadow: 0 0 0 1px rgba(232,180,80,0.6); }
3684
+ .ocp-cli-banner-bar > span { transition: none; }
3685
+ }
3686
+ .ocpv2-panel.ocp-cli-blocked .ocpv2-compose-btn:not(.ocpv2-compose-abort) { opacity: 0.4; cursor: not-allowed; }
3687
+ .ocpv2-panel.ocp-cli-blocked .ocpv2-compose-input { opacity: 0.7; }
3530
3688
  `;
3531
3689
 
3532
3690
  export function injectStyles() {
@@ -486,8 +486,9 @@ export const api = {
486
486
  sessionDelete: (sessionId) => request({ type: 'session:delete', sessionId }),
487
487
  sessionMessages:(sessionId) => request({ type: 'session:messages', sessionId }),
488
488
  sessionContext: ({ sessionId, cwd } = {}) => request({ type: 'session:context', sessionId, cwd }),
489
- send: ({ sessionId, parts, model, agent, mode, variant, cwd, mcpProfile, noAbortPrev }) =>
490
- request({ type: 'message:send', sessionId, parts, model, agent, mode, variant, cwd, mcpProfile, noAbortPrev: !!noAbortPrev }, 30 * 60 * 1000),
489
+ // `human` / `greetingId`: lib/greeting/ (a server that does not know them ignores them).
490
+ send: ({ sessionId, parts, model, agent, mode, variant, cwd, mcpProfile, noAbortPrev, human, greetingId }) =>
491
+ request({ type: 'message:send', sessionId, parts, model, agent, mode, variant, cwd, mcpProfile, noAbortPrev: !!noAbortPrev, ...(human ? { human: true } : {}), ...(greetingId ? { greetingId } : {}) }, 30 * 60 * 1000),
491
492
  abort: (sessionId) => request({ type: 'message:abort', sessionId }),
492
493
  compact: ({ sessionId, cwd, mcpProfile, model } = {}) => request({ type: 'session:compact', sessionId, cwd, mcpProfile, model }, 5 * 60 * 1000),
493
494
  permissionReply: ({ sessionId, permissionId, response, cwd, message }) =>
@@ -1,6 +1,20 @@
1
- import { initPwaInstall, isStandalone, requestPwaInstall } from './pwa-install.js';
1
+ import { canPromptPwaInstall, initPwaInstall, installGuideMode, installGuideRequested, isStandalone,
2
+ leavePwaInstallHandoff, requestPwaInstall } from './pwa-install.js';
3
+ import { isDesktopApp } from './desktop-bridge.js';
2
4
 
3
- if (isStandalone()) location.replace('/');
5
+ // The wizard (first run) and the main UI (setup done) open the browser's dialog from a press the user makes
6
+ // anyway, so a browser that has one shows this page only when Apps > Install as App asked for it. true, false,
7
+ // or null for a read that did not answer: only an answer sends the user on, null keeps the guide.
8
+ async function setupComplete() {
9
+ try {
10
+ const res = await fetch('/api/setup/onboarding', { signal: AbortSignal.timeout(3000) });
11
+ const { setupComplete: done } = res.ok ? await res.json() : {};
12
+ return typeof done === 'boolean' ? done : null;
13
+ } catch { return null; }
14
+ }
15
+
16
+ // Already the installed app, or the SynaBun desktop app's own window: nothing to install here.
17
+ if (isStandalone() || isDesktopApp()) location.replace('/');
4
18
  else {
5
19
  const pt = navigator.language.toLowerCase().startsWith('pt');
6
20
  const copy = pt ? {
@@ -8,6 +22,7 @@ else {
8
22
  'install-intro': 'O servidor está rodando em segundo plano. Confirme a instalação uma vez no navegador para abrir o SynaBun em sua própria janela.',
9
23
  'install-chromium': 'Chrome ou Edge: escolha Instalar como app abaixo ou a opção Instalar app no menu do navegador.',
10
24
  'install-safari': 'Safari no macOS: escolha Arquivo → Adicionar ao Dock. Mantenha o nome SynaBun.',
25
+ 'install-other': 'O SynaBun é instalado como app pelo Chrome, pelo Edge ou pelo Safari no macOS. Abra-o em um deles para instalar.',
11
26
  'install-later': 'Após instalar, abra novamente o iniciador nativo do SynaBun. Ele inicia ou reutiliza o servidor e abre o PWA instalado. Fechar a janela mantém o servidor ativo. Use Apps → Stop Server para pará-lo.',
12
27
  'install-button': 'Instalar como app', 'install-continue': 'Continuar para o SynaBun',
13
28
  } : {};
@@ -17,6 +32,7 @@ else {
17
32
  const instructions = pt
18
33
  ? 'Use a opção Instalar app do Chrome/Edge ou Arquivo → Adicionar ao Dock no Safari. A instalação exige sua confirmação no navegador.'
19
34
  : 'Use Chrome/Edge’s Install app menu or Safari’s File → Add to Dock. Installation requires your browser confirmation.';
35
+ // Before the read below: the event can arrive while it is on its way.
20
36
  initPwaInstall({ changed: value => {
21
37
  status.textContent = value === 'installed'
22
38
  ? (pt ? 'Instalado. Abra novamente o iniciador do SynaBun para usar o PWA.' : 'Installed. Reopen the SynaBun launcher to use the PWA.')
@@ -31,4 +47,18 @@ else {
31
47
  } catch { status.textContent = instructions; }
32
48
  });
33
49
  if ('serviceWorker' in navigator) navigator.serviceWorker.register('/sw.js').catch(() => {});
50
+
51
+ // One plain action or one plain instruction, never both: the button only where the browser has a dialog to open.
52
+ const canPrompt = canPromptPwaInstall();
53
+ const show = done => {
54
+ let mode = installGuideMode({ canPrompt, setupComplete: done, requested: installGuideRequested(location.search), vendor: navigator.vendor });
55
+ // The main UI opens the dialog only for the mark this leaves: where it cannot be left, the button stays.
56
+ if (mode === 'handoff' && !leavePwaInstallHandoff()) mode = 'button';
57
+ if (mode === 'wizard' || mode === 'handoff') return location.replace('/');
58
+ document.getElementById(mode === 'button' ? 'install-chromium' : `install-${mode}`).hidden = false;
59
+ document.getElementById('install-button').hidden = mode !== 'button';
60
+ document.querySelector('main').hidden = false;
61
+ };
62
+ if (canPrompt) setupComplete().then(show);
63
+ else show(null);
34
64
  }
@@ -2,9 +2,9 @@
2
2
  #pwa-install { display: grid; place-items: center; overflow: auto; padding: 24px; color: var(--t-bright); }
3
3
  #pwa-install main { width: min(560px, 100%); padding: 24px; background: var(--s-panel); border: 1px solid var(--b-light); border-radius: var(--r-card); }
4
4
  #pwa-install h1 { font-family: var(--ff-heading); font-size: calc(28px * var(--font-scale)); font-weight: 600; line-height: 1.2; margin: 16px 0; }
5
- #pwa-install p, #pwa-install li { font-size: var(--fs-sm); line-height: 1.5; }
6
- #pwa-install ol { padding-left: 24px; margin: 16px 0; }
7
- #pwa-install li + li { margin-top: 8px; }
5
+ #pwa-install [hidden] { display: none !important; }
6
+ #pwa-install p { font-size: var(--fs-sm); line-height: 1.5; }
7
+ #pwa-install p + p { margin-top: 16px; }
8
8
  #pwa-install .actions { display: flex; flex-wrap: wrap; align-items: center; gap: 16px; margin-top: 24px; }
9
9
  #pwa-install button { background: var(--s-active); border: 1px solid var(--b-visible); border-radius: var(--r-small); color: var(--t-bright); font: inherit; padding: 8px 12px; cursor: pointer; }
10
10
  #pwa-install button:hover { background: var(--s-hover); }
@@ -1,9 +1,14 @@
1
- /** Shared by the existing Install as App menu and the native launcher's first-run page. */
1
+ /** Shared by the Install as App menu, the first-run wizard, the native launcher's guide page and the main UI it hands over to. */
2
2
  export function isStandalone(win = window, nav = navigator) {
3
3
  return win.matchMedia('(display-mode: standalone)').matches
4
4
  || win.matchMedia('(display-mode: window-controls-overlay)').matches || !!nav.standalone;
5
5
  }
6
6
 
7
+ /** Chromium only: Safari and Firefox have no install event, so there is nothing a page can open there. */
8
+ export function canPromptPwaInstall(win = window) {
9
+ return 'onbeforeinstallprompt' in win;
10
+ }
11
+
7
12
  export function initPwaInstall({ win = window, nav = navigator, changed = () => {} } = {}) {
8
13
  win.addEventListener('beforeinstallprompt', event => {
9
14
  event.preventDefault();
@@ -26,3 +31,97 @@ export async function requestPwaInstall(win = window) {
26
31
  win._pwaInstallPrompt = null; // Browser install events are single-use, including dismissal.
27
32
  return outcome;
28
33
  }
34
+
35
+ const OFFERED_KEY = 'synabun-pwa-install-offered';
36
+
37
+ function wasOffered(win) {
38
+ if (win._pwaInstallOffered) return true;
39
+ try { return win.sessionStorage.getItem(OFFERED_KEY) === '1'; } catch { return false; }
40
+ }
41
+
42
+ /**
43
+ * The first-run wizard asks nothing itself: it opens the browser's own install dialog once, from a click the
44
+ * user makes anyway. Call this synchronously in that click, before any await: prompt() needs the click's user
45
+ * activation and uses it up, so never call it from a click that opens a popup, the clipboard or a file picker.
46
+ * Nobody waits for the answer. Without an event it does nothing and the next click tries again; once the dialog
47
+ * has opened, this tab never opens it again (sessionStorage: a reload of the wizard does not ask twice).
48
+ */
49
+ export function offerPwaInstallOnce({ win = window, nav = navigator } = {}) {
50
+ const prompt = win._pwaInstallPrompt;
51
+ if (!prompt || isStandalone(win, nav) || wasOffered(win)) return false;
52
+ if (nav.userActivation && !nav.userActivation.isActive) return false; // A scripted click: keep the event for a real one.
53
+ win._pwaInstallPrompt = null; // Single-use, whatever the user answers.
54
+ try { Promise.resolve(prompt.prompt()).catch(() => {}); } catch { return false; }
55
+ win._pwaInstallOffered = true;
56
+ try { win.sessionStorage.setItem(OFFERED_KEY, '1'); } catch {}
57
+ return true;
58
+ }
59
+
60
+ const HANDOFF_KEY = 'synabun-pwa-install-handoff';
61
+
62
+ /**
63
+ * The guide page's half of the handoff: it leaves a mark for the main UI and goes there instead of asking.
64
+ * sessionStorage, so the mark is this tab's alone. False when it could not be left: the guide stays then.
65
+ */
66
+ export function leavePwaInstallHandoff(win = window) {
67
+ try { win.sessionStorage.setItem(HANDOFF_KEY, '1'); return true; } catch { return false; }
68
+ }
69
+
70
+ // A press that follows a link, or belongs to a shortcut (reload, new tab, DevTools), would spend the one dialog
71
+ // on a page that is leaving or busy. The next press gets it.
72
+ function keepsItsPress(event) {
73
+ return !!(event.ctrlKey || event.metaKey || event.altKey) || /^F\d+$/.test(event.key || '')
74
+ || !!event.target?.closest?.('a[href]');
75
+ }
76
+
77
+ /**
78
+ * The main UI's half. It takes the mark, so only the one page load the guide handed over is armed: not a
79
+ * reload, and never a normal tab. Armed, the first click or key press anywhere opens the browser's dialog
80
+ * through offerPwaInstallOnce(), in the capture phase because prompt() must run inside the press and the UI
81
+ * stops many of its events. Without an event yet the next press tries. `leave` names presses to keep out of
82
+ * it (the menu item that opens the dialog itself). Returns the function that disarms it.
83
+ */
84
+ export function armPwaInstallHandoff({ win = window, nav = navigator, leave = () => false } = {}) {
85
+ let marked = false;
86
+ try {
87
+ marked = win.sessionStorage.getItem(HANDOFF_KEY) === '1';
88
+ win.sessionStorage.removeItem(HANDOFF_KEY);
89
+ } catch {}
90
+ if (!marked || isStandalone(win, nav) || wasOffered(win)) return () => {};
91
+ const disarm = () => ['click', 'keydown'].forEach(type => win.removeEventListener(type, press, true));
92
+ const press = event => {
93
+ if (keepsItsPress(event) || leave(event)) return;
94
+ if (offerPwaInstallOnce({ win, nav })) disarm();
95
+ };
96
+ ['click', 'keydown'].forEach(type => win.addEventListener(type, press, true));
97
+ return disarm;
98
+ }
99
+
100
+ const GUIDE_URL = '/install-app.html';
101
+
102
+ /**
103
+ * Where Apps > Install as App goes when the browser has no dialog ready. The mark says the user asked for the
104
+ * page, so it shows instead of handing over. Safari can add the page it is on to the Dock and the launcher
105
+ * matches that URL without a query, so only a browser with the install event gets the mark.
106
+ */
107
+ export function installGuideUrl(win = window) {
108
+ return canPromptPwaInstall(win) ? `${GUIDE_URL}?from=menu` : GUIDE_URL;
109
+ }
110
+
111
+ export function installGuideRequested(search = '') {
112
+ return new URLSearchParams(search).get('from') === 'menu';
113
+ }
114
+
115
+ /**
116
+ * What the launcher's guide page is, for this browser and this install:
117
+ * 'app' (already the installed app: leave), 'wizard' (first run where the wizard opens the dialog: leave),
118
+ * 'handoff' (setup done and nobody asked for the page: leave, the main UI opens the dialog),
119
+ * 'button' (one button that opens the browser's dialog: the page was asked for, or the setup read did not
120
+ * answer), 'safari' or 'other' (plain instructions).
121
+ */
122
+ export function installGuideMode({ standalone = false, canPrompt = false, setupComplete = null, requested = false, vendor = '' } = {}) {
123
+ if (standalone) return 'app';
124
+ if (!canPrompt) return /apple/i.test(vendor) ? 'safari' : 'other';
125
+ if (setupComplete === false) return 'wizard';
126
+ return setupComplete === true && !requested ? 'handoff' : 'button';
127
+ }
@@ -0,0 +1,271 @@
1
+ // ═══════════════════════════════════════════
2
+ // SETTINGS — About (the pane)
3
+ // ═══════════════════════════════════════════
4
+ //
5
+ // The last page of Settings: who SynaBun is and what it runs on. A header that is always shown (the app's icon,
6
+ // name, version and channel, how it was installed, the package's one-line description, "Check for updates" and
7
+ // "Copy info"), then three sections: Links, System, Dependencies, and the license line at the foot.
8
+ //
9
+ // It asks GET /api/system/about once, when the page is first shown. A server from before that route (404, or not
10
+ // JSON) gets the header and the version from /api/health and /api/system/version, and a note that the rest comes
11
+ // with a restart. settings-about.js decides what is said; this draws it.
12
+ //
13
+ // "Check for updates" is the Apps menu's own item (#menu-check-cli-updates, ui-menubar.js): the same check, the
14
+ // same toast. Links open in a new window, which the desktop app hands to the default browser.
15
+
16
+ import * as kit from './settings-kit.js';
17
+ import { revealSettingsTarget } from './settings-shell.js';
18
+ import { desktopBridge } from '../desktop-bridge.js';
19
+ import {
20
+ ABOUT_URL, ABOUT_FILTER_MIN, buildLabel, diagnosticsText, filterDependencies, installLine, isExternalUrl, linkHost, linkLabel,
21
+ npmUrl, osLine, packageLabel, readAboutAnswer, readClientRuntime, versionLine,
22
+ } from './settings-about.js';
23
+
24
+ const UPDATE_MENU_ITEM = 'menu-check-cli-updates';
25
+ const EXTERNAL = 'target="_blank" rel="noopener noreferrer"';
26
+ const ARROW = '<svg class="stg-about-arrow" viewBox="0 0 16 16" aria-hidden="true"><path d="M5.5 4.5h6v6M11.5 4.5l-7 7"/></svg>';
27
+
28
+ export function buildAboutTab() {
29
+ return `
30
+ <div class="stg-pane stg-about" data-stg-pane="about">
31
+ <div class="stg-about-hero" id="stg-about-hero">
32
+ <img class="stg-about-logo" src="/icon-192.png" alt="" width="64" height="64" decoding="async">
33
+ <h3 class="stg-about-name">SynaBun</h3>
34
+ <p class="stg-about-version" id="stg-about-version" aria-live="polite">${kit.te('settings.redesign.about.loading')}</p>
35
+ <p class="stg-about-line" id="stg-about-install" hidden></p>
36
+ <p class="stg-about-line stg-about-desc" id="stg-about-desc" hidden></p>
37
+ <div class="stg-action-row stg-about-actions">
38
+ <button type="button" class="stg-action-btn" id="stg-about-update" ${kit.inv('ABT001')} title="${kit.H('ABT001')}">${kit.L('ABT001')}</button>
39
+ <button type="button" class="stg-action-btn" id="stg-about-copy" ${kit.inv('ABT002')} title="${kit.H('ABT002')}">${kit.L('ABT002')}</button>
40
+ </div>
41
+ <p class="stg-help stg-about-note" id="stg-about-note" role="status" hidden></p>
42
+ </div>
43
+ ${kit.card({
44
+ id: 'stg-sec-about-links', section: 'about.links',
45
+ body: `<ul class="stg-about-links" id="stg-about-links" data-stg-search="off"></ul>`,
46
+ })}
47
+ ${kit.card({
48
+ id: 'stg-sec-about-system', section: 'about.system',
49
+ body: `<dl class="stg-about-facts" id="stg-about-facts" data-stg-search="off"></dl>`,
50
+ })}
51
+ ${kit.card({
52
+ id: 'stg-sec-about-dependencies', section: 'about.dependencies',
53
+ // how many dependencies the lists hold; empty until the server has answered
54
+ status: '<span class="stg-about-count" id="stg-about-deps-count"></span>',
55
+ body: `
56
+ <div class="stg-about-filter" ${kit.inv('ABT003')} hidden>
57
+ <input type="search" class="stg-input stg-about-filter-input" id="stg-about-filter" placeholder="${kit.te('settings.redesign.about.deps.filterPlaceholder')}" aria-label="${kit.L('ABT003')}" aria-describedby="stg-about-filter-help" autocomplete="off" autocapitalize="off" spellcheck="false" enterkeyhint="search">
58
+ <span class="stg-sr" id="stg-about-filter-help">${kit.H('ABT003')}</span>
59
+ </div>
60
+ <div class="stg-about-deps" id="stg-about-deps" data-stg-search="off"></div>`,
61
+ })}
62
+ <footer class="stg-about-footer" id="stg-about-footer" hidden></footer>
63
+ </div>`;
64
+ }
65
+
66
+ // ── Drawing ──
67
+
68
+ function linkRow(link) {
69
+ return `<li><a class="stg-about-link" href="${kit.esc(link.url)}" ${EXTERNAL}>
70
+ <span class="stg-about-link-label">${kit.esc(linkLabel(link, kit.tx))}</span>
71
+ <span class="stg-about-link-url">${kit.esc(linkHost(link.url))}</span>${ARROW}</a></li>`;
72
+ }
73
+
74
+ function fact(label, value, { mono = false } = {}) {
75
+ if (value == null || value === '') return '';
76
+ return `<div class="stg-about-fact"><dt>${label}</dt><dd${mono ? ' class="stg-about-mono"' : ''}>${kit.esc(String(value))}</dd></div>`;
77
+ }
78
+
79
+ function depRow(dep) {
80
+ const url = npmUrl(dep.name);
81
+ const name = url ? `<a class="stg-about-dep-name" href="${kit.esc(url)}" ${EXTERNAL}>${kit.esc(dep.name)}</a>` : `<span class="stg-about-dep-name">${kit.esc(dep.name)}</span>`;
82
+ const version = dep.installed
83
+ ? `<span class="stg-about-dep-version">${kit.esc(dep.version)}</span>`
84
+ : `<span class="stg-about-dep-version" data-state="missing">${kit.esc(dep.declared)} · ${kit.te('settings.redesign.about.deps.notInstalled')}</span>`;
85
+ const meta = [
86
+ dep.installed && dep.declared && dep.declared !== dep.version ? kit.te('settings.redesign.about.deps.declared', { range: dep.declared }) : '',
87
+ dep.optional ? kit.te('settings.redesign.about.deps.optional') : '',
88
+ dep.license ? kit.esc(dep.license) : '',
89
+ ].filter(Boolean).join(' · ');
90
+ return `<li class="stg-about-dep">${name}${version}${meta ? `<span class="stg-about-dep-meta">${meta}</span>` : ''}</li>`;
91
+ }
92
+
93
+ function depGroup(title, sub, deps, { note = '' } = {}) {
94
+ if (!deps.length) return '';
95
+ return `<div class="stg-about-group">
96
+ <h4 class="stg-subhead stg-about-group-head"><span>${title}</span>${sub ? `<span class="stg-about-group-sub">${sub}</span>` : ''}<span class="stg-about-count">${deps.length}</span></h4>
97
+ ${note ? `<p class="stg-help stg-about-group-note">${note}</p>` : ''}
98
+ <ul class="stg-about-dep-list">${deps.map(depRow).join('')}</ul>
99
+ </div>`;
100
+ }
101
+
102
+ function drawDependencies(host, shown) {
103
+ const { packages, vendored, counts } = shown;
104
+ const runtime = packages.map((p) => depGroup(kit.esc(packageLabel(p, kit.tx)), kit.esc(p.name), p.runtime, { note: p.onDemand ? kit.te('settings.redesign.about.deps.onDemand') : '' })).join('');
105
+ const singles = vendored.filter((b) => b.packages.length === 1).flatMap((b) => b.packages);
106
+ const bundles = vendored.filter((b) => b.packages.length > 1)
107
+ .map((b) => depGroup(kit.esc(b.label), kit.esc(b.version || ''), b.packages)).join('');
108
+ const bundled = singles.length || bundles
109
+ ? `<div class="stg-about-bundled">${depGroup(kit.te('settings.redesign.about.deps.vendored'), '', singles)}${bundles}</div>`
110
+ : '';
111
+ // A filter opens the group to show its matches; one the user opened stays open while they type.
112
+ const devOpen = !!shown.query || !!host.querySelector('.stg-about-dev')?.open;
113
+ const dev = counts.dev
114
+ ? `<details class="stg-advanced stg-about-dev"${devOpen ? ' open' : ''}>
115
+ <summary>${kit.CHEVRON}<span>${kit.te('settings.redesign.about.deps.dev', { count: counts.dev })}</span></summary>
116
+ <div class="stg-advanced-body">${packages.map((p) => depGroup(kit.esc(packageLabel(p, kit.tx)), kit.esc(p.name), p.dev)).join('')}</div>
117
+ </details>`
118
+ : '';
119
+ const empty = counts.total ? '' : `<p class="stg-help">${shown.query ? kit.te('settings.redesign.about.deps.noMatch', { query: shown.query }) : kit.te('settings.redesign.about.deps.none')}</p>`;
120
+ host.innerHTML = `${runtime}${bundled}${dev}${empty}`;
121
+ }
122
+
123
+ /**
124
+ * Load and draw the page the first time it is shown, and wire its buttons.
125
+ * @param {HTMLElement} overlay the Settings panel
126
+ * @param {Object} [o]
127
+ * @param {Function} [o.fetchImpl] fetch (tests)
128
+ * @param {(msg: string) => void} [o.toast] the panel's toast
129
+ * @param {Window} [o.win] where the user agent and the desktop app's bridge are read
130
+ */
131
+ export function wireAboutTab(overlay, { fetchImpl = (...args) => fetch(...args), toast = () => {}, win = window } = {}) {
132
+ const pane = kit.paneOf(overlay, 'about');
133
+ if (!pane) return;
134
+ const $ = (id) => pane.querySelector(`#${id}`);
135
+ const client = readClientRuntime({ userAgent: win.navigator?.userAgent, desktop: desktopBridge(win) });
136
+ let model = null;
137
+ let loading = null;
138
+
139
+ const getJson = async (url) => {
140
+ try {
141
+ const res = await fetchImpl(url, { headers: { Accept: 'application/json' } });
142
+ if (!res.ok) return null;
143
+ return await res.json().catch(() => null);
144
+ } catch {
145
+ return null;
146
+ }
147
+ };
148
+
149
+ const show = (node, text) => {
150
+ if (!node) return;
151
+ node.textContent = text || '';
152
+ node.hidden = !text;
153
+ };
154
+
155
+ function drawHeader() {
156
+ const { app } = model;
157
+ show($('stg-about-version'), versionLine(app, kit.tx));
158
+ show($('stg-about-install'), installLine(app, kit.tx));
159
+ show($('stg-about-desc'), app.description);
160
+ show($('stg-about-note'), model.full ? '' : kit.tx('settings.redesign.about.restartNote'));
161
+ }
162
+
163
+ function drawLinks() {
164
+ const host = $('stg-about-links');
165
+ if (!host) return;
166
+ host.innerHTML = model.links.length
167
+ ? model.links.filter((l) => isExternalUrl(l.url)).map(linkRow).join('')
168
+ : `<li class="stg-help stg-about-empty">${model.full ? kit.te('settings.redesign.about.links.none') : kit.te('settings.redesign.about.afterRestart')}</li>`;
169
+ }
170
+
171
+ function drawFacts() {
172
+ const host = $('stg-about-facts');
173
+ if (!host) return;
174
+ const { app, runtime } = model;
175
+ host.innerHTML = [
176
+ fact(kit.te('settings.redesign.about.row.version'), app.version ? `${app.version} (${buildLabel(app, kit.tx)})` : ''),
177
+ fact(kit.te('settings.redesign.about.row.install'), installLine(app, kit.tx)),
178
+ fact('Node.js', runtime.node),
179
+ fact('SQLite', runtime.sqlite),
180
+ fact('Electron', client.electron),
181
+ fact('Chromium', client.chromium),
182
+ fact(kit.te('settings.redesign.about.row.os'), osLine(runtime, client)),
183
+ fact(kit.te('settings.redesign.about.row.dataFolder'), runtime.dataHome, { mono: true }),
184
+ fact(kit.te('settings.redesign.about.row.port'), runtime.port),
185
+ ].join('');
186
+ }
187
+
188
+ function drawDeps() {
189
+ const host = $('stg-about-deps');
190
+ const count = $('stg-about-deps-count');
191
+ const filter = pane.querySelector('.stg-about-filter');
192
+ const deps = model.dependencies;
193
+ if (!host) return;
194
+ if (!deps) {
195
+ host.innerHTML = `<p class="stg-help stg-about-empty">${kit.te('settings.redesign.about.afterRestart')}</p>`;
196
+ if (filter) filter.hidden = true;
197
+ return;
198
+ }
199
+ if (count) count.textContent = deps.counts.total ? String(deps.counts.total) : '';
200
+ if (filter) filter.hidden = deps.counts.total <= ABOUT_FILTER_MIN;
201
+ drawDependencies(host, filterDependencies(deps, $('stg-about-filter')?.value || ''));
202
+ }
203
+
204
+ function drawFooter() {
205
+ const host = $('stg-about-footer');
206
+ if (!host) return;
207
+ const { app } = model;
208
+ const lines = [];
209
+ if (app.license) lines.push(`<p>${kit.te('settings.redesign.about.footer.license', { license: app.license })}</p>`);
210
+ if (app.copyright) lines.push(`<p>${kit.te('settings.redesign.about.footer.copyright', { years: app.copyright.years, holder: app.copyright.holder })}</p>`);
211
+ if (model.dependencies?.counts.total) lines.push(`<p><button type="button" class="stg-about-builtwith" id="stg-about-builtwith">${kit.te('settings.redesign.about.footer.builtWith')}</button></p>`);
212
+ host.innerHTML = lines.join('');
213
+ host.hidden = !lines.length;
214
+ }
215
+
216
+ function draw() {
217
+ drawHeader();
218
+ drawLinks();
219
+ drawFacts();
220
+ drawDeps();
221
+ drawFooter();
222
+ }
223
+
224
+ async function load() {
225
+ const about = await getJson(ABOUT_URL);
226
+ let answer = readAboutAnswer({ about });
227
+ if (!answer.full) {
228
+ // An older server: what it has always said.
229
+ const [health, version] = await Promise.all([getJson('/api/health'), getJson('/api/system/version')]);
230
+ answer = readAboutAnswer({ health, version });
231
+ }
232
+ if (!document.body.contains(overlay)) return;
233
+ model = answer;
234
+ draw();
235
+ }
236
+
237
+ const ensure = () => { if (!loading) loading = load(); return loading; };
238
+ if (kit.paneActive(overlay, 'about')) ensure();
239
+ overlay.addEventListener('stg-page-change', (e) => { if (e.detail?.page === 'about') ensure(); });
240
+
241
+ // ── Buttons ──
242
+
243
+ const updateBtn = $('stg-about-update');
244
+ // The Apps menu's own item: no menu (an embed, a test page), no button.
245
+ if (updateBtn && !document.getElementById(UPDATE_MENU_ITEM)) updateBtn.hidden = true;
246
+ updateBtn?.addEventListener('click', () => document.getElementById(UPDATE_MENU_ITEM)?.click());
247
+
248
+ $('stg-about-copy')?.addEventListener('click', async () => {
249
+ if (!model) await ensure();
250
+ const body = diagnosticsText(model, client, { tx: kit.tx });
251
+ try {
252
+ await win.navigator.clipboard.writeText(body);
253
+ toast(kit.tx('settings.redesign.about.copied'));
254
+ } catch {
255
+ toast(kit.tx('settings.redesign.about.copyFailed'));
256
+ }
257
+ });
258
+
259
+ $('stg-about-filter')?.addEventListener('input', () => { if (model) drawDeps(); });
260
+
261
+ pane.addEventListener('click', (e) => {
262
+ if (!e.target.closest?.('#stg-about-builtwith')) return;
263
+ const section = $('stg-sec-about-dependencies');
264
+ const content = overlay.querySelector('.settings-content');
265
+ if (!section || !content) return;
266
+ revealSettingsTarget(overlay, section);
267
+ // Only the page scrolls, never the panel around it.
268
+ const top = section.getBoundingClientRect().top - content.getBoundingClientRect().top + content.scrollTop - 8;
269
+ content.scrollTo({ top: Math.max(0, top), behavior: matchMedia('(prefers-reduced-motion: reduce)').matches ? 'auto' : 'smooth' });
270
+ });
271
+ }