synabun 2.1.0 → 2.2.0

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 (317) hide show
  1. package/CHANGELOG.md +121 -0
  2. package/README.md +1 -1
  3. package/docs/whatsapp.md +11 -11
  4. package/lib/client-config-repair.js +13 -9
  5. package/lib/external-tools.js +6 -5
  6. package/lib/update-snapshot.js +77 -3
  7. package/lib/virustotal.mjs +12 -1
  8. package/mcp-server/dist/index.js +1 -1
  9. package/mcp-server/dist/index.js.map +1 -1
  10. package/mcp-server/dist/services/budget-judgments.d.ts +1 -1
  11. package/mcp-server/dist/services/budget-judgments.d.ts.map +1 -1
  12. package/mcp-server/dist/services/budget-judgments.js +5 -3
  13. package/mcp-server/dist/services/budget-judgments.js.map +1 -1
  14. package/mcp-server/dist/services/identity.d.ts +2 -2
  15. package/mcp-server/dist/services/identity.js +2 -2
  16. package/mcp-server/dist/services/memory-sessions.d.ts +1 -1
  17. package/mcp-server/dist/services/memory-sessions.d.ts.map +1 -1
  18. package/mcp-server/dist/services/memory-sessions.js +28 -2
  19. package/mcp-server/dist/services/memory-sessions.js.map +1 -1
  20. package/mcp-server/dist/services/neural-interface.d.ts +2 -1
  21. package/mcp-server/dist/services/neural-interface.d.ts.map +1 -1
  22. package/mcp-server/dist/services/neural-interface.js.map +1 -1
  23. package/mcp-server/dist/services/server-instructions.d.ts.map +1 -1
  24. package/mcp-server/dist/services/server-instructions.js +2 -1
  25. package/mcp-server/dist/services/server-instructions.js.map +1 -1
  26. package/mcp-server/dist/services/tool-catalog.js +1 -1
  27. package/mcp-server/dist/services/tool-catalog.js.map +1 -1
  28. package/mcp-server/dist/services/tool-list.d.ts +1 -1
  29. package/mcp-server/dist/services/tool-list.d.ts.map +1 -1
  30. package/mcp-server/dist/services/tool-runtime.d.ts +2 -2
  31. package/mcp-server/dist/services/tool-runtime.d.ts.map +1 -1
  32. package/mcp-server/dist/services/tool-runtime.js +1 -1
  33. package/mcp-server/dist/services/tool-runtime.js.map +1 -1
  34. package/mcp-server/dist/services/typesafe-config.js +1 -1
  35. package/mcp-server/dist/services/typesafe-config.js.map +1 -1
  36. package/mcp-server/dist/tools/agents.d.ts +8 -8
  37. package/mcp-server/dist/tools/agents.d.ts.map +1 -1
  38. package/mcp-server/dist/tools/agents.js +10 -9
  39. package/mcp-server/dist/tools/agents.js.map +1 -1
  40. package/mcp-server/dist/tools/choice.d.ts +6 -0
  41. package/mcp-server/dist/tools/choice.d.ts.map +1 -1
  42. package/mcp-server/dist/tools/choice.js +18 -13
  43. package/mcp-server/dist/tools/choice.js.map +1 -1
  44. package/mcp-server/dist/tools/git-tools.js +2 -2
  45. package/mcp-server/dist/tools/git-tools.js.map +1 -1
  46. package/mcp-server/dist/tools/morelogin.d.ts.map +1 -1
  47. package/mcp-server/dist/tools/morelogin.js +9 -6
  48. package/mcp-server/dist/tools/morelogin.js.map +1 -1
  49. package/mcp-server/dist/tools/skill-tools.d.ts +8 -8
  50. package/mcp-server/dist/tools/skill-tools.d.ts.map +1 -1
  51. package/mcp-server/dist/tools/skill-tools.js +11 -8
  52. package/mcp-server/dist/tools/skill-tools.js.map +1 -1
  53. package/mcp-server/package-lock.json +2 -2
  54. package/mcp-server/package.json +1 -1
  55. package/mcp-server/src/index.ts +1 -1
  56. package/mcp-server/src/services/budget-judgments.ts +5 -3
  57. package/mcp-server/src/services/identity.ts +2 -2
  58. package/mcp-server/src/services/memory-sessions.ts +24 -3
  59. package/mcp-server/src/services/neural-interface.ts +2 -1
  60. package/mcp-server/src/services/server-instructions.ts +2 -1
  61. package/mcp-server/src/services/tool-catalog.ts +1 -1
  62. package/mcp-server/src/services/tool-list.ts +1 -1
  63. package/mcp-server/src/services/tool-runtime.ts +2 -2
  64. package/mcp-server/src/services/typesafe-config.ts +1 -1
  65. package/mcp-server/src/tools/agents.ts +10 -9
  66. package/mcp-server/src/tools/choice.ts +17 -12
  67. package/mcp-server/src/tools/git-tools.ts +2 -2
  68. package/mcp-server/src/tools/morelogin.ts +6 -4
  69. package/mcp-server/src/tools/skill-tools.ts +11 -8
  70. package/neural-interface/i18n/en.json +501 -39
  71. package/neural-interface/i18n/pt-BR.json +501 -39
  72. package/neural-interface/lib/agent-sandbox-elevation.js +6 -0
  73. package/neural-interface/lib/antigravity/acp-client-host.js +554 -0
  74. package/neural-interface/lib/antigravity/acp-connection.js +299 -0
  75. package/neural-interface/lib/antigravity/bypass.js +100 -0
  76. package/neural-interface/lib/antigravity/catalog.js +273 -0
  77. package/neural-interface/lib/antigravity/home.js +260 -0
  78. package/neural-interface/lib/antigravity/migrate-gemini.js +139 -0
  79. package/neural-interface/lib/antigravity/panel-bridge.js +1058 -0
  80. package/neural-interface/lib/antigravity/policy-hook.mjs +78 -0
  81. package/neural-interface/lib/antigravity/policy.js +74 -0
  82. package/neural-interface/lib/antigravity/tool-call.js +178 -0
  83. package/neural-interface/lib/antigravity/transcripts.js +252 -0
  84. package/neural-interface/lib/antigravity/usage.js +43 -0
  85. package/neural-interface/lib/assistant-api.js +1 -0
  86. package/neural-interface/lib/assistant-brains/antigravity.js +423 -0
  87. package/neural-interface/lib/assistant-budget-planner.js +7 -3
  88. package/neural-interface/lib/assistant-budget-split.js +42 -15
  89. package/neural-interface/lib/assistant-catalog.js +76 -9
  90. package/neural-interface/lib/assistant-config.js +6 -3
  91. package/neural-interface/lib/assistant-dispatch.js +207 -49
  92. package/neural-interface/lib/assistant-envelope.js +153 -1
  93. package/neural-interface/lib/assistant-evidence.js +80 -0
  94. package/neural-interface/lib/assistant-hidden-models.js +7 -5
  95. package/neural-interface/lib/assistant-persona.js +16 -11
  96. package/neural-interface/lib/assistant-plan-permissions.js +15 -0
  97. package/neural-interface/lib/assistant-router.js +237 -35
  98. package/neural-interface/lib/assistant-runtime.js +158 -12
  99. package/neural-interface/lib/assistant-task-prompt.js +10 -8
  100. package/neural-interface/lib/assistant-tools.js +2 -2
  101. package/neural-interface/lib/assistant-usage.js +29 -0
  102. package/neural-interface/lib/backup-service.js +6 -1
  103. package/neural-interface/lib/browser-guard-cdp.js +5 -4
  104. package/neural-interface/lib/browser-network-guard.js +274 -82
  105. package/neural-interface/lib/browser-tool-policy.js +12 -7
  106. package/neural-interface/lib/claude-agent-bridge.js +7 -0
  107. package/neural-interface/lib/claude-history-cache.js +210 -0
  108. package/neural-interface/lib/claude-model-catalog.js +5 -1
  109. package/neural-interface/lib/cli-install-api.js +1 -1
  110. package/neural-interface/lib/cli-install.js +160 -31
  111. package/neural-interface/lib/cli-resolver.js +9 -2
  112. package/neural-interface/lib/cli-update-planner.js +25 -15
  113. package/neural-interface/lib/codex-rollout-reader.js +52 -0
  114. package/neural-interface/lib/codex-worker-app-server.js +25 -6
  115. package/neural-interface/lib/editor-git-api.js +3 -20
  116. package/neural-interface/lib/exec-loop-command.js +9 -2
  117. package/neural-interface/lib/file-explorer-api.js +74 -40
  118. package/neural-interface/lib/file-explorer-watch.js +9 -3
  119. package/neural-interface/lib/git-bash/install.js +52 -6
  120. package/neural-interface/lib/git-bash/manager.js +59 -17
  121. package/neural-interface/lib/git-bash/pins.js +12 -0
  122. package/neural-interface/lib/git-read.js +2 -2
  123. package/neural-interface/lib/git-status-runner.js +390 -0
  124. package/neural-interface/lib/git-status-watchdog.js +40 -0
  125. package/neural-interface/lib/greeting/budgets.js +1 -0
  126. package/neural-interface/lib/greeting/engine.js +2 -2
  127. package/neural-interface/lib/greeting/sources.js +10 -12
  128. package/neural-interface/lib/greeting/writer.js +1 -1
  129. package/neural-interface/lib/mcp-client-config.js +37 -6
  130. package/neural-interface/lib/mcp-external-servers.js +13 -4
  131. package/neural-interface/lib/mcp-installer.js +2 -1
  132. package/neural-interface/lib/memory-session-capture.js +17 -3
  133. package/neural-interface/lib/morelogin.js +147 -25
  134. package/neural-interface/lib/native-loop-providers.js +420 -21
  135. package/neural-interface/lib/native-loop-runtime.js +23 -2
  136. package/neural-interface/lib/opencode-native-boundary.js +11 -2
  137. package/neural-interface/lib/owner-authority.js +1 -1
  138. package/neural-interface/lib/path-confine.js +12 -9
  139. package/neural-interface/lib/peer-identity.js +32 -2
  140. package/neural-interface/lib/pty-host/core.js +28 -1
  141. package/neural-interface/lib/pty-host/lifetime.js +12 -0
  142. package/neural-interface/lib/pty-host/manager.js +12 -1
  143. package/neural-interface/lib/remote-policy.js +1 -1
  144. package/neural-interface/lib/request-gate.js +3 -0
  145. package/neural-interface/lib/rulesets/api.js +6 -5
  146. package/neural-interface/lib/rulesets/fs-safe.js +1 -1
  147. package/neural-interface/lib/rulesets/installer.js +89 -10
  148. package/neural-interface/lib/rulesets/render.js +1 -1
  149. package/neural-interface/lib/session-cache-builder.js +64 -1
  150. package/neural-interface/lib/skills/catalog.js +5 -1
  151. package/neural-interface/lib/skills/creation-brief.js +1 -1
  152. package/neural-interface/lib/skills/creation-sources.js +19 -1
  153. package/neural-interface/lib/skills/deploy.js +9 -4
  154. package/neural-interface/lib/skills/draft-confinement.js +3 -1
  155. package/neural-interface/lib/skills/eval-confinement.js +3 -1
  156. package/neural-interface/lib/skills/eval-trace.js +16 -0
  157. package/neural-interface/lib/skills/export.js +5 -2
  158. package/neural-interface/lib/skills/known-bridge-digests.json +58 -4
  159. package/neural-interface/lib/skills/ownership.js +1 -1
  160. package/neural-interface/lib/skills/roots.js +10 -3
  161. package/neural-interface/lib/skills/runtime-checks.js +6 -3
  162. package/neural-interface/lib/skills/service.js +15 -7
  163. package/neural-interface/lib/skills/telemetry.js +6 -0
  164. package/neural-interface/lib/synabun-version.js +1 -1
  165. package/neural-interface/lib/system-activity.js +1 -1
  166. package/neural-interface/lib/whatsapp/brain.js +7 -3
  167. package/neural-interface/lib/whatsapp/cards.js +22 -9
  168. package/neural-interface/lib/whatsapp/config.js +1 -1
  169. package/neural-interface/lib/worker-confinement.js +77 -1
  170. package/neural-interface/lib/worker-question.js +74 -0
  171. package/neural-interface/lib/worker-resume.js +13 -4
  172. package/neural-interface/lib/worker-sandbox.js +46 -16
  173. package/neural-interface/package-lock.json +18 -2
  174. package/neural-interface/package.json +2 -1
  175. package/neural-interface/public/onboarding.html +1 -1
  176. package/neural-interface/public/shared/agent-runtime-options.js +89 -11
  177. package/neural-interface/public/shared/agy/agy-bypass.js +47 -0
  178. package/neural-interface/public/shared/agy/agy-client.js +266 -0
  179. package/neural-interface/public/shared/agy/agy-commands.js +120 -0
  180. package/neural-interface/public/shared/agy/agy-copy.js +326 -0
  181. package/neural-interface/public/shared/agy/agy-diff.js +161 -0
  182. package/neural-interface/public/shared/agy/agy-events.js +469 -0
  183. package/neural-interface/public/shared/agy/agy-greeting.js +26 -0
  184. package/neural-interface/public/shared/agy/agy-options.js +232 -0
  185. package/neural-interface/public/shared/agy/agy-panel.js +2543 -0
  186. package/neural-interface/public/shared/agy/agy-render.js +392 -0
  187. package/neural-interface/public/shared/agy/agy-setup.js +149 -0
  188. package/neural-interface/public/shared/agy/agy-styles.js +604 -0
  189. package/neural-interface/public/shared/agy/agy-tabs-logic.js +181 -0
  190. package/neural-interface/public/shared/api.js +4 -4
  191. package/neural-interface/public/shared/assistant/asst-agents-dock.js +3 -4
  192. package/neural-interface/public/shared/assistant/asst-api.js +1 -1
  193. package/neural-interface/public/shared/assistant/asst-brain-picker.js +56 -9
  194. package/neural-interface/public/shared/assistant/asst-budget.js +5 -4
  195. package/neural-interface/public/shared/assistant/asst-control.js +48 -0
  196. package/neural-interface/public/shared/assistant/asst-hosts.js +16 -7
  197. package/neural-interface/public/shared/assistant/asst-limit-dock.js +6 -1
  198. package/neural-interface/public/shared/assistant/asst-limit-wait.js +38 -5
  199. package/neural-interface/public/shared/assistant/asst-panel.js +45 -8
  200. package/neural-interface/public/shared/assistant/asst-render.js +51 -12
  201. package/neural-interface/public/shared/assistant/asst-route.js +237 -57
  202. package/neural-interface/public/shared/assistant/asst-routes-editor.js +9 -8
  203. package/neural-interface/public/shared/assistant/asst-scenes.js +54 -1
  204. package/neural-interface/public/shared/assistant/asst-sidepanel.js +51 -7
  205. package/neural-interface/public/shared/assistant/asst-slash.js +8 -2
  206. package/neural-interface/public/shared/assistant/asst-state.js +76 -9
  207. package/neural-interface/public/shared/assistant/asst-styles.js +34 -0
  208. package/neural-interface/public/shared/assistant/asst-tool-kinds.js +34 -7
  209. package/neural-interface/public/shared/assistant/asst-usage.js +2 -1
  210. package/neural-interface/public/shared/cdx/cdx-output.js +26 -3
  211. package/neural-interface/public/shared/cdx/cdx-panel.js +14 -2
  212. package/neural-interface/public/shared/cdx/cdx-preview.js +12 -0
  213. package/neural-interface/public/shared/cdx/cdx-protocol.js +11 -10
  214. package/neural-interface/public/shared/cdx/cdx-render.js +10 -9
  215. package/neural-interface/public/shared/cdx/cdx-requests.js +11 -1
  216. package/neural-interface/public/shared/cdx/cdx-styles.js +4 -0
  217. package/neural-interface/public/shared/cdx/cdx-tabs.js +75 -5
  218. package/neural-interface/public/shared/cli-install-controller.js +218 -0
  219. package/neural-interface/public/shared/cli-install-view.js +7 -4
  220. package/neural-interface/public/shared/cli-status.js +9 -8
  221. package/neural-interface/public/shared/constants.js +3 -2
  222. package/neural-interface/public/shared/context-menu-model.js +1 -0
  223. package/neural-interface/public/shared/cp/cp-restore.js +162 -0
  224. package/neural-interface/public/shared/cp/cp-tool-cards.js +10 -3
  225. package/neural-interface/public/shared/desktop-bridge.js +17 -0
  226. package/neural-interface/public/shared/desktop-titlebar-view.js +12 -0
  227. package/neural-interface/public/shared/desktop-titlebar.js +13 -4
  228. package/neural-interface/public/shared/desktop-update-view.js +1 -0
  229. package/neural-interface/public/shared/explorer/fx-api.js +4 -4
  230. package/neural-interface/public/shared/explorer/fx-links.js +3 -3
  231. package/neural-interface/public/shared/explorer/fx-logic.js +1 -1
  232. package/neural-interface/public/shared/explorer/fx-main.js +48 -16
  233. package/neural-interface/public/shared/explorer/fx-tree.js +1 -0
  234. package/neural-interface/public/shared/greeting/greeting-card.js +2 -2
  235. package/neural-interface/public/shared/greeting/greeting-view.js +1 -0
  236. package/neural-interface/public/shared/html-shell.js +21 -4
  237. package/neural-interface/public/shared/ocp-v2/ocp-v2-panel.js +27 -7
  238. package/neural-interface/public/shared/ocp-v2/ocp-v2-rehydrate.js +13 -4
  239. package/neural-interface/public/shared/ocp-v2/ocp-v2-render.js +7 -1
  240. package/neural-interface/public/shared/ocp-v2/ocp-v2-styles.js +17 -0
  241. package/neural-interface/public/shared/ocp-v2/ocp-v2-ws.js +46 -3
  242. package/neural-interface/public/shared/provider-icons.js +4 -2
  243. package/neural-interface/public/shared/settings/settings-rules.js +2 -2
  244. package/neural-interface/public/shared/settings/settings-skill-folders-pane.js +1 -1
  245. package/neural-interface/public/shared/settings/settings-skill-folders.js +2 -2
  246. package/neural-interface/public/shared/showcase-privacy.js +6 -1
  247. package/neural-interface/public/shared/sidepanel-session-status.js +3 -3
  248. package/neural-interface/public/shared/skills/ss-creation.js +6 -3
  249. package/neural-interface/public/shared/skills/ss-dialog.js +5 -2
  250. package/neural-interface/public/shared/skills/ss-lint.js +9 -1
  251. package/neural-interface/public/shared/skills/ss-profiles.js +44 -6
  252. package/neural-interface/public/shared/skills/ss-review-view.js +2 -2
  253. package/neural-interface/public/shared/skills/studio/sx-actions.js +1 -1
  254. package/neural-interface/public/shared/skills/studio/sx-create-ai-view.js +1 -1
  255. package/neural-interface/public/shared/skills/studio/sx-create-ai.js +2 -1
  256. package/neural-interface/public/shared/skills/studio/sx-dom.js +2 -1
  257. package/neural-interface/public/shared/skills/studio/sx-fixes-view.js +359 -0
  258. package/neural-interface/public/shared/skills/studio/sx-fixes.js +327 -0
  259. package/neural-interface/public/shared/skills/studio/sx-inspector.js +1 -1
  260. package/neural-interface/public/shared/skills/studio/sx-install-flow.js +9 -6
  261. package/neural-interface/public/shared/skills/studio/sx-install.js +20 -3
  262. package/neural-interface/public/shared/skills/studio/sx-library-view.js +144 -35
  263. package/neural-interface/public/shared/skills/studio/sx-library.js +6 -2
  264. package/neural-interface/public/shared/skills/studio/sx-main.js +11 -2
  265. package/neural-interface/public/shared/skills/studio/sx-review-view.js +1 -1
  266. package/neural-interface/public/shared/skills/studio/sx-subject.js +7 -6
  267. package/neural-interface/public/shared/skills/studio/sx-workspace-view.js +6 -5
  268. package/neural-interface/public/shared/state.js +1 -1
  269. package/neural-interface/public/shared/streaming-markdown.js +58 -0
  270. package/neural-interface/public/shared/styles.css +354 -34
  271. package/neural-interface/public/shared/text-cache.js +29 -0
  272. package/neural-interface/public/shared/tool-list-picker.js +2 -2
  273. package/neural-interface/public/shared/tool-list-view.js +2 -2
  274. package/neural-interface/public/shared/topright-overflow-logic.js +54 -0
  275. package/neural-interface/public/shared/ui-antigravity-panel.js +19 -0
  276. package/neural-interface/public/shared/ui-assistant.js +2 -0
  277. package/neural-interface/public/shared/ui-automation-studio.js +20 -12
  278. package/neural-interface/public/shared/ui-claude-panel.js +299 -77
  279. package/neural-interface/public/shared/ui-cli-banner.js +23 -167
  280. package/neural-interface/public/shared/ui-file-explorer.js +48 -6
  281. package/neural-interface/public/shared/ui-file-links.js +2 -2
  282. package/neural-interface/public/shared/ui-games.js +2 -2
  283. package/neural-interface/public/shared/ui-judgments.js +1 -1
  284. package/neural-interface/public/shared/ui-keybinds.js +8 -2
  285. package/neural-interface/public/shared/ui-link.js +3 -3
  286. package/neural-interface/public/shared/ui-menubar.js +2 -2
  287. package/neural-interface/public/shared/ui-native-loop-router.js +4 -0
  288. package/neural-interface/public/shared/ui-navbar.js +45 -0
  289. package/neural-interface/public/shared/ui-notifications.js +1 -0
  290. package/neural-interface/public/shared/ui-resume.js +9 -7
  291. package/neural-interface/public/shared/ui-schedules-studio.js +41 -10
  292. package/neural-interface/public/shared/ui-scroll-follow.js +16 -5
  293. package/neural-interface/public/shared/ui-sessions.js +22 -12
  294. package/neural-interface/public/shared/ui-settings.js +90 -65
  295. package/neural-interface/public/shared/ui-sidepanel-session-entry.js +9 -2
  296. package/neural-interface/public/shared/ui-sidepanel-sessions.js +3 -3
  297. package/neural-interface/public/shared/ui-sidepanel-tray.js +48 -7
  298. package/neural-interface/public/shared/ui-sidepanel-windows.js +3 -3
  299. package/neural-interface/public/shared/ui-terminal.js +146 -10
  300. package/neural-interface/public/shared/ui-tool-updates.js +3 -3
  301. package/neural-interface/public/shared/ui-tooltip.js +71 -46
  302. package/neural-interface/public/shared/ui-topright-overflow.js +233 -0
  303. package/neural-interface/public/shared/ui-whiteboard.js +1 -1
  304. package/neural-interface/public/shared/ui-worker-end.js +1 -1
  305. package/neural-interface/public/shared/whatsapp/wa-view.js +2 -2
  306. package/neural-interface/public/shared/worker-questions.js +34 -0
  307. package/neural-interface/public/sidepanel-session.html +1 -1
  308. package/neural-interface/server.js +644 -426
  309. package/neural-interface/templates/rulesets/README.md +1 -1
  310. package/neural-interface/templates/rulesets/legacy-hashes.json +5 -5
  311. package/neural-interface/templates/rulesets/manifest.json +1 -1
  312. package/npm-shrinkwrap.json +18 -2
  313. package/package.json +2 -1
  314. package/skills/skill-forge/SKILL.md +4 -4
  315. package/skills/synabun/SKILL.md +1 -1
  316. package/skills/synabun/modules/schedule.md +3 -3
  317. /package/neural-interface/templates/rulesets/{gemini.md → antigravity.md} +0 -0
@@ -12,8 +12,9 @@
12
12
  // (OpenCode); the panel element gets `<prefix>-cli-blocked` while sending is
13
13
  // off.
14
14
  //
15
- // What it says is cli-install-view.js (DOM-free, tested under node); the data
16
- // and the install stream are cli-status.js. A panel mounts one controller:
15
+ // What it says is cli-install-view.js (DOM-free, tested under node); the state
16
+ // (the answer, the install, Re-check, Add to PATH) is cli-install-controller.js,
17
+ // which the Antigravity panel's setup card uses too. A panel mounts one banner:
17
18
  //
18
19
  // const cli = mountCliBanner({ toolKey: 'codex', prefix: 'cxp',
19
20
  // panel: () => panelEl, anchor: () => messagesContainer,
@@ -23,16 +24,12 @@
23
24
  // cli.nudge(); a send that was held back
24
25
  // cli.flagFailure(text); a session could not start the CLI
25
26
 
26
- import { subscribeCliStatus, recheckCliStatus, getCliDocUrl, getCliInstallCommand, getCliLabel, installCli, followCliInstall, getCliInstallJobs, addCliToPath } from './cli-status.js';
27
- import { cliBannerView, cliInstallText, cliStateOf, reduceInstallJob, setCliInstallTranslator, INSTALLABLE_CLIS } from './cli-install-view.js';
27
+ import { cliInstallText, setCliInstallTranslator } from './cli-install-view.js';
28
+ import { createCliInstallController, DONE_NOTICE_MS } from './cli-install-controller.js';
28
29
  import { t } from './i18n.js';
29
30
 
30
31
  setCliInstallTranslator((key, params) => t(`cliInstall.${key}`, params));
31
32
 
32
- /** How long the note about where an install put the CLI stays, when it has one. */
33
- const DONE_NOTICE_MS = 12000;
34
- const NUDGE_MS = 700;
35
-
36
33
  /**
37
34
  * @param {object} o
38
35
  * @param {'claude-code' | 'codex' | 'opencode'} o.toolKey
@@ -44,29 +41,10 @@ const NUDGE_MS = 700;
44
41
  */
45
42
  export function mountCliBanner({ toolKey, prefix, panel, anchor, onBlockedChange = () => {}, doneNoticeMs = DONE_NOTICE_MS } = {}) {
46
43
  const p = String(prefix || 'cp');
47
- let info = null; // this CLI in the latest answer of /api/system/tool-versions (null: none yet)
48
- let forced = null; // { reason }: a session could not start it, and the server was not asked again yet
49
- let job = null; // the install under way or just ended (cli-install-view.js reduceInstallJob)
50
- let installing = false;
51
- let addingPath = false;
52
- let pathError = '';
53
- let looked = false; // asked whether the server is already installing it since it went missing
54
44
  let el = null;
55
- let doneTimer = null;
56
- let nudgeTimer = null;
57
45
  let lastBlocked = null;
58
46
  let destroyed = false;
59
-
60
- const view = () => cliBannerView({
61
- toolKey,
62
- label: getCliLabel(toolKey),
63
- info,
64
- forced,
65
- job,
66
- addingPath, pathError,
67
- guideUrl: getCliDocUrl(toolKey),
68
- installCommand: getCliInstallCommand(toolKey),
69
- }, { t: cliInstallText });
47
+ let controller = null;
70
48
 
71
49
  const cls = (name) => `${p}-cli-banner-${name}`;
72
50
 
@@ -93,10 +71,10 @@ export function mountCliBanner({ toolKey, prefix, panel, anchor, onBlockedChange
93
71
  </div>
94
72
  </div>
95
73
  `;
96
- root.querySelector(`.${cls('install')}`).addEventListener('click', () => { startInstall(); });
97
- root.querySelector(`.${cls('recheck')}`).addEventListener('click', () => { recheck(); });
98
- root.querySelector(`.${cls('path')}`).addEventListener('click', () => { addPath(); });
99
- root.querySelector(`.${cls('dismiss')}`).addEventListener('click', () => { clearDone(); });
74
+ root.querySelector(`.${cls('install')}`).addEventListener('click', () => { controller?.install(); });
75
+ root.querySelector(`.${cls('recheck')}`).addEventListener('click', () => { controller?.recheck(); });
76
+ root.querySelector(`.${cls('path')}`).addEventListener('click', () => { controller?.addPath(); });
77
+ root.querySelector(`.${cls('dismiss')}`).addEventListener('click', () => { controller?.clearDone(); });
100
78
  root.querySelector(`.${cls('copy')}`).addEventListener('click', async (event) => {
101
79
  const button = event.currentTarget;
102
80
  const command = root.querySelector(`.${cls('command')} code`)?.textContent || '';
@@ -117,7 +95,7 @@ export function mountCliBanner({ toolKey, prefix, panel, anchor, onBlockedChange
117
95
  }
118
96
 
119
97
  function paint(v) {
120
- el.className = `${p}-cli-banner ${p}-cli-banner--${v.kind}${nudgeTimer ? ` ${p}-cli-banner--nudge` : ''}`;
98
+ el.className = `${p}-cli-banner ${p}-cli-banner--${v.kind}${v.nudging ? ` ${p}-cli-banner--nudge` : ''}`;
121
99
  el.setAttribute('role', v.live === 'assertive' ? 'alert' : 'status');
122
100
  el.setAttribute('aria-live', v.live);
123
101
  el.querySelector(`.${cls('icon')}`).textContent = v.icon;
@@ -180,7 +158,8 @@ export function mountCliBanner({ toolKey, prefix, panel, anchor, onBlockedChange
180
158
  else link.removeAttribute('href');
181
159
  const recheckButton = el.querySelector(`.${cls('recheck')}`);
182
160
  recheckButton.hidden = !v.recheck;
183
- if (!recheckButton.disabled) recheckButton.textContent = cliInstallText('recheck');
161
+ recheckButton.disabled = !!v.checking;
162
+ recheckButton.textContent = cliInstallText(v.checking ? 'checking' : 'recheck');
184
163
  const pathButton = el.querySelector(`.${cls('path')}`);
185
164
  pathButton.hidden = !v.addPath;
186
165
  pathButton.textContent = v.addPath?.label || '';
@@ -191,8 +170,8 @@ export function mountCliBanner({ toolKey, prefix, panel, anchor, onBlockedChange
191
170
  }
192
171
 
193
172
  function render() {
194
- if (destroyed) return;
195
- const v = view();
173
+ if (destroyed || !controller) return;
174
+ const v = controller.view();
196
175
  const panelEl = panel?.();
197
176
  if (panelEl) panelEl.classList.toggle(`${p}-cli-blocked`, v.blocked);
198
177
  if (v.show && place()) paint(v);
@@ -203,151 +182,28 @@ export function mountCliBanner({ toolKey, prefix, panel, anchor, onBlockedChange
203
182
  }
204
183
  }
205
184
 
206
- function clearDone() {
207
- clearTimeout(doneTimer);
208
- doneTimer = null;
209
- if (job?.phase === 'done') job = null;
210
- render();
211
- }
212
-
213
- function apply(event) {
214
- if (!job && event?.type !== 'start') job = reduceInstallJob(null, { type: 'start' });
215
- job = reduceInstallJob(job, event);
216
- if (event?.type === 'done' && event.ok && event.status && typeof event.status === 'object') {
217
- info = event.status;
218
- forced = null;
219
- }
220
- render();
221
- }
222
-
223
- function settle(final) {
224
- if (job?.phase === 'running' && final) job = reduceInstallJob(job, final);
225
- if (job?.phase === 'done') {
226
- clearTimeout(doneTimer);
227
- const where = job.path;
228
- if (where && (where.onPath === false || where.added === true)) doneTimer = setTimeout(clearDone, doneNoticeMs);
229
- else job = null;
230
- }
231
- render();
232
- }
233
-
234
- async function startInstall() {
235
- if (installing || !INSTALLABLE_CLIS.includes(toolKey)) return;
236
- installing = true;
237
- clearTimeout(doneTimer);
238
- job = reduceInstallJob(null, { type: 'start' });
239
- render();
240
- let final = null;
241
- try {
242
- final = await installCli(toolKey, { onEvent: apply });
243
- } catch (error) {
244
- final = { type: 'done', ok: false, code: 'internal', error: error?.message || null };
245
- } finally {
246
- installing = false;
247
- settle(final);
248
- }
249
- }
250
-
251
- async function addPath() {
252
- if (addingPath) return;
253
- addingPath = true; pathError = ''; render();
254
- try {
255
- const result = await addCliToPath(toolKey);
256
- info = { ...info, pathStatus: { dir: result.dir, onPath: true, canAdd: false } };
257
- job = reduceInstallJob(null, { type: 'done', ok: true, status: info, path: { ...result, added: true } });
258
- settle();
259
- } catch (error) { pathError = error.message; }
260
- finally { addingPath = false; render(); }
261
- }
262
-
263
- // An install the server is already running (another window started it, or this page was reloaded during
264
- // one): followed here as if this banner had started it.
265
- async function followRunning() {
266
- if (installing) return;
267
- const jobs = await getCliInstallJobs();
268
- if (destroyed || installing || jobs?.[toolKey]?.state !== 'running') return;
269
- installing = true;
270
- job = reduceInstallJob(null, { type: 'start' });
271
- render();
272
- let final = null;
273
- try {
274
- final = await followCliInstall(toolKey, { onEvent: apply });
275
- } catch (error) {
276
- final = { type: 'done', ok: false, code: 'connection', error: error?.message || null };
277
- } finally {
278
- installing = false;
279
- settle(final);
280
- }
281
- }
282
-
283
- async function recheck() {
284
- const button = el?.querySelector(`.${cls('recheck')}`);
285
- if (button) { button.disabled = true; button.textContent = cliInstallText('checking'); }
286
- // Asking again starts over: an earlier failure is not repeated over a fresh answer.
287
- if (job?.phase === 'failed') job = null;
288
- try {
289
- const next = await recheckCliStatus(toolKey);
290
- info = next || info;
291
- if (cliStateOf(info) === 'installed' || cliStateOf(info) === 'broken') forced = null;
292
- } catch { /* the banner stays as it is */ }
293
- finally {
294
- if (button?.isConnected) { button.disabled = false; button.textContent = cliInstallText('recheck'); }
295
- render();
296
- }
297
- }
298
-
299
- const unsubscribe = subscribeCliStatus(toolKey, (next) => {
300
- info = next || null;
301
- const state = cliStateOf(info);
302
- if (state === 'installed') {
303
- forced = null;
304
- if (job?.phase === 'failed') job = null;
305
- }
306
- if (state === 'missing') {
307
- if (!looked) { looked = true; followRunning().catch(() => {}); }
308
- } else {
309
- looked = false;
310
- }
311
- render();
312
- });
185
+ controller = createCliInstallController({ toolKey, doneNoticeMs, onChange: () => render() });
186
+ render();
313
187
 
314
188
  return {
315
189
  /** Draw again (the panel was shown, or rebuilt). */
316
190
  refresh: render,
317
191
  /** Sending is held back: the CLI is missing, broken, failed to install, or being installed. */
318
- blocked: () => view().blocked,
192
+ blocked: () => controller.blocked(),
319
193
  /** What the banner shows now (cli-install-view.js cliBannerView). */
320
- view,
194
+ view: () => controller.view(),
321
195
  /** A send was held back: draw attention to the banner for a moment. */
322
- nudge() {
323
- if (!view().show) return;
324
- clearTimeout(nudgeTimer);
325
- nudgeTimer = setTimeout(() => { nudgeTimer = null; render(); }, NUDGE_MS);
326
- render();
327
- },
196
+ nudge: () => controller.nudge(),
328
197
  /**
329
198
  * A session could not start the CLI ("Claude CLI not found", a spawn error): show "failed to start" with its
330
199
  * words at once, then ask the server again; its answer decides what stays.
331
200
  */
332
- flagFailure(reason = null) {
333
- forced = { reason: reason ? String(reason).slice(0, 300) : null };
334
- render();
335
- recheckCliStatus(toolKey)
336
- .then((next) => {
337
- info = next || info;
338
- const state = cliStateOf(info);
339
- if (state === 'installed' || state === 'broken') forced = null;
340
- render();
341
- })
342
- .catch(() => {});
343
- },
201
+ flagFailure: (reason = null) => controller.flagFailure(reason),
344
202
  /** Install from the banner's own button (for a panel's own entry point). */
345
- install: startInstall,
203
+ install: () => controller.install(),
346
204
  destroy() {
347
205
  destroyed = true;
348
- unsubscribe();
349
- clearTimeout(doneTimer);
350
- clearTimeout(nudgeTimer);
206
+ controller.destroy();
351
207
  el?.remove();
352
208
  el = null;
353
209
  },
@@ -18,6 +18,7 @@ import { registerAction } from './ui-keybinds.js';
18
18
  import { storage } from './storage.js';
19
19
  import { sendToPanel as sendToClaudePanel } from './ui-claude-panel.js';
20
20
  import { attachPathToOpencode } from './ui-opencode-panel-v2.js';
21
+ import { attachPathToAntigravity, isAntigravityPanelOpen, toggleAntigravityPanel } from './ui-antigravity-panel.js';
21
22
  import { isCodexPanelOpen, toggleCodexPanel } from './cdx/cdx-panel.js';
22
23
  import { addPathChip as addCodexPathChip } from './cdx/cdx-tabs.js';
23
24
  import { on, emit, state } from './state.js';
@@ -35,6 +36,7 @@ const SEND = {
35
36
  claude: (path) => sendToClaudePanel(path, { asFile: true }),
36
37
  codex: async (path) => { if (!isCodexPanelOpen()) await toggleCodexPanel(); addCodexPathChip(path); },
37
38
  opencode: (path) => attachPathToOpencode(path),
39
+ antigravity: async (path) => { if (!isAntigravityPanelOpen()) await toggleAntigravityPanel(); return attachPathToAntigravity(path); },
38
40
  assistant: (path) => attachToAssistant('assistant', path),
39
41
  };
40
42
  function attachToAssistant(target, filePath) {
@@ -289,6 +291,13 @@ async function showTree() {
289
291
  }
290
292
  }
291
293
 
294
+ // What each scan state says, in words. The header never shows them while a branch is on screen (that would
295
+ // move the row): there they are a dot on the branch button, its tooltip and label, and a line of the git popover.
296
+ const SCAN_STATE_TEXT = { scanning: 'fileExplorer.branch.scanning', 'timed-out': 'fileExplorer.branch.timedOut', failed: 'fileExplorer.branch.scanFailed' };
297
+ function scanStateText(git) {
298
+ return git && SCAN_STATE_TEXT[git.scanState] ? tr(SCAN_STATE_TEXT[git.scanState]) : '';
299
+ }
300
+
292
301
  function updateBranchLabel(git) {
293
302
  _git = git;
294
303
  const btn = $('fe-branch-btn');
@@ -296,15 +305,33 @@ function updateBranchLabel(git) {
296
305
  const badge = $('fe-branch-btn-badge');
297
306
  if (!btn) return;
298
307
 
299
- if (git.isGit && git.branch) {
308
+ const state = SCAN_STATE_TEXT[git.scanState] ? git.scanState : '';
309
+ const message = scanStateText(git);
310
+ const hasBranch = !!(git.isGit && git.branch);
311
+ // Nothing on screen yet (the project's first scan): the state, in one truncated line, where the branch will be.
312
+ const placeholder = !hasBranch && !git.shown && !!message;
313
+ let dot = btn.querySelector('.fe-branch-btn-state');
314
+ if (!dot) { dot = document.createElement('span'); dot.className = 'fe-branch-btn-state'; dot.setAttribute('aria-hidden', 'true'); btn.appendChild(dot); }
315
+ if (state) btn.dataset.scanState = state; else delete btn.dataset.scanState;
316
+ btn.setAttribute('aria-busy', String(state === 'scanning'));
317
+ // Read out, never drawn: only what the owner is waiting for (a first scan, a Refresh), or how one ended.
318
+ const live = $('fe-git-scan-live');
319
+ // A background scan clears it (that is not read out) rather than leave an old sentence there.
320
+ if (live) { const said = git.loud ? message : ''; if (live.textContent !== said) live.textContent = said; }
321
+
322
+ if (hasBranch || placeholder) {
300
323
  btn.style.display = '';
301
- if (nameEl) nameEl.textContent = git.branch;
302
- if (badge) badge.textContent = git.count > 0 ? git.count : '';
303
- btn.setAttribute('aria-label', tr('fileExplorer.branch.label', { branch: git.branch, count: git.count }));
304
- btn.dataset.tooltip = tr('fileExplorer.branch.label', { branch: git.branch, count: git.count });
324
+ btn.classList.toggle('fe-branch-btn--placeholder', placeholder);
325
+ if (nameEl) nameEl.textContent = hasBranch ? git.branch : message;
326
+ if (badge) badge.textContent = hasBranch && git.count > 0 ? git.count : '';
327
+ const label = hasBranch ? tr('fileExplorer.branch.label', { branch: git.branch, count: git.count }) : '';
328
+ const full = label && message ? tr('fileExplorer.branch.withState', { label, state: message }) : label || message;
329
+ btn.setAttribute('aria-label', full);
330
+ btn.dataset.tooltip = full;
305
331
  if (_gitPopoverOpen) renderGitSummary();
306
332
  } else {
307
333
  btn.style.display = 'none';
334
+ btn.classList.remove('fe-branch-btn--placeholder');
308
335
  closeGitPopover();
309
336
  }
310
337
  }
@@ -358,7 +385,8 @@ function openGitPopover() {
358
385
  requestAnimationFrame(() => pop.classList.add('visible'));
359
386
  loadGitBranches();
360
387
  renderGitSummary();
361
- _fx?.refresh();
388
+ // Fresh counts for the popover, without a word in the header: nobody pressed Refresh.
389
+ _fx?.refresh({ quiet: true });
362
390
  }
363
391
 
364
392
  function closeGitPopover() {
@@ -379,6 +407,7 @@ function toggleGitPopover() {
379
407
 
380
408
  // What changed, in words and counts: from the git state the tree already holds.
381
409
  function renderGitSummary() {
410
+ renderGitScanState();
382
411
  const summary = $('fe-git-summary');
383
412
  if (!summary) return;
384
413
  const changes = _fx?.state.git.changed() || [];
@@ -405,6 +434,18 @@ function renderGitSummary() {
405
434
  }
406
435
  }
407
436
 
437
+ // How the last scan went, in the popover (the header has no room for words): a timeout or a failure always,
438
+ // a scan under way only when the owner is waiting for it (the first one, a Refresh).
439
+ function renderGitScanState() {
440
+ const line = $('fe-git-scan-state');
441
+ if (!line) return;
442
+ const state = _git?.scanState;
443
+ const message = state === 'scanning' && !_git.loud ? '' : scanStateText(_git);
444
+ line.textContent = message;
445
+ line.hidden = !message;
446
+ if (message) line.dataset.tone = state; else delete line.dataset.tone;
447
+ }
448
+
408
449
  async function loadGitBranches() {
409
450
  if (!_selectedProject) return;
410
451
  const sel = $('fe-git-branch-select');
@@ -561,6 +602,7 @@ function createGitPopover() {
561
602
  </div>
562
603
  <div class="fe-git-pop-sep"></div>
563
604
  <div class="fe-git-pop-section">
605
+ <div class="fe-git-scan-state" id="fe-git-scan-state" hidden></div>
564
606
  <div class="fe-git-summary" id="fe-git-summary"></div>
565
607
  </div>
566
608
  </div>
@@ -2,8 +2,8 @@
2
2
  // SynaBun Neural Interface — File links in agent messages: the page side
3
3
  // ═══════════════════════════════════════════
4
4
  //
5
- // One click handler for the links in the messages of the four side panels
6
- // (Assistant, Claude, Codex, OpenCode). A link that points at a file the code
5
+ // One click handler for the links in the messages of the five side panels
6
+ // (Assistant, Claude, Codex, OpenCode, Antigravity). A link that points at a file the code
7
7
  // editor can open opens it there (`open-file-editor`), at its line and column;
8
8
  // every other link keeps what its panel did with it. What a target means and
9
9
  // what a click does are decided in file-links.js; this module gives it the
@@ -197,12 +197,12 @@ function detectActiveGame() {
197
197
  // ── SVG icons for opponent picker ──
198
198
  const ICON_CLAUDE = '<svg viewBox="0 0 24 24" fill="currentColor"><path d="M4.709 15.955l4.72-2.647.08-.23-.08-.128H9.2l-.79-.048-2.698-.073-2.339-.097-2.266-.122-.571-.121L0 11.784l.055-.352.48-.321.686.06 1.52.103 2.278.158 1.652.097 2.449.255h.389l.055-.157-.134-.098-.103-.097-2.358-1.596-2.552-1.688-1.336-.972-.724-.491-.364-.462-.158-1.008.656-.722.881.06.225.061.893.686 1.908 1.476 2.491 1.833.365.304.145-.103.019-.073-.164-.274-1.355-2.446-1.446-2.49-.644-1.032-.17-.619a2.97 2.97 0 01-.104-.729L6.283.134 6.696 0l.996.134.42.364.62 1.414 1.002 2.229 1.555 3.03.456.898.243.832.091.255h.158V9.01l.128-1.706.237-2.095.23-2.695.08-.76.376-.91.747-.492.584.28.48.685-.067.444-.286 1.851-.559 2.903-.364 1.942h.212l.243-.242.985-1.306 1.652-2.064.73-.82.85-.904.547-.431h1.033l.76 1.129-.34 1.166-1.064 1.347-.881 1.142-1.264 1.7-.79 1.36.073.11.188-.02 2.856-.606 1.543-.28 1.841-.315.833.388.091.395-.328.807-1.969.486-2.309.462-3.439.813-.042.03.049.061 1.549.146.662.036h1.622l3.02.225.79.522.474.638-.079.485-1.215.62-1.64-.389-3.829-.91-1.312-.329h-.182v.11l1.093 1.068 2.006 1.81 2.509 2.33.127.578-.322.455-.34-.049-2.205-1.657-.851-.747-1.926-1.62h-.128v.17l.444.649 2.345 3.521.122 1.08-.17.353-.608.213-.668-.122-1.374-1.925-1.415-2.167-1.143-1.943-.14.08-.674 7.254-.316.37-.729.28-.607-.461-.322-.747.322-1.476.389-1.924.315-1.53.286-1.9.17-.632-.012-.042-.14.018-1.434 1.967-2.18 2.945-1.726 1.845-.414.164-.717-.37.067-.662.401-.589 2.388-3.036 1.44-1.882.93-1.086-.006-.158h-.055L4.132 18.56l-1.13.146-.487-.456.061-.746.231-.243 1.908-1.312-.006.006z"/></svg>';
199
199
  const ICON_OPENAI = '<svg viewBox="0 0 24 24" fill="currentColor"><path d="M9.205 8.658v-2.26c0-.19.072-.333.238-.428l4.543-2.616c.619-.357 1.356-.523 2.117-.523 2.854 0 4.662 2.212 4.662 4.566 0 .167 0 .357-.024.547l-4.71-2.759a.797.797 0 00-.856 0l-5.97 3.473zm10.609 8.8V12.06c0-.333-.143-.57-.429-.737l-5.97-3.473 1.95-1.118a.433.433 0 01.476 0l4.543 2.617c1.309.76 2.189 2.378 2.189 3.948 0 1.808-1.07 3.473-2.76 4.163zM7.802 12.703l-1.95-1.142c-.167-.095-.239-.238-.239-.428V5.899c0-2.545 1.95-4.472 4.591-4.472 1 0 1.927.333 2.712.928L8.23 5.067c-.285.166-.428.404-.428.737v6.898zM12 15.128l-2.795-1.57v-3.33L12 8.658l2.795 1.57v3.33L12 15.128zm1.796 7.23c-1 0-1.927-.332-2.712-.927l4.686-2.712c.285-.166.428-.404.428-.737v-6.898l1.974 1.142c.167.095.238.238.238.428v5.233c0 2.545-1.974 4.472-4.614 4.472zm-5.637-5.303l-4.544-2.617c-1.308-.761-2.188-2.378-2.188-3.948A4.482 4.482 0 014.21 6.327v5.423c0 .333.143.571.428.738l5.947 3.449-1.95 1.118a.432.432 0 01-.476 0zm-.262 3.9c-2.688 0-4.662-2.021-4.662-4.519 0-.19.024-.38.047-.57l4.686 2.71c.286.167.571.167.856 0l5.97-3.448v2.26c0 .19-.07.333-.237.428l-4.543 2.616c-.619.357-1.356.523-2.117.523zm5.899 2.83a5.947 5.947 0 005.827-4.756C22.287 18.339 24 15.84 24 13.296c0-1.665-.713-3.282-1.998-4.448.119-.5.19-.999.19-1.498 0-3.401-2.759-5.947-5.946-5.947-.642 0-1.26.095-1.88.31A5.962 5.962 0 0010.205 0a5.947 5.947 0 00-5.827 4.757C1.713 5.447 0 7.945 0 10.49c0 1.666.713 3.283 1.998 4.448-.119.5-.19 1-.19 1.499 0 3.401 2.759 5.946 5.946 5.946.642 0 1.26-.095 1.88-.309a5.96 5.96 0 004.162 1.713z"/></svg>';
200
- const ICON_GEMINI = '<svg viewBox="0 0 24 24" fill="currentColor"><path d="M20.616 10.835a14.147 14.147 0 01-4.45-3.001 14.111 14.111 0 01-3.678-6.452.503.503 0 00-.975 0 14.134 14.134 0 01-3.679 6.452 14.155 14.155 0 01-4.45 3.001c-.65.28-1.318.505-2.002.678a.502.502 0 000 .975c.684.172 1.35.397 2.002.677a14.147 14.147 0 014.45 3.001 14.112 14.112 0 013.679 6.453.502.502 0 00.975 0c.172-.685.397-1.351.677-2.003a14.145 14.145 0 013.001-4.45 14.113 14.113 0 016.453-3.678.503.503 0 000-.975 13.245 13.245 0 01-2.003-.678z"/></svg>';
200
+ const ICON_ANTIGRAVITY = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M4.5 20 12 4l7.5 16"/><path d="M8.2 20a3.8 3.8 0 0 1 7.6 0"/></svg>';
201
201
 
202
202
  const OPPONENTS = [
203
203
  { id: 'claude-code', icon: ICON_CLAUDE, label: 'Claude' },
204
204
  { id: 'codex', icon: ICON_OPENAI, label: 'OpenAI' },
205
- { id: 'gemini', icon: ICON_GEMINI, label: 'Gemini' },
205
+ { id: 'antigravity', icon: ICON_ANTIGRAVITY, label: 'Antigravity' },
206
206
  ];
207
207
 
208
208
  // ── Opponent picker overlay ──
@@ -1030,7 +1030,7 @@ export function wireJudgmentsTab(overlay, { toast = () => {}, initial = null } =
1030
1030
  return { line: p == null ? '' : flagged(kit.tx('settings.redesign.judgments.pGoalMet', { pct: pct(p) }), p >= knob('loop-goal', 'minConfidence', 0.8)) };
1031
1031
  }
1032
1032
  function budgetSplitLine(row, answers) {
1033
- const names = { share_claude_code: 'Claude Code', share_codex: 'Codex', share_opencode: 'OpenCode' };
1033
+ const names = { share_claude_code: 'Claude Code', share_codex: 'Codex', share_opencode: 'OpenCode', share_antigravity: 'Antigravity' };
1034
1034
  const parts = Object.entries(names).filter(([key]) => answers[key]?.type === 'score').map(([key, label]) => {
1035
1035
  const a = answers[key];
1036
1036
  return escapeHtml(kit.tx('settings.redesign.judgments.budgetShareScore', { provider: label, score: num(a.score ?? a.value), confidence: pct(a.confidence) }));
@@ -12,7 +12,7 @@ const CLI_ICONS = {
12
12
  assistant: '<svg viewBox="0 0 24 24" fill="currentColor"><rect x="8" y="5.5" width="11" height="5" rx="2.5" transform="rotate(-12 13.5 8)"/><rect x="4.5" y="12.5" width="11" height="5" rx="2.5" transform="rotate(-12 10 15)"/></svg>',
13
13
  claude: '<svg viewBox="0 0 24 24" fill="currentColor"><path d="M4.709 15.955l4.72-2.647.08-.23-.08-.128H9.2l-.79-.048-2.698-.073-2.339-.097-2.266-.122-.571-.121L0 11.784l.055-.352.48-.321.686.06 1.52.103 2.278.158 1.652.097 2.449.255h.389l.055-.157-.134-.098-.103-.097-2.358-1.596-2.552-1.688-1.336-.972-.724-.491-.364-.462-.158-1.008.656-.722.881.06.225.061.893.686 1.908 1.476 2.491 1.833.365.304.145-.103.019-.073-.164-.274-1.355-2.446-1.446-2.49-.644-1.032-.17-.619a2.97 2.97 0 01-.104-.729L6.283.134 6.696 0l.996.134.42.364.62 1.414 1.002 2.229 1.555 3.03.456.898.243.832.091.255h.158V9.01l.128-1.706.237-2.095.23-2.695.08-.76.376-.91.747-.492.584.28.48.685-.067.444-.286 1.851-.559 2.903-.364 1.942h.212l.243-.242.985-1.306 1.652-2.064.73-.82.85-.904.547-.431h1.033l.76 1.129-.34 1.166-1.064 1.347-.881 1.142-1.264 1.7-.79 1.36.073.11.188-.02 2.856-.606 1.543-.28 1.841-.315.833.388.091.395-.328.807-1.969.486-2.309.462-3.439.813-.042.03.049.061 1.549.146.662.036h1.622l3.02.225.79.522.474.638-.079.485-1.215.62-1.64-.389-3.829-.91-1.312-.329h-.182v.11l1.093 1.068 2.006 1.81 2.509 2.33.127.578-.322.455-.34-.049-2.205-1.657-.851-.747-1.926-1.62h-.128v.17l.444.649 2.345 3.521.122 1.08-.17.353-.608.213-.668-.122-1.374-1.925-1.415-2.167-1.143-1.943-.14.08-.674 7.254-.316.37-.729.28-.607-.461-.322-.747.322-1.476.389-1.924.315-1.53.286-1.9.17-.632-.012-.042-.14.018-1.434 1.967-2.18 2.945-1.726 1.845-.414.164-.717-.37.067-.662.401-.589 2.388-3.036 1.44-1.882.93-1.086-.006-.158h-.055L4.132 18.56l-1.13.146-.487-.456.061-.746.231-.243 1.908-1.312-.006.006z"/></svg>',
14
14
  codex: '<svg viewBox="0 0 24 24" fill="currentColor"><path d="M9.205 8.658v-2.26c0-.19.072-.333.238-.428l4.543-2.616c.619-.357 1.356-.523 2.117-.523 2.854 0 4.662 2.212 4.662 4.566 0 .167 0 .357-.024.547l-4.71-2.759a.797.797 0 00-.856 0l-5.97 3.473zm10.609 8.8V12.06c0-.333-.143-.57-.429-.737l-5.97-3.473 1.95-1.118a.433.433 0 01.476 0l4.543 2.617c1.309.76 2.189 2.378 2.189 3.948 0 1.808-1.07 3.473-2.76 4.163zM7.802 12.703l-1.95-1.142c-.167-.095-.239-.238-.239-.428V5.899c0-2.545 1.95-4.472 4.591-4.472 1 0 1.927.333 2.712.928L8.23 5.067c-.285.166-.428.404-.428.737v6.898zM12 15.128l-2.795-1.57v-3.33L12 8.658l2.795 1.57v3.33L12 15.128zm1.796 7.23c-1 0-1.927-.332-2.712-.927l4.686-2.712c.285-.166.428-.404.428-.737v-6.898l1.974 1.142c.167.095.238.238.238.428v5.233c0 2.545-1.974 4.472-4.614 4.472zm-5.637-5.303l-4.544-2.617c-1.308-.761-2.188-2.378-2.188-3.948A4.482 4.482 0 014.21 6.327v5.423c0 .333.143.571.428.738l5.947 3.449-1.95 1.118a.432.432 0 01-.476 0zm-.262 3.9c-2.688 0-4.662-2.021-4.662-4.519 0-.19.024-.38.047-.57l4.686 2.71c.286.167.571.167.856 0l5.97-3.448v2.26c0 .19-.07.333-.237.428l-4.543 2.616c-.619.357-1.356.523-2.117.523zm5.899 2.83a5.947 5.947 0 005.827-4.756C22.287 18.339 24 15.84 24 13.296c0-1.665-.713-3.282-1.998-4.448.119-.5.19-.999.19-1.498 0-3.401-2.759-5.947-5.946-5.947-.642 0-1.26.095-1.88.31A5.962 5.962 0 0010.205 0a5.947 5.947 0 00-5.827 4.757C1.713 5.447 0 7.945 0 10.49c0 1.666.713 3.283 1.998 4.448-.119.5-.19 1-.19 1.499 0 3.401 2.759 5.946 5.946 5.946.642 0 1.26-.095 1.88-.309a5.96 5.96 0 004.162 1.713z"/></svg>',
15
- gemini: '<svg viewBox="0 0 24 24" fill="currentColor"><path d="M20.616 10.835a14.147 14.147 0 01-4.45-3.001 14.111 14.111 0 01-3.678-6.452.503.503 0 00-.975 0 14.134 14.134 0 01-3.679 6.452 14.155 14.155 0 01-4.45 3.001c-.65.28-1.318.505-2.002.678a.502.502 0 000 .975c.684.172 1.35.397 2.002.677a14.147 14.147 0 014.45 3.001 14.112 14.112 0 013.679 6.453.502.502 0 00.975 0c.172-.685.397-1.351.677-2.003a14.145 14.145 0 013.001-4.45 14.113 14.113 0 016.453-3.678.503.503 0 000-.975 13.245 13.245 0 01-2.003-.678z"/></svg>',
15
+ antigravity: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M4.5 20 12 4l7.5 16"/><path d="M8.2 20a3.8 3.8 0 0 1 7.6 0"/></svg>',
16
16
  browser: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><circle cx="12" cy="12" r="10"/><ellipse cx="12" cy="12" rx="4" ry="10"/><line x1="2" y1="12" x2="22" y2="12"/><path d="M4.5 7h15M4.5 17h15" stroke-width="1"/></svg>',
17
17
  youtube: '<svg viewBox="0 0 24 24" fill="currentColor"><path d="M23.498 6.186a3.016 3.016 0 00-2.122-2.136C19.505 3.545 12 3.545 12 3.545s-7.505 0-9.377.505A3.017 3.017 0 00.502 6.186C0 8.07 0 12 0 12s0 3.93.502 5.814a3.016 3.016 0 002.122 2.136c1.871.505 9.376.505 9.376.505s7.505 0 9.377-.505a3.015 3.015 0 002.122-2.136C24 15.93 24 12 24 12s0-3.93-.502-5.814zM9.545 15.568V8.432L15.818 12l-6.273 3.568z"/></svg>',
18
18
  };
@@ -26,7 +26,8 @@ let _recording = null; // { actionId, originalCombo } when recording
26
26
  let _pendingBindings = null; // working copy during modal editing
27
27
  let _modalOpen = false;
28
28
  // Actions that were removed ("toggle-toolbar" was "Hide all toolbars"): a saved key for one is dropped on load.
29
- const RETIRED_ACTIONS = ['toggle-toolbar', 'toggle-minimap'];
29
+ // "launch-gemini" became "launch-antigravity" (Antigravity replaced the Gemini CLI): its key carries over first.
30
+ const RETIRED_ACTIONS = ['toggle-toolbar', 'toggle-minimap', 'launch-gemini'];
30
31
 
31
32
  // ── Combo building ──
32
33
 
@@ -447,6 +448,11 @@ export async function initKeybinds() {
447
448
  const data = await fetchKeybinds();
448
449
  if (data && data.bindings) {
449
450
  _bindings = { ...DEFAULT_KEYBINDS, ...data.bindings };
451
+ // A key saved for the Gemini CLI launches Antigravity, unless Antigravity has one of its own
452
+ // (lib/antigravity/migrate-gemini.js migrateKeybinds does the same to the saved file).
453
+ if (Object.hasOwn(data.bindings, 'launch-gemini') && !Object.hasOwn(data.bindings, 'launch-antigravity')) {
454
+ _bindings['launch-antigravity'] = data.bindings['launch-gemini'];
455
+ }
450
456
  } else {
451
457
  _bindings = { ...DEFAULT_KEYBINDS };
452
458
  }
@@ -3,7 +3,7 @@
3
3
  // ═══════════════════════════════════════════
4
4
  //
5
5
  // Floating Link Panel that mediates turn-based conversations
6
- // between AI agents (Claude Code, Codex, Gemini, etc.) via linked
6
+ // between AI agents (Claude Code, Codex, Antigravity, etc.) via linked
7
7
  // terminal sessions. Users can inject messages, control auto-relay,
8
8
  // and watch agents collaborate in real-time.
9
9
 
@@ -31,7 +31,7 @@ const SVG_COPY = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" str
31
31
  const PROFILE_META = {
32
32
  'claude-code': { label: 'Claude Code', color: '#D4A27F' },
33
33
  'codex': { label: 'Codex CLI', color: '#74c7a5' },
34
- 'gemini': { label: 'Gemini CLI', color: '#669DF6' },
34
+ 'antigravity': { label: 'Antigravity CLI', color: '#669DF6' },
35
35
  'shell': { label: 'Shell', color: '#aaaaaa' },
36
36
  'browser': { label: 'Browser', color: '#4fc3f7' },
37
37
  };
@@ -245,7 +245,7 @@ async function _showSetupView() {
245
245
  <div class="link-panel-empty">
246
246
  <div class="link-panel-empty-icon">${SVG_UNLINK}</div>
247
247
  <p>Open at least 2 terminal sessions to create a link.</p>
248
- <p style="opacity:0.5;font-size:12px;">Use the terminal panel to launch Claude Code, Codex, or Gemini.</p>
248
+ <p style="opacity:0.5;font-size:12px;">Use the terminal panel to launch Claude Code, Codex, or Antigravity.</p>
249
249
  </div>
250
250
  `;
251
251
  return;
@@ -160,7 +160,7 @@ export function initMenubar() {
160
160
  'menu-terminal-assistant': 'terminal',
161
161
  'menu-terminal-claude': 'terminal',
162
162
  'menu-terminal-codex': 'terminal',
163
- 'menu-terminal-gemini': 'terminal',
163
+ 'menu-terminal-antigravity': 'terminal',
164
164
  'menu-terminal-opencode': 'terminal',
165
165
  'menu-terminal-shell': 'terminal',
166
166
  'menu-terminal-browser': 'browser',
@@ -572,7 +572,7 @@ function wireTerminalMenu() {
572
572
  'menu-terminal-assistant': () => emit('assistant:open'),
573
573
  'menu-terminal-claude': () => emit('terminal:open', { profile: 'claude-code' }),
574
574
  'menu-terminal-codex': () => emit('terminal:open', { profile: 'codex' }),
575
- 'menu-terminal-gemini': () => emit('terminal:open', { profile: 'gemini' }),
575
+ 'menu-terminal-antigravity': () => emit('terminal:open', { profile: 'antigravity' }),
576
576
  'menu-terminal-opencode': () => emit('terminal:open', { profile: 'opencode' }),
577
577
  'menu-check-cli-updates': async () => {
578
578
  const [tools, syn] = await Promise.all([
@@ -2,6 +2,7 @@ import { on } from './state.js';
2
2
  import { attachClaudeAutomation, detachClaudeAutomation, handleClaudeAutomationEvent } from './ui-claude-panel.js';
3
3
  import { attachCodexAutomation, detachCodexAutomation, handleCodexAutomationEvent } from './ui-codex-panel.js';
4
4
  import { attachOpenCodeAutomation, detachOpenCodeAutomation } from './ui-opencode-panel-v2.js';
5
+ import { attachAntigravityAutomation, detachAntigravityAutomation, handleAntigravityAutomationEvent } from './ui-antigravity-panel.js';
5
6
  import { nativeLoopWindowId } from './ui-native-window-id.js';
6
7
  import {
7
8
  isNewerRunDescriptor,
@@ -161,6 +162,7 @@ async function attach(run, focus) {
161
162
  }
162
163
  if (provider === 'codex') return attachCodexAutomation(run, options);
163
164
  if (provider === 'opencode') return attachOpenCodeAutomation(run, options);
165
+ if (provider === 'antigravity') return attachAntigravityAutomation(run, options);
164
166
  return { ok: false, reason: 'unsupported_provider' };
165
167
  }
166
168
 
@@ -169,6 +171,7 @@ async function detachProviderRun(runId, provider) {
169
171
  if (provider === 'claude-code' || provider === 'claude') return await detachClaudeAutomation(runId);
170
172
  if (provider === 'codex') return await detachCodexAutomation(runId);
171
173
  if (provider === 'opencode') return await detachOpenCodeAutomation(runId);
174
+ if (provider === 'antigravity') return await detachAntigravityAutomation(runId);
172
175
  } catch {}
173
176
  return false;
174
177
  }
@@ -189,6 +192,7 @@ function deliverProviderEvent(message) {
189
192
  return handleClaudeAutomationEvent(run.runId, payload);
190
193
  }
191
194
  if (payload.provider === 'codex') return handleCodexAutomationEvent(run.runId, payload);
195
+ if (payload.provider === 'antigravity') return handleAntigravityAutomationEvent(run.runId, payload);
192
196
  } catch {}
193
197
  return false;
194
198
  }
@@ -15,6 +15,7 @@ import { startTutorial } from './ui-tutorial.js';
15
15
  import { toggleClaudePanel, isClaudePanelOpen } from './ui-claude-panel.js';
16
16
  import { toggleCodexPanel, isCodexPanelOpen } from './ui-codex-panel.js';
17
17
  import { toggleOpencodePanel, isOpencodePanelOpen } from './ui-opencode-panel-v2.js';
18
+ import { toggleAntigravityPanel, isAntigravityPanelOpen } from './ui-antigravity-panel.js';
18
19
  import { toggleAssistantPanel, isAssistantPanelOpen, initAssistantPanel } from './ui-assistant-panel.js';
19
20
  import { initAssistant } from './ui-assistant.js';
20
21
  import { toggleSessionMonitor, fetchLoopsForBadge, fetchNotificationsForBadge } from './ui-sessions.js';
@@ -25,6 +26,7 @@ import { initToolUpdates } from './ui-tool-updates.js';
25
26
  import { initCrashNotice } from './ui-crash-notice.js';
26
27
  import { initSidepanelTrayPlaceholders } from './ui-sidepanel-tray.js';
27
28
  import { initDesktopTitleBar } from './desktop-titlebar.js';
29
+ import { initToprightOverflow } from './ui-topright-overflow.js';
28
30
 
29
31
  const $ = (id) => document.getElementById(id);
30
32
 
@@ -35,6 +37,7 @@ export function initNavbar() {
35
37
  const claudePanelBtn = $('topright-claude-panel-btn');
36
38
  const codexPanelBtn = $('topright-codex-panel-btn');
37
39
  const opencodePanelBtn = $('topright-opencode-panel-btn');
40
+ const antigravityPanelBtn = $('topright-antigravity-panel-btn');
38
41
  initSidepanelTrayPlaceholders();
39
42
  // The Assistant: shared host wiring (terminal tab + sidepanel), then the
40
43
  // sidepanel host, which restores its saved tabs as tray pills.
@@ -62,6 +65,11 @@ export function initNavbar() {
62
65
  toggle: toggleOpencodePanel,
63
66
  button: opencodePanelBtn,
64
67
  },
68
+ antigravity: {
69
+ isOpen: isAntigravityPanelOpen,
70
+ toggle: toggleAntigravityPanel,
71
+ button: antigravityPanelBtn,
72
+ },
65
73
  };
66
74
 
67
75
  function syncAgentPanelButtons() {
@@ -113,6 +121,16 @@ export function initNavbar() {
113
121
  syncAgentPanelButtons();
114
122
  });
115
123
  }
124
+ if (antigravityPanelBtn) {
125
+ antigravityPanelBtn.addEventListener('click', async (e) => {
126
+ e.preventDefault();
127
+ await toggleAgentPanel('antigravity');
128
+ });
129
+ on('antigravity-panel:visibility', (visible) => {
130
+ antigravityPanelBtn.classList.toggle('active', !!visible);
131
+ syncAgentPanelButtons();
132
+ });
133
+ }
116
134
  on('claude-panel:visibility', () => syncAgentPanelButtons());
117
135
  on('opencode-child:visibility', () => syncAgentPanelButtons());
118
136
 
@@ -328,6 +346,33 @@ export function initNavbar() {
328
346
  });
329
347
  }
330
348
 
349
+ // ── The whiteboard rail toggle (#wb-hide-btn ↔ #toolbar-show-btn) ──
350
+ // Hides the whiteboard tool rail (#wb-toolbar, Focus Mode) behind a SynaBun logo button above the console button
351
+ // (#term-peek-dock). The workspace toolbar and the session pills (#term-minimized-tray) stay. Remembered across reloads.
352
+ const wbRail = $('wb-toolbar');
353
+ const hideBtn = $('wb-hide-btn');
354
+ const showBtn = $('toolbar-show-btn');
355
+ if (wbRail && hideBtn && showBtn) {
356
+ const setRailHidden = (hidden) => {
357
+ wbRail.classList.toggle('wb-rail-hidden', hidden);
358
+ wbRail.toggleAttribute('inert', hidden);
359
+ hideBtn.setAttribute('aria-pressed', String(hidden));
360
+ showBtn.setAttribute('aria-pressed', String(!hidden));
361
+ showBtn.hidden = !hidden;
362
+ };
363
+ setRailHidden(storage.getItem(KEYS.WHITEBOARD_TOOLBAR_HIDDEN) === 'true');
364
+ const toggle = (hidden) => {
365
+ setRailHidden(hidden);
366
+ storage.setItem(KEYS.WHITEBOARD_TOOLBAR_HIDDEN, String(hidden));
367
+ (hidden ? showBtn : hideBtn).focus({ preventScroll: true });
368
+ };
369
+ hideBtn.addEventListener('click', () => toggle(true));
370
+ showBtn.addEventListener('click', () => toggle(false));
371
+ }
372
+
373
+ // ── One row, always: what does not fit the band goes into the ⋯ menu ──
374
+ initToprightOverflow();
375
+
331
376
  // ── Session pills in the row: the whole name is the tooltip ──
332
377
  // Laid out in a row, a pill keeps its width on hover (styles.css): widening, it would push the others onto another
333
378
  // line under the pointer. A name cut short is read from the tooltip instead; a pill that brings a tooltip of its own
@@ -49,6 +49,7 @@ const PANEL_SHOW_EVENTS = {
49
49
  claude: 'claude-panel:show',
50
50
  codex: 'codex-panel:show',
51
51
  opencode: 'opencode-panel:show',
52
+ antigravity: 'antigravity-panel:show',
52
53
  assistant: 'assistant-panel:show',
53
54
  };
54
55