synabun 2.0.0 → 2.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +57 -0
- package/NOTICE +5 -0
- package/README.md +1 -1
- package/THIRD-PARTY-LICENSES.md +46 -0
- package/benchmarks/fixtures/browser/README.md +1 -1
- package/benchmarks/fixtures/desktop/README.md +1 -1
- package/docs/whatsapp.md +2 -2
- package/hooks/claude-code/pre-compact.mjs +11 -0
- package/hooks/claude-code/prompt-submit.mjs +60 -184
- package/hooks/claude-code/session-start.mjs +73 -16
- package/hooks/claude-code/shared.mjs +20 -0
- package/hooks/claude-code/stop.mjs +5 -5
- package/lib/start-launcher.js +88 -1
- package/mcp-server/package-lock.json +2 -2
- package/mcp-server/package.json +1 -3
- package/neural-interface/i18n/en.json +1500 -153
- package/neural-interface/i18n/pt-BR.json +1498 -152
- package/neural-interface/lib/assistant-api.js +62 -6
- package/neural-interface/lib/assistant-brains/claude.js +4 -0
- package/neural-interface/lib/assistant-catalog.js +179 -27
- package/neural-interface/lib/assistant-config.js +31 -2
- package/neural-interface/lib/assistant-dispatch.js +62 -13
- package/neural-interface/lib/assistant-hidden-models.js +393 -66
- package/neural-interface/lib/assistant-memory.js +24 -10
- package/neural-interface/lib/assistant-persona.js +6 -2
- package/neural-interface/lib/assistant-pricing.js +21 -6
- package/neural-interface/lib/assistant-runtime.js +181 -12
- package/neural-interface/lib/assistant-task-prompt.js +13 -3
- package/neural-interface/lib/assistant-test-density.js +142 -0
- package/neural-interface/lib/assistant-user-context-budgets.js +123 -0
- package/neural-interface/lib/assistant-user-context.js +908 -0
- package/neural-interface/lib/assistant-user-knowledge.js +328 -0
- package/neural-interface/lib/assistant-user-learn.js +404 -0
- package/neural-interface/lib/assistant-user-threads.js +226 -0
- package/neural-interface/lib/assistant-user-writer.js +181 -0
- package/neural-interface/lib/browser-background-tab.js +134 -0
- package/neural-interface/lib/browser-screenshot.js +678 -61
- package/neural-interface/lib/claude-agent-bridge.js +33 -6
- package/neural-interface/lib/claude-model-catalog.js +30 -2
- package/neural-interface/lib/claude-temporary.js +46 -7
- package/neural-interface/lib/cloudflared/install.js +533 -0
- package/neural-interface/lib/cloudflared/manager.js +545 -0
- package/neural-interface/lib/cloudflared/pins.js +115 -0
- package/neural-interface/lib/cloudflared/pins.json +63 -0
- package/neural-interface/lib/cloudflared/tunnel.js +265 -0
- package/neural-interface/lib/editor-completion-endpoint.js +264 -0
- package/neural-interface/lib/editor-completion-opencode.js +436 -0
- package/neural-interface/lib/editor-completion.js +714 -0
- package/neural-interface/lib/editor-config.js +149 -0
- package/neural-interface/lib/editor-files-api.js +268 -0
- package/neural-interface/lib/editor-git-api.js +134 -0
- package/neural-interface/lib/file-explorer-api.js +1039 -0
- package/neural-interface/lib/file-explorer-system.js +355 -0
- package/neural-interface/lib/file-explorer-trash.ps1 +85 -0
- package/neural-interface/lib/file-explorer-watch.js +239 -0
- package/neural-interface/lib/git-diff-api.js +103 -0
- package/neural-interface/lib/git-read.js +202 -0
- package/neural-interface/lib/greeting/api.js +89 -0
- package/neural-interface/lib/greeting/budgets.js +90 -0
- package/neural-interface/lib/greeting/engine.js +461 -0
- package/neural-interface/lib/greeting/facts.js +320 -0
- package/neural-interface/lib/greeting/sources.js +252 -0
- package/neural-interface/lib/greeting/writer.js +139 -0
- package/neural-interface/lib/http-guards.js +189 -13
- package/neural-interface/lib/machine-names.js +88 -0
- package/neural-interface/lib/memory-api.js +31 -7
- package/neural-interface/lib/native-loop-providers.js +10 -5
- package/neural-interface/lib/plugin-installer.js +4 -2
- package/neural-interface/lib/project-storage.js +5 -7
- package/neural-interface/lib/request-gate.js +589 -0
- package/neural-interface/lib/ui-blob-api.js +108 -0
- package/neural-interface/lib/ui-blob-store.js +68 -0
- package/neural-interface/package-lock.json +701 -2
- package/neural-interface/package.json +13 -2
- package/neural-interface/public/install-app.html +9 -6
- package/neural-interface/public/offline.html +3 -2
- package/neural-interface/public/onboarding.html +12 -1
- package/neural-interface/public/shared/agent-runtime-options.js +22 -6
- package/neural-interface/public/shared/assistant/asst-automation.js +286 -0
- package/neural-interface/public/shared/assistant/asst-brain-picker.js +312 -62
- package/neural-interface/public/shared/assistant/asst-composer.js +11 -1
- package/neural-interface/public/shared/assistant/asst-mascot.js +18 -58
- package/neural-interface/public/shared/assistant/asst-menu.js +12 -8
- package/neural-interface/public/shared/assistant/asst-panel.js +150 -28
- package/neural-interface/public/shared/assistant/asst-render.js +162 -19
- package/neural-interface/public/shared/assistant/asst-route-models.js +108 -0
- package/neural-interface/public/shared/assistant/asst-route.js +23 -329
- package/neural-interface/public/shared/assistant/asst-routes-editor.js +495 -0
- package/neural-interface/public/shared/assistant/asst-sanitize.js +8 -1
- package/neural-interface/public/shared/assistant/asst-sidepanel.js +31 -9
- package/neural-interface/public/shared/assistant/asst-state.js +185 -1
- package/neural-interface/public/shared/assistant/asst-styles.js +158 -4
- package/neural-interface/public/shared/blob-store.js +111 -22
- package/neural-interface/public/shared/cdx/cdx-panel.js +32 -11
- package/neural-interface/public/shared/cdx/cdx-render.js +25 -89
- package/neural-interface/public/shared/cdx/cdx-styles.js +36 -0
- package/neural-interface/public/shared/cdx/cdx-tabs.js +87 -4
- package/neural-interface/public/shared/constants.js +6 -1
- package/neural-interface/public/shared/cp/cp-rehydrate.js +2 -1
- package/neural-interface/public/shared/cp/cp-styles.js +15 -0
- package/neural-interface/public/shared/editor/editor-ai-logic.js +409 -0
- package/neural-interface/public/shared/editor/editor-ai.js +619 -0
- package/neural-interface/public/shared/editor/editor-commands.js +212 -0
- package/neural-interface/public/shared/editor/editor-compare.js +103 -0
- package/neural-interface/public/shared/editor/editor-completion-list.js +172 -0
- package/neural-interface/public/shared/editor/editor-doc.js +257 -0
- package/neural-interface/public/shared/editor/editor-git-logic.js +131 -0
- package/neural-interface/public/shared/editor/editor-git.js +95 -0
- package/neural-interface/public/shared/editor/editor-languages.js +156 -0
- package/neural-interface/public/shared/editor/editor-lint.js +229 -0
- package/neural-interface/public/shared/editor/editor-loader.js +51 -0
- package/neural-interface/public/shared/editor/editor-markdown.js +90 -0
- package/neural-interface/public/shared/editor/editor-menu.js +233 -0
- package/neural-interface/public/shared/editor/editor-minimap.js +371 -0
- package/neural-interface/public/shared/editor/editor-palette-logic.js +192 -0
- package/neural-interface/public/shared/editor/editor-palette.js +125 -0
- package/neural-interface/public/shared/editor/editor-panel-logic.js +288 -0
- package/neural-interface/public/shared/editor/editor-panel.js +2099 -0
- package/neural-interface/public/shared/editor/editor-prefs.js +224 -0
- package/neural-interface/public/shared/editor/editor-safety.js +500 -0
- package/neural-interface/public/shared/editor/editor-search.js +352 -0
- package/neural-interface/public/shared/editor/editor-statusbar.js +144 -0
- package/neural-interface/public/shared/editor/editor-tabs-logic.js +331 -0
- package/neural-interface/public/shared/editor/editor-tabs.js +233 -0
- package/neural-interface/public/shared/editor/editor-theme.js +244 -0
- package/neural-interface/public/shared/editor/editor-view.js +332 -0
- package/neural-interface/public/shared/explorer/fx-api.js +216 -0
- package/neural-interface/public/shared/explorer/fx-links.js +204 -0
- package/neural-interface/public/shared/explorer/fx-logic.js +537 -0
- package/neural-interface/public/shared/explorer/fx-main.js +865 -0
- package/neural-interface/public/shared/explorer/fx-names.js +87 -0
- package/neural-interface/public/shared/explorer/fx-ops.js +370 -0
- package/neural-interface/public/shared/explorer/fx-popups.js +490 -0
- package/neural-interface/public/shared/explorer/fx-tree.js +790 -0
- package/neural-interface/public/shared/file-links.js +339 -0
- package/neural-interface/public/shared/greeting/greeting-card.js +178 -0
- package/neural-interface/public/shared/greeting/greeting-view.js +148 -0
- package/neural-interface/public/shared/html-shell.js +160 -165
- package/neural-interface/public/shared/ocp-v2/ocp-v2-childpanel.js +4 -0
- package/neural-interface/public/shared/ocp-v2/ocp-v2-greeting.js +47 -0
- package/neural-interface/public/shared/ocp-v2/ocp-v2-panel.js +99 -17
- package/neural-interface/public/shared/ocp-v2/ocp-v2-render.js +7 -1
- package/neural-interface/public/shared/ocp-v2/ocp-v2-send.js +5 -0
- package/neural-interface/public/shared/ocp-v2/ocp-v2-styles.js +40 -0
- package/neural-interface/public/shared/ocp-v2/ocp-v2-ws.js +3 -2
- package/neural-interface/public/shared/pwa-install-page.js +29 -1
- package/neural-interface/public/shared/pwa-install.css +3 -3
- package/neural-interface/public/shared/pwa-install.js +100 -1
- package/neural-interface/public/shared/settings/settings-code-editor-pane.js +412 -0
- package/neural-interface/public/shared/settings/settings-code-editor.js +328 -0
- package/neural-interface/public/shared/settings/settings-greeting.js +133 -0
- package/neural-interface/public/shared/settings/settings-ia.js +18 -4
- package/neural-interface/public/shared/settings/settings-user-context.js +154 -0
- package/neural-interface/public/shared/start-bridge.js +238 -19
- package/neural-interface/public/shared/styles.css +1414 -721
- package/neural-interface/public/shared/synabun-mascot.js +20 -56
- package/neural-interface/public/shared/tunnel-view.js +167 -0
- package/neural-interface/public/shared/ui-claude-panel.js +157 -16
- package/neural-interface/public/shared/ui-detail.js +15 -0
- package/neural-interface/public/shared/ui-explorer.js +22 -0
- package/neural-interface/public/shared/ui-file-explorer.js +481 -2496
- package/neural-interface/public/shared/ui-file-links.js +103 -0
- package/neural-interface/public/shared/ui-help.js +26 -0
- package/neural-interface/public/shared/ui-invite.js +126 -116
- package/neural-interface/public/shared/ui-keybinds.js +4 -0
- package/neural-interface/public/shared/ui-loading.js +159 -15
- package/neural-interface/public/shared/ui-menubar.js +12 -30
- package/neural-interface/public/shared/ui-native-loop-router-state.js +11 -0
- package/neural-interface/public/shared/ui-native-loop-router.js +15 -2
- package/neural-interface/public/shared/ui-navbar.js +45 -21
- package/neural-interface/public/shared/ui-settings.js +455 -488
- package/neural-interface/public/shared/ui-sidepanel-runtime.js +2 -1
- package/neural-interface/public/shared/ui-sync.js +16 -0
- package/neural-interface/public/shared/ui-terminal.js +23 -5
- package/neural-interface/public/shared/ui-tunnel.js +130 -0
- package/neural-interface/public/shared/ui-tutorial-steps.js +6 -2
- package/neural-interface/public/shared/ui-whiteboard.js +12 -0
- package/neural-interface/public/shared/ui-worker-end.js +245 -0
- package/neural-interface/public/shared/ui-workspaces.js +41 -27
- package/neural-interface/public/shared/worker-end.js +251 -0
- package/neural-interface/public/sw.js +2 -2
- package/neural-interface/public/vendor/codemirror/chunks/apl-CSDKT72Z.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/asciiarmor-JYYPU5BM.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/asn1-NRWCZDA6.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/asterisk-5OU6D4ZQ.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/brainfuck-ID4N4SYC.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/chunk-4RZWGCS2.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/chunk-ACTJM3JR.js +11 -0
- package/neural-interface/public/vendor/codemirror/chunks/chunk-S6BMAXLK.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/chunk-TO2RDOHQ.js +3 -0
- package/neural-interface/public/vendor/codemirror/chunks/chunk-US2GMZZQ.js +13 -0
- package/neural-interface/public/vendor/codemirror/chunks/chunk-WYYO5ZAJ.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/chunk-XALZTDJC.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/chunk-YRNUTRDV.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/chunk-Z3QOKH62.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/clike-OWQBSOGL.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/clojure-KV75UQWC.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/cmake-GTEP5Y3C.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/cobol-WCZ7QEGJ.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/coffeescript-TGLBOT6B.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/commonlisp-WVVI432V.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/crystal-S6R6HLKQ.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/css-PDA2RINP.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/cypher-HL5QPL4C.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/d-HHJH2HAJ.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/diff-MN3A35BY.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/dist-25SQLBVS.js +3 -0
- package/neural-interface/public/vendor/codemirror/chunks/dist-27ZGGJJS.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/dist-4E52OJIF.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/dist-6FT73XDC.js +2 -0
- package/neural-interface/public/vendor/codemirror/chunks/dist-BE6YGRMW.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/dist-CJSGKW2O.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/dist-CRPH2WLI.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/dist-CTG7JEQN.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/dist-I3FEVELX.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/dist-IM5BBSWZ.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/dist-KERUKTIF.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/dist-LXTMWZAM.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/dist-MABASSET.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/dist-MJVBUZLL.js +3 -0
- package/neural-interface/public/vendor/codemirror/chunks/dist-MNB3WZVU.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/dist-QYWXOE3U.js +7 -0
- package/neural-interface/public/vendor/codemirror/chunks/dist-TEBOFWLZ.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/dist-UNXFROM3.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/dist-XL2SLQO6.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/dist-XO42Y3AP.js +6 -0
- package/neural-interface/public/vendor/codemirror/chunks/dist-Y2WWDWJK.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/dist-YFEZKWMU.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/dist-ZY7SWQO6.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/dockerfile-3Y4R4NCH.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/dtd-5343GJ4P.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/dylan-FI2CXSW2.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/ebnf-GNQ77YWA.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/ecl-MRPUAE2L.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/eiffel-DZROES27.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/elm-4KICRFKQ.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/erlang-3IHYC263.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/factor-C6D3E5UK.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/fcl-VYTWUJMI.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/forth-ZBO2EH5M.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/fortran-5NDCRWIK.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/gas-SKRMOXFP.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/gherkin-L45YXZLW.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/groovy-GLPTLN44.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/haskell-E7NF7P4Y.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/haxe-K2NVDOB6.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/http-Q6DRQIJB.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/idl-MZYNJVLN.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/javascript-6YRYK2HR.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/julia-HXEL2T5G.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/livescript-MV7CI2JZ.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/lua-E36NHOOD.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/mathematica-3XOZZCMC.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/mbox-ODYGFKS7.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/mirc-WB43AWF5.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/mllike-ZT6ET7ZH.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/modelica-LLMZPFGA.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/mscgen-2OPNNYFH.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/mumps-6P3HWJH6.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/nginx-TCGKQTRW.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/nsis-ATIGIXTJ.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/ntriples-5PLBI5OV.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/octave-QF2S3NL6.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/oz-JFIVFFH2.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/pascal-JG6SW76V.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/perl-4B3IWVVO.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/pig-ABEBH4MI.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/powershell-H32XE7TL.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/properties-NN644MMA.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/protobuf-YKWQK2J3.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/pug-RIYXEKFW.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/puppet-JZQ5WPEV.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/python-EBCGGHAC.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/q-5UMH2YQY.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/r-7XMGG453.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/rpm-IEV7TSLR.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/ruby-T4SPFPNM.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/sas-GEDX67ET.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/scheme-FTLOWKPC.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/shell-HUFRKFSJ.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/sieve-P5QUDVYO.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/smalltalk-2SORJNQI.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/solr-M22TGAR5.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/sparql-4M3KL752.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/spreadsheet-3K23LA4N.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/sql-VHRFMUBI.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/stex-QZDAQUY2.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/stylus-7ZDKAKBD.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/swift-WCLCLGMR.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/tcl-JLMU6WOK.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/textile-4CHDKZ4T.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/tiddlywiki-DKMCZZQ4.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/tiki-F4JIG36Q.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/toml-BHMHTIM2.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/troff-YQ2KV5NT.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/ttcn-ZHRCVFJY.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/ttcn-cfg-KJOVTY4X.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/turtle-535P5N6I.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/vb-76UMQHOT.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/vbscript-CGNDTK6Z.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/velocity-VSJDO5T5.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/verilog-4T6ETXIP.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/vhdl-T7FNAXUN.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/webidl-P6MJSJ6J.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/xquery-UOVILRGN.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/yacas-CV2APROL.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/z80-3KFNVM4M.js +1 -0
- package/neural-interface/public/vendor/codemirror/codemirror-6.43.14-75f05e67.min.js +2 -0
- package/neural-interface/public/vendor/codemirror/manifest.json +60 -0
- package/neural-interface/scripts/build-vendor.mjs +78 -2
- package/neural-interface/scripts/vendor-codemirror-entry.js +121 -0
- package/neural-interface/server.js +873 -1028
- package/neural-interface/templates/rulesets/README.md +1 -1
- package/package.json +2 -8
- package/setup.js +44 -7
- package/benchmarks/browser-live-results.json +0 -79
- package/benchmarks/browser-v2-results.json +0 -258
- package/neural-interface/lib/codex-capabilities.d.ts +0 -41
- package/neural-interface/public/category-logos/claude.svg +0 -1
- package/neural-interface/public/category-logos/gemini-color.svg +0 -1
- package/neural-interface/public/category-logos/openai.svg +0 -1
- package/neural-interface/public/category-logos/synabun.png +0 -0
- package/neural-interface/public/claude.svg +0 -1
- package/neural-interface/public/gallery.svg +0 -6
- package/neural-interface/public/leonardoai.svg +0 -1
- package/neural-interface/public/mcp.svg +0 -4
- package/neural-interface/public/opencode-logo-dark-square.svg +0 -1
- package/neural-interface/public/pin-svgrepo-com.svg +0 -25
- package/neural-interface/public/sessionicon.svg +0 -8
- package/neural-interface/public/toolbar-album.svg +0 -8
- package/neural-interface/public/toolbar-question.svg +0 -6
- package/neural-interface/public/toolbar-sessions.svg +0 -6
- package/neural-interface/scripts/codex-protocol-fixture.mjs +0 -83
- package/skills/synabun/synabunicon.png +0 -0
|
@@ -11,7 +11,8 @@ export function routeSidepanelEvent(event, data) {
|
|
|
11
11
|
const host = sidepanelHost();
|
|
12
12
|
if (!host) return false;
|
|
13
13
|
if (hostedSidepanelId) {
|
|
14
|
-
|
|
14
|
+
// The code editor lives in the main window: what opens it from a detached session goes there.
|
|
15
|
+
if (!['open-plan-editor', 'open-changelog-editor', 'open-file-editor', 'browser:reconnect'].includes(event)) return false;
|
|
15
16
|
host.emitFromSession(hostedSidepanelId, event, data);
|
|
16
17
|
return true;
|
|
17
18
|
}
|
|
@@ -74,6 +74,18 @@ const SYNC_HANDLERS = {
|
|
|
74
74
|
// Assistant → Models toggles (every provider): the Assistant's model menus,
|
|
75
75
|
// Routes editor and Models manager reload their lists.
|
|
76
76
|
'assistant:hidden-models-changed': (msg) => emit('sync:assistant:hidden-models-changed', msg),
|
|
77
|
+
// A provider lists other models than before (CLI update, account change, OpenCode
|
|
78
|
+
// provider connected): the same menus reload.
|
|
79
|
+
'assistant:catalog-changed': (msg) => emit('sync:assistant:catalog-changed', msg),
|
|
80
|
+
|
|
81
|
+
// Settings → Automation behavior switched in another window: an open Settings follows.
|
|
82
|
+
'assistant:user-context-changed': (msg) => emit('sync:assistant:user-context-changed', msg),
|
|
83
|
+
|
|
84
|
+
// Code editor preferences saved in another window (word wrap, font size…): the editor follows.
|
|
85
|
+
'editor:config-changed': (msg) => emit('sync:editor:config-changed', msg),
|
|
86
|
+
|
|
87
|
+
// Settings → Greeting switched in another window: an open Settings follows.
|
|
88
|
+
'greeting:changed': (msg) => emit('sync:greeting:changed', msg),
|
|
77
89
|
|
|
78
90
|
// Card sync (relayed from other clients)
|
|
79
91
|
'card:opened': (msg) => emit('sync:card:opened', msg),
|
|
@@ -178,6 +190,10 @@ const SYNC_HANDLERS = {
|
|
|
178
190
|
// Skin change sync
|
|
179
191
|
'skin:changed': (msg) => emit('sync:skin:changed', msg),
|
|
180
192
|
|
|
193
|
+
// The server says it is restarting, before it goes: every window shows the
|
|
194
|
+
// restart, not only the one that asked for it (ui-loading.js).
|
|
195
|
+
'server:restarting': (msg) => emit('server:restarting', msg),
|
|
196
|
+
|
|
181
197
|
// Session indexing progress
|
|
182
198
|
'indexing:started': (msg) => emit('ws:message', msg),
|
|
183
199
|
'indexing:session-started': (msg) => emit('ws:message', msg),
|
|
@@ -20,7 +20,7 @@ import { readinessKind, typeWhenReady } from './term-ready.js';
|
|
|
20
20
|
import { initRendererManager, acquireRenderer, disposeRenderer, hasWebgl, noteFocus, adoptRenderer, rendererState, rendererStats } from './term-renderer.js';
|
|
21
21
|
import { mountAssistant } from './assistant/asst-panel.js';
|
|
22
22
|
import { getAssistantSession } from './assistant/asst-api.js';
|
|
23
|
-
import { brainLabel, normalizeBrain, readStoredBrain } from './assistant/asst-state.js';
|
|
23
|
+
import { brainLabel, normalizeBrain, readStoredBrain, restoredSessionName, sessionNameGivenUp } from './assistant/asst-state.js';
|
|
24
24
|
import { initAssistant, registerAssistantHost, openAssistant, assistantOwnerOf, fetchLiveAssistantSessions, isAssistantSessionLive } from './ui-assistant.js';
|
|
25
25
|
|
|
26
26
|
const $ = (id) => document.getElementById(id);
|
|
@@ -3467,7 +3467,7 @@ function _updateAssistantBrainChip(session) {
|
|
|
3467
3467
|
chip.hidden = !label;
|
|
3468
3468
|
}
|
|
3469
3469
|
|
|
3470
|
-
/** After a tab/float rename, push the new label into the panel (marks it user-renamed). */
|
|
3470
|
+
/** After a tab/float rename, push the new label into the panel (marks it user-renamed; the panel writes it to the session record). */
|
|
3471
3471
|
function _syncAssistantLabel(session) {
|
|
3472
3472
|
if (!session?._isAssistant || !session._assistant) return;
|
|
3473
3473
|
if (session._assistant.getTitle() !== session.label) session._assistant.setTitle(session.label);
|
|
@@ -3484,6 +3484,11 @@ function _mountAssistantSession(sessionId, { meta = null, saved = null, brain =
|
|
|
3484
3484
|
if (existing) { if (!_restoringLayout) _focusAssistantTab(existing); return existing; }
|
|
3485
3485
|
|
|
3486
3486
|
const resolvedBrain = normalizeBrain(brain || meta?.brain || saved?.brain || readStoredBrain(storage));
|
|
3487
|
+
// A renamed session comes back under its name: the session record's (a rename made in either
|
|
3488
|
+
// host, the one the Sessions menu lists), else the rename saved with this tab. A record whose
|
|
3489
|
+
// name was given up says so: the label and the renamed flag saved with a tab are stale.
|
|
3490
|
+
const named = restoredSessionName(meta, label);
|
|
3491
|
+
const givenUp = sessionNameGivenUp(meta);
|
|
3487
3492
|
const viewport = document.createElement('div');
|
|
3488
3493
|
viewport.className = 'term-viewport assistant-viewport';
|
|
3489
3494
|
viewport.dataset.sessionId = sessionId;
|
|
@@ -3495,13 +3500,13 @@ function _mountAssistantSession(sessionId, { meta = null, saved = null, brain =
|
|
|
3495
3500
|
id: sessionId,
|
|
3496
3501
|
profile: 'assistant',
|
|
3497
3502
|
cwd: resolvedBrain.project || null,
|
|
3498
|
-
label:
|
|
3503
|
+
label: named || (givenUp ? '' : saved?.label) || meta?.title || profileDef?.label || 'Assistant',
|
|
3499
3504
|
term: null, fitAddon: null, searchAddon: null,
|
|
3500
3505
|
ws: null, // no PTY socket — the panel owns /ws/assistant/:id
|
|
3501
3506
|
viewport, ro: null, renderer: null,
|
|
3502
3507
|
dead: false,
|
|
3503
3508
|
pinned: saved?.pinned || false,
|
|
3504
|
-
_userRenamed: saved?.userRenamed || !!
|
|
3509
|
+
_userRenamed: !givenUp && (saved?.userRenamed || !!named),
|
|
3505
3510
|
_floatColor: saved?.floatColor || null,
|
|
3506
3511
|
_isAssistant: true, // flag for tab-type-specific logic
|
|
3507
3512
|
_assistant: null, // controller (set right after mount)
|
|
@@ -3525,6 +3530,19 @@ function _mountAssistantSession(sessionId, { meta = null, saved = null, brain =
|
|
|
3525
3530
|
if (titleEl && titleEl.contentEditable !== 'true') titleEl.textContent = next;
|
|
3526
3531
|
saveSessionRegistry();
|
|
3527
3532
|
},
|
|
3533
|
+
// A rename the panel could not save: the tab and its saved label go back to the record's name.
|
|
3534
|
+
setName(text, renamed) {
|
|
3535
|
+
const s = live();
|
|
3536
|
+
const next = String(text || '').trim();
|
|
3537
|
+
if (!next) return;
|
|
3538
|
+
s.label = next;
|
|
3539
|
+
s._userRenamed = !!renamed;
|
|
3540
|
+
renderTabBar();
|
|
3541
|
+
const titleEl = _detachedTabs.get(sessionId)?.el?.querySelector('.term-float-tab-title');
|
|
3542
|
+
if (titleEl && titleEl.contentEditable !== 'true') titleEl.textContent = next;
|
|
3543
|
+
saveSessionRegistry();
|
|
3544
|
+
saveTerminalLayout();
|
|
3545
|
+
},
|
|
3528
3546
|
setStatus(status) { setSessionStatusBadge(sessionId, status); },
|
|
3529
3547
|
setBrain(b) {
|
|
3530
3548
|
const s = live();
|
|
@@ -3539,7 +3557,7 @@ function _mountAssistantSession(sessionId, { meta = null, saved = null, brain =
|
|
|
3539
3557
|
requestDetach() { const idx = _sessions.findIndex(s => s.id === sessionId); if (idx >= 0 && !_detachedTabs.has(sessionId)) detachTab(idx); },
|
|
3540
3558
|
};
|
|
3541
3559
|
session._assistant = mountAssistant(viewport, { sessionId, brain: resolvedBrain, host, session: meta, label: session.label, reattach });
|
|
3542
|
-
if (session._userRenamed && session.label) session._assistant.setTitle(session.label);
|
|
3560
|
+
if (session._userRenamed && session.label) session._assistant.setTitle(session.label, { restored: true });
|
|
3543
3561
|
|
|
3544
3562
|
renderTabBar();
|
|
3545
3563
|
if (_restoringLayout) {
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
// ═══════════════════════════════════════════
|
|
2
|
+
// SynaBun Neural Interface — the public address: the page side
|
|
3
|
+
// ═══════════════════════════════════════════
|
|
4
|
+
//
|
|
5
|
+
// What the two cards that start the Cloudflare tunnel share (the Share menu's
|
|
6
|
+
// Connection block in ui-invite.js, Settings → Automations → Online access in
|
|
7
|
+
// ui-settings.js): asking the server, starting and stopping, asking again
|
|
8
|
+
// while something is on its way (a download, the wait for an address), and
|
|
9
|
+
// the reading of each answer in the page's language. What an answer means is
|
|
10
|
+
// tunnel-view.js; how it is drawn is each card's own.
|
|
11
|
+
//
|
|
12
|
+
// The requests are sent with fetch as they are: same origin, no body. The
|
|
13
|
+
// server takes a start or a stop from the app's own pages only
|
|
14
|
+
// (lib/http-guards.js ownerRouteGuard), and a refusal is shown in the card
|
|
15
|
+
// like any other failure, not as the app-wide "forbidden" notice.
|
|
16
|
+
|
|
17
|
+
import { getLocale, t } from './i18n.js';
|
|
18
|
+
import { tunnelView } from './tunnel-view.js';
|
|
19
|
+
|
|
20
|
+
const POLL_MS = 1000;
|
|
21
|
+
/** How long a card keeps asking by itself. A download or a start that takes longer is shown again on the next look. */
|
|
22
|
+
const POLL_LIMIT_MS = 20 * 60 * 1000;
|
|
23
|
+
|
|
24
|
+
const translate = (key, params) => t(`tunnel.${key}`, params);
|
|
25
|
+
function number(value, digits) {
|
|
26
|
+
try { return value.toLocaleString(getLocale(), { minimumFractionDigits: digits, maximumFractionDigits: digits }); } catch { return value.toFixed(digits); }
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/** tunnel-view.js tunnelView() in the page's language. */
|
|
30
|
+
export function readTunnel(status) {
|
|
31
|
+
return tunnelView(status, { t: translate, number });
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/** The sentence a card shows where it offers the remote MCP address and no access key exists. */
|
|
35
|
+
export function mcpKeyNeededText() {
|
|
36
|
+
return translate('mcpKeyNeeded');
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
async function ask(path, method = 'GET') {
|
|
40
|
+
const res = await fetch(path, { method });
|
|
41
|
+
let body = null;
|
|
42
|
+
try { body = await res.json(); } catch { body = null; }
|
|
43
|
+
return { status: res.status, ok: res.ok, body };
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* One card's hold on the tunnel.
|
|
48
|
+
*
|
|
49
|
+
* @param {object} [o]
|
|
50
|
+
* @param {(view: object, status: object | null) => void} [o.onChange] called with every new reading
|
|
51
|
+
* @param {() => boolean} [o.alive] false once the card is gone: the asking-again stops
|
|
52
|
+
* @returns {{ view: () => object, status: () => (object | null), show: (status: object) => object,
|
|
53
|
+
* refresh: () => Promise<object>, start: () => Promise<object>, stop: () => Promise<object>, dispose: () => void }}
|
|
54
|
+
*/
|
|
55
|
+
export function createTunnelCard({ onChange = () => {}, alive = () => true } = {}) {
|
|
56
|
+
let status = null;
|
|
57
|
+
let timer = null;
|
|
58
|
+
let since = 0;
|
|
59
|
+
let disposed = false;
|
|
60
|
+
|
|
61
|
+
function schedule(view) {
|
|
62
|
+
clearTimeout(timer);
|
|
63
|
+
timer = null;
|
|
64
|
+
if (disposed || !view.poll || !alive()) { since = 0; return; }
|
|
65
|
+
if (!since) since = Date.now();
|
|
66
|
+
if (Date.now() - since > POLL_LIMIT_MS) return;
|
|
67
|
+
timer = setTimeout(refresh, POLL_MS);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/** Take an answer as the state of things, tell the card, and ask again later when it will change by itself. */
|
|
71
|
+
function show(next) {
|
|
72
|
+
status = next && typeof next === 'object' ? next : null;
|
|
73
|
+
const view = readTunnel(status);
|
|
74
|
+
if (!disposed) { try { onChange(view, status); } catch (error) { console.warn('[tunnel] The card could not be drawn:', error); } }
|
|
75
|
+
schedule(view);
|
|
76
|
+
return view;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
async function refresh() {
|
|
80
|
+
if (disposed) return readTunnel(status);
|
|
81
|
+
try {
|
|
82
|
+
const got = await ask('/api/tunnel/status');
|
|
83
|
+
if (got.ok && got.body) return show(got.body);
|
|
84
|
+
} catch { /* the server is away for a moment: what is shown stays, and the next look asks again */ }
|
|
85
|
+
const view = readTunnel(status);
|
|
86
|
+
schedule(view);
|
|
87
|
+
return view;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/** A start or a stop that the server did not take: shown in the card, with what the card knew before. */
|
|
91
|
+
function refused(got, error) {
|
|
92
|
+
// A server from before the tunnel program came with SynaBun answers a start with 400 when no cloudflared is
|
|
93
|
+
// installed, and its words tell the user to install one. What is missing is a restart: the card says that.
|
|
94
|
+
if (got?.status === 400 && !status?.cloudflared) return show({ ok: true, available: false, running: false, starting: false, url: null });
|
|
95
|
+
const failure = got?.status === 403
|
|
96
|
+
? { code: 'refused', message: got.body?.error || '' }
|
|
97
|
+
: { code: 'unknown', message: got?.body?.error || error?.message || '' };
|
|
98
|
+
return show({ ...(status || {}), ok: true, running: false, starting: false, url: null, state: 'failed', error: failure, cloudflared: status?.cloudflared || { state: 'missing' } });
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
async function send(path) {
|
|
102
|
+
let got = null;
|
|
103
|
+
try { got = await ask(path, 'POST'); } catch (error) { return refused(null, error); }
|
|
104
|
+
if (!got.ok || got.body?.ok === false) return refused(got);
|
|
105
|
+
return refresh();
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
return {
|
|
109
|
+
view: () => readTunnel(status),
|
|
110
|
+
status: () => status,
|
|
111
|
+
show,
|
|
112
|
+
refresh,
|
|
113
|
+
start: () => send('/api/tunnel/start'),
|
|
114
|
+
stop: () => send('/api/tunnel/stop'),
|
|
115
|
+
dispose() { disposed = true; clearTimeout(timer); timer = null; },
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Put new markup into a card's block without disturbing the person using it: nothing happens when the markup is
|
|
121
|
+
* what is there already, and a button that had the keyboard's focus has it again afterwards (by its id).
|
|
122
|
+
*/
|
|
123
|
+
export function drawInto(container, html) {
|
|
124
|
+
if (!container || container.__tunnelHtml === html) return false;
|
|
125
|
+
const focused = container.contains(document.activeElement) ? document.activeElement?.id : '';
|
|
126
|
+
container.innerHTML = html;
|
|
127
|
+
container.__tunnelHtml = html;
|
|
128
|
+
if (focused) { try { container.querySelector(`#${CSS.escape(focused)}`)?.focus({ preventScroll: true }); } catch { /* the button is gone with its state */ } }
|
|
129
|
+
return true;
|
|
130
|
+
}
|
|
@@ -82,11 +82,15 @@ const ASSISTANT_BUTTON = ['#topright-assistant-panel-btn', 'closed'];
|
|
|
82
82
|
const ACTIVE_TAB = '#assistant-panel.open .asp-viewport.active';
|
|
83
83
|
const ACTIVE_BAR = '#assistant-panel.open .asp-tab-bar.active';
|
|
84
84
|
|
|
85
|
+
// With the workspace tools hidden (the toolbar's toggle) their controls are off screen, and the step outlines the
|
|
86
|
+
// toggle that shows them.
|
|
87
|
+
const TOOLS_TOGGLE = ['#topright-tools-btn'];
|
|
88
|
+
|
|
85
89
|
export const FEATURE_STEPS = [
|
|
86
|
-
{ id: 'explain-memory-explorer', chapter: 'memory', targets: [['#topright-memory-explorer-btn']] },
|
|
90
|
+
{ id: 'explain-memory-explorer', chapter: 'memory', targets: [['#topright-memory-explorer-btn'], TOOLS_TOGGLE] },
|
|
87
91
|
{ id: 'explain-graph', chapter: 'memory', copy: { '2d': 'body2d' }, targets: [['[data-menu="graph"] > .menubar-label']] },
|
|
88
92
|
{ id: 'explain-focus', chapter: 'workspace', targets: [['#wb-toolbar'], ['#titlebar-viz-toggle', 'graph']] },
|
|
89
|
-
{ id: 'explain-workspaces', chapter: 'workspace', targets: [['#ws-indicator']] },
|
|
93
|
+
{ id: 'explain-workspaces', chapter: 'workspace', targets: [['#ws-indicator'], TOOLS_TOGGLE] },
|
|
90
94
|
{ id: 'explain-terminals', chapter: 'tools', menu: 'apps', targets: [['#menu-terminal-claude', 'body', '#menu-terminal-opencode']] },
|
|
91
95
|
{ id: 'explain-browser', chapter: 'tools', menu: 'apps', targets: [['#menu-terminal-browser']] },
|
|
92
96
|
{ id: 'explain-side-panel', chapter: 'tools', targets: [['#topright-claude-panel-btn', 'body', '#topright-opencode-panel-btn']] },
|
|
@@ -2882,6 +2882,11 @@ export function isSidepanelKeyTarget(el) {
|
|
|
2882
2882
|
return !!el?.closest?.('[data-sidepanel-provider]');
|
|
2883
2883
|
}
|
|
2884
2884
|
|
|
2885
|
+
// A key pressed inside the File Explorer's code editor (shared/editor/editor-panel.js) is the editor's.
|
|
2886
|
+
export function isEditorPanelKeyTarget(el) {
|
|
2887
|
+
return !!el?.closest?.('#fe-editor-panel');
|
|
2888
|
+
}
|
|
2889
|
+
|
|
2885
2890
|
function onKeyDown(e) {
|
|
2886
2891
|
if (!isWhiteboardActive()) return;
|
|
2887
2892
|
|
|
@@ -2897,6 +2902,13 @@ function onKeyDown(e) {
|
|
|
2897
2902
|
// Nor while it is inside a side panel: Escape there stops a turn, and Delete, Backspace or
|
|
2898
2903
|
// Ctrl+A/Z/C on one of its controls is not a board edit.
|
|
2899
2904
|
if (isSidepanelKeyTarget(e.target) || isSidepanelKeyTarget(ae)) return;
|
|
2905
|
+
// Nor while it is inside the code editor's panel: on one of its tabs, header or status-bar buttons, in one of
|
|
2906
|
+
// its menus, its palette or its popover (all attached inside the panel), M, Delete, Backspace or
|
|
2907
|
+
// Ctrl+A/Z/Y/C is the editor's key, or nobody's, and never a board edit.
|
|
2908
|
+
if (isEditorPanelKeyTarget(e.target) || isEditorPanelKeyTarget(ae)) return;
|
|
2909
|
+
// Nor while it is in the File Explorer, or in what it opens over the page (its menu, preview and questions):
|
|
2910
|
+
// Delete there deletes a file, and a letter is the name of a row being typed.
|
|
2911
|
+
if (ae?.closest?.('#file-explorer-panel, .fe-ctx-flyout, .fe-modal-overlay, .fe-git-popover')) return;
|
|
2900
2912
|
|
|
2901
2913
|
const isEditingText = !!document.activeElement?.closest('.wb-text.editing');
|
|
2902
2914
|
const editingListLi = document.activeElement?.closest('.wb-list.editing li') || null;
|
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
// ═══════════════════════════════════════════
|
|
2
|
+
// SynaBun Neural Interface — how a dispatched worker's tab ends: the page side
|
|
3
|
+
// ═══════════════════════════════════════════
|
|
4
|
+
//
|
|
5
|
+
// What the three provider sidepanels (Claude Code, Codex, OpenCode) share for
|
|
6
|
+
// the end of a worker the Assistant dispatched: following the run until it
|
|
7
|
+
// has something to show, asking the dispatcher how it went (GET
|
|
8
|
+
// /api/assistant/runs/:runId, then its `assistant:dispatch` events), the card
|
|
9
|
+
// itself, and the way back to the Assistant session that dispatched it. What
|
|
10
|
+
// the card says is worker-end.js; where it goes in a transcript, how it looks
|
|
11
|
+
// and how its tab closes are each panel's own (every panel passes its own
|
|
12
|
+
// classes and its own close action, so the card wears that panel's styles).
|
|
13
|
+
//
|
|
14
|
+
// The way back closes the worker's tab with the close the panel's own X runs
|
|
15
|
+
// (it never deletes the run record: the Assistant still reads its result, and
|
|
16
|
+
// the agents dock still lists it) and then shows the session through
|
|
17
|
+
// `assistant:show` with the sidepanel as the host for a session that is open
|
|
18
|
+
// nowhere: the terminal tab or the sidepanel that has it keeps it. Closing the
|
|
19
|
+
// tab of a worker that finished its task completes its run instead of
|
|
20
|
+
// stopping it (endWorkerRunOnClose): the Assistant reads it as done, never as
|
|
21
|
+
// stopped by the user.
|
|
22
|
+
|
|
23
|
+
import { emit, on } from './state.js';
|
|
24
|
+
import { t } from './i18n.js';
|
|
25
|
+
import { TERMINAL_STATES, WORKER_END_CLASS, isDispatchedRun, isRunWorking, mayHaveEnded, workerEndView } from './worker-end.js';
|
|
26
|
+
|
|
27
|
+
export { WORKER_END_CLASS };
|
|
28
|
+
|
|
29
|
+
const translate = (key, params) => t(`workerEnd.${key}`, params);
|
|
30
|
+
|
|
31
|
+
// runId → { run, dispatch, settled, wasWorking, busy, apply, readKey, sig }
|
|
32
|
+
const _runs = new Map();
|
|
33
|
+
let _wired = false;
|
|
34
|
+
|
|
35
|
+
function newer(next, current) {
|
|
36
|
+
if (!current) return true;
|
|
37
|
+
const a = Number(next?.version) || 0;
|
|
38
|
+
const b = Number(current?.version) || 0;
|
|
39
|
+
if (a !== b) return a > b;
|
|
40
|
+
return String(next?.updatedAt || '') >= String(current?.updatedAt || '');
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function wire() {
|
|
44
|
+
if (_wired) return;
|
|
45
|
+
_wired = true;
|
|
46
|
+
// Every change of a run the dispatcher makes (a turn, a result, the end, a
|
|
47
|
+
// failure's cause judged later) reaches the tab without another request.
|
|
48
|
+
on('sync:assistant:dispatch', (message) => {
|
|
49
|
+
const view = message?.run;
|
|
50
|
+
const entry = view?.runId ? _runs.get(view.runId) : null;
|
|
51
|
+
if (!entry || !newer(view, entry.dispatch)) return;
|
|
52
|
+
noteDispatch(entry, view);
|
|
53
|
+
paint(entry);
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function noteDispatch(entry, view) {
|
|
58
|
+
entry.dispatch = view;
|
|
59
|
+
entry.settled = true;
|
|
60
|
+
// What the run was doing before it ended: a stop in the middle of a turn is a stop.
|
|
61
|
+
if (!TERMINAL_STATES.has(String(view.state || ''))) entry.wasWorking = isRunWorking(null, view);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function paint(entry) {
|
|
65
|
+
const view = workerEndView(entry.run, entry.dispatch, { t: translate, busy: entry.busy, stoppedMidTurn: entry.wasWorking });
|
|
66
|
+
// An ended run is drawn once the dispatcher has answered: never "Stopped" first and "Task complete" a moment later.
|
|
67
|
+
if (view && !entry.settled) return;
|
|
68
|
+
const sig = view?.sig || '';
|
|
69
|
+
if (sig === entry.sig) return;
|
|
70
|
+
entry.sig = sig;
|
|
71
|
+
try { entry.apply(view); } catch (error) { console.warn('[worker-end] a panel failed to draw the end of a run', error); }
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
async function read(entry) {
|
|
75
|
+
const run = entry.run;
|
|
76
|
+
const key = `${run.status}|${run.turnState || ''}|${run.version || 0}`;
|
|
77
|
+
if (entry.readKey === key) return;
|
|
78
|
+
entry.readKey = key;
|
|
79
|
+
try {
|
|
80
|
+
const response = await fetch(`/api/assistant/runs/${encodeURIComponent(run.runId)}`);
|
|
81
|
+
const data = response.ok ? await response.json() : null;
|
|
82
|
+
if (_runs.get(run.runId) !== entry) return;
|
|
83
|
+
if (data?.run && newer(data.run, entry.dispatch)) noteDispatch(entry, data.run);
|
|
84
|
+
} catch {
|
|
85
|
+
if (_runs.get(run.runId) !== entry) return;
|
|
86
|
+
}
|
|
87
|
+
// No answer (an older server, a run the dispatcher no longer keeps): the runtime's own reading is drawn.
|
|
88
|
+
entry.settled = true;
|
|
89
|
+
paint(entry);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Follow a run attached to a provider tab. Call it on every attach (the
|
|
94
|
+
* router attaches again on each change of the run): `apply(view)` is called
|
|
95
|
+
* with the card to show, or null to take it away, only when that changes.
|
|
96
|
+
* A run the Assistant did not dispatch gets `apply(null)` and is not followed.
|
|
97
|
+
*
|
|
98
|
+
* @param {object} run the runtime descriptor the panel was attached with
|
|
99
|
+
* @param {object} o
|
|
100
|
+
* @param {(view: object | null) => void} o.apply draw the card (or remove it)
|
|
101
|
+
* @param {boolean} [o.busy] the tab sees background work of the run still going
|
|
102
|
+
*/
|
|
103
|
+
export function trackWorkerEnd(run, { apply, busy = false } = {}) {
|
|
104
|
+
if (!run?.runId || typeof apply !== 'function') return;
|
|
105
|
+
if (!isDispatchedRun(run)) {
|
|
106
|
+
_runs.delete(run.runId);
|
|
107
|
+
apply(null);
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
wire();
|
|
111
|
+
let entry = _runs.get(run.runId);
|
|
112
|
+
if (!entry) {
|
|
113
|
+
entry = { run: null, dispatch: null, settled: false, wasWorking: false, busy: false, apply, readKey: '', sig: null };
|
|
114
|
+
_runs.set(run.runId, entry);
|
|
115
|
+
}
|
|
116
|
+
entry.apply = apply;
|
|
117
|
+
entry.busy = !!busy;
|
|
118
|
+
if (newer(run, entry.run)) entry.run = run;
|
|
119
|
+
if (!TERMINAL_STATES.has(String(entry.run.status || '')) && !TERMINAL_STATES.has(String(entry.dispatch?.state || ''))) {
|
|
120
|
+
entry.wasWorking = isRunWorking(entry.run, null);
|
|
121
|
+
}
|
|
122
|
+
paint(entry);
|
|
123
|
+
if (mayHaveEnded(entry.run)) read(entry);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/** The tab saw its background work change: the card of a warm worker waits for it. */
|
|
127
|
+
export function updateWorkerEndBusy(runId, busy) {
|
|
128
|
+
const entry = _runs.get(runId);
|
|
129
|
+
if (!entry || entry.busy === !!busy) return;
|
|
130
|
+
entry.busy = !!busy;
|
|
131
|
+
paint(entry);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/** The tab of the run closed (or let the run go): stop following it. */
|
|
135
|
+
export function forgetWorkerEnd(runId) {
|
|
136
|
+
if (runId) _runs.delete(runId);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* The run reported its task done and waits warm for a follow-up, with no
|
|
141
|
+
* background work of its own going: its card is (or would be) a warm "Task
|
|
142
|
+
* complete". Known only for a run the tab follows, so ask before forgetWorkerEnd().
|
|
143
|
+
*/
|
|
144
|
+
export function isFinishedWarmWorker(runId, { busy = false } = {}) {
|
|
145
|
+
const entry = runId ? _runs.get(runId) : null;
|
|
146
|
+
if (!entry?.settled || !entry.run) return false;
|
|
147
|
+
const view = workerEndView(entry.run, entry.dispatch, { busy: busy || entry.busy, stoppedMidTurn: entry.wasWorking });
|
|
148
|
+
return view?.kind === 'complete' && view.warm;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* End the run of a worker tab the user closes (its X, or Back to Assistant).
|
|
153
|
+
* A worker that finished its task and waits warm for a follow-up is completed
|
|
154
|
+
* (POST /api/assistant/runs/:runId/tab-closed: completed, reason `tab_closed`,
|
|
155
|
+
* its result untouched). Any other run, a run the server finds working after
|
|
156
|
+
* all, and a server without that route get `stop()`: the panel's own stop,
|
|
157
|
+
* what its X always did. Decided when called: call it before forgetWorkerEnd().
|
|
158
|
+
*
|
|
159
|
+
* @param {string} runId
|
|
160
|
+
* @param {() => void} stop the panel's stop (POST /api/loop/stop)
|
|
161
|
+
* @param {object} [o]
|
|
162
|
+
* @param {boolean} [o.busy] the tab sees background work of the run still going
|
|
163
|
+
* @returns {Promise<'completed' | 'stopped'>}
|
|
164
|
+
*/
|
|
165
|
+
export function endWorkerRunOnClose(runId, stop, { busy = false } = {}) {
|
|
166
|
+
const stopped = () => { stop(); return 'stopped'; };
|
|
167
|
+
if (!isFinishedWarmWorker(runId, { busy })) return Promise.resolve(stopped());
|
|
168
|
+
return fetch(`/api/assistant/runs/${encodeURIComponent(runId)}/tab-closed`, {
|
|
169
|
+
method: 'POST',
|
|
170
|
+
headers: { 'Content-Type': 'application/json' },
|
|
171
|
+
body: '{}',
|
|
172
|
+
}).then(async (response) => {
|
|
173
|
+
const data = response.ok ? await response.json().catch(() => null) : null;
|
|
174
|
+
return data?.ok ? 'completed' : stopped();
|
|
175
|
+
}).catch(stopped);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Show the Assistant session that dispatched a run: focused where it is open
|
|
180
|
+
* (the terminal tab or the sidepanel), else opened in the Assistant sidepanel.
|
|
181
|
+
*/
|
|
182
|
+
export function backToAssistant(assistantSessionId) {
|
|
183
|
+
if (!assistantSessionId) return false;
|
|
184
|
+
emit('assistant:show', { sessionId: assistantSessionId, host: 'sidepanel' });
|
|
185
|
+
return true;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* The card. Every element takes the owning panel's classes, so it is drawn in
|
|
190
|
+
* that panel's type and colours; `data-tone` (ok, warn, err, off) is what the
|
|
191
|
+
* panel's styles colour the title by, and the title always says the state in
|
|
192
|
+
* words. The button is there only for a run with a dispatching session, and
|
|
193
|
+
* works once.
|
|
194
|
+
*
|
|
195
|
+
* @param {object} view a workerEndView() result
|
|
196
|
+
* @param {object} [o]
|
|
197
|
+
* @param {{ card?: string, title?: string, detail?: string, meta?: string, note?: string, actions?: string, button?: string }} [o.classes]
|
|
198
|
+
* @param {(view: object) => (void | boolean | Promise<void | boolean>)} [o.onBack] close the tab, then
|
|
199
|
+
* backToAssistant(); `false` when the tab stayed (the button works again)
|
|
200
|
+
*/
|
|
201
|
+
export function buildWorkerEndCard(view, { classes = {}, onBack = null } = {}) {
|
|
202
|
+
const card = document.createElement('div');
|
|
203
|
+
card.className = [WORKER_END_CLASS, classes.card].filter(Boolean).join(' ');
|
|
204
|
+
card.dataset.workerEnd = view.kind;
|
|
205
|
+
card.dataset.tone = view.tone;
|
|
206
|
+
card.setAttribute('role', 'status');
|
|
207
|
+
|
|
208
|
+
const title = document.createElement('div');
|
|
209
|
+
title.className = ['worker-end-title', classes.title].filter(Boolean).join(' ');
|
|
210
|
+
title.textContent = view.title;
|
|
211
|
+
card.appendChild(title);
|
|
212
|
+
|
|
213
|
+
for (const part of ['detail', 'meta', 'note']) {
|
|
214
|
+
if (!view[part]) continue;
|
|
215
|
+
const el = document.createElement('div');
|
|
216
|
+
el.className = [`worker-end-${part}`, classes[part]].filter(Boolean).join(' ');
|
|
217
|
+
el.textContent = view[part];
|
|
218
|
+
card.appendChild(el);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
if (view.assistantSessionId && typeof onBack === 'function') {
|
|
222
|
+
const actions = document.createElement('div');
|
|
223
|
+
actions.className = ['worker-end-actions', classes.actions].filter(Boolean).join(' ');
|
|
224
|
+
const button = document.createElement('button');
|
|
225
|
+
button.type = 'button';
|
|
226
|
+
button.className = ['worker-end-back', classes.button].filter(Boolean).join(' ');
|
|
227
|
+
button.textContent = view.backLabel;
|
|
228
|
+
button.title = view.backHint;
|
|
229
|
+
button.addEventListener('click', () => {
|
|
230
|
+
if (button.disabled) return;
|
|
231
|
+
button.disabled = true;
|
|
232
|
+
Promise.resolve()
|
|
233
|
+
.then(() => onBack(view))
|
|
234
|
+
// `false`: the panel asked first and the user kept the tab.
|
|
235
|
+
.then((done) => { if (done === false) button.disabled = false; })
|
|
236
|
+
.catch((error) => {
|
|
237
|
+
console.warn('[worker-end] going back to the Assistant failed', error);
|
|
238
|
+
button.disabled = false;
|
|
239
|
+
});
|
|
240
|
+
});
|
|
241
|
+
actions.appendChild(button);
|
|
242
|
+
card.appendChild(actions);
|
|
243
|
+
}
|
|
244
|
+
return card;
|
|
245
|
+
}
|
|
@@ -4,7 +4,8 @@
|
|
|
4
4
|
//
|
|
5
5
|
// Save/load full scene snapshots: open cards, graph node positions,
|
|
6
6
|
// camera state (3D), active category filters, and selection.
|
|
7
|
-
//
|
|
7
|
+
// The switcher leads the top-right dock; Snap to grid and Tile terminals are on
|
|
8
|
+
// the whiteboard rail and in View.
|
|
8
9
|
// Export/import as JSON for backup.
|
|
9
10
|
//
|
|
10
11
|
// Events emitted:
|
|
@@ -20,6 +21,7 @@
|
|
|
20
21
|
import { state, emit, on } from './state.js';
|
|
21
22
|
import { KEYS } from './constants.js';
|
|
22
23
|
import { storage } from './storage.js';
|
|
24
|
+
import { t } from './i18n.js';
|
|
23
25
|
import { posStore } from './node-positions-store.js';
|
|
24
26
|
import { getVariant } from './registry.js';
|
|
25
27
|
import {
|
|
@@ -525,18 +527,13 @@ function updateIndicator() {
|
|
|
525
527
|
if (!indicator || !nameEl) return;
|
|
526
528
|
|
|
527
529
|
const activeId = getActiveId();
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
}
|
|
536
|
-
}
|
|
537
|
-
|
|
538
|
-
nameEl.textContent = 'No workspace';
|
|
539
|
-
indicator.classList.remove('has-workspace');
|
|
530
|
+
const active = activeId ? getWorkspaces().find(w => w.id === activeId) : null;
|
|
531
|
+
nameEl.textContent = active ? active.name : 'No workspace';
|
|
532
|
+
indicator.classList.toggle('has-workspace', !!active);
|
|
533
|
+
// The name is cut short in the bar, or not shown at all where the band is narrow: the tooltip has it whole.
|
|
534
|
+
const label = active ? t('toolbar.workspaceActive', { name: active.name }) : t('toolbar.workspaceNone');
|
|
535
|
+
indicator.dataset.tooltip = label;
|
|
536
|
+
indicator.setAttribute('aria-label', label);
|
|
540
537
|
}
|
|
541
538
|
|
|
542
539
|
|
|
@@ -552,6 +549,7 @@ function openDropdown() {
|
|
|
552
549
|
_isOpen = true;
|
|
553
550
|
dropdown.style.display = '';
|
|
554
551
|
indicator?.classList.add('open');
|
|
552
|
+
indicator?.setAttribute('aria-expanded', 'true');
|
|
555
553
|
renderList();
|
|
556
554
|
|
|
557
555
|
emit('panel:close-all-dropdowns-except', 'workspace');
|
|
@@ -565,6 +563,7 @@ function closeDropdown() {
|
|
|
565
563
|
_isOpen = false;
|
|
566
564
|
dropdown.style.display = 'none';
|
|
567
565
|
indicator?.classList.remove('open');
|
|
566
|
+
indicator?.setAttribute('aria-expanded', 'false');
|
|
568
567
|
}
|
|
569
568
|
|
|
570
569
|
function toggleDropdown() {
|
|
@@ -668,25 +667,33 @@ export function initWorkspaces() {
|
|
|
668
667
|
});
|
|
669
668
|
}
|
|
670
669
|
|
|
671
|
-
// Grid snap toggle
|
|
670
|
+
// Grid snap: the whiteboard rail's toggle and View → Snap to grid (the rail shows in Focus Mode only)
|
|
672
671
|
const gridBtn = $('ws-grid-toggle');
|
|
672
|
+
const gridMenu = $('menu-toggle-grid-snap');
|
|
673
|
+
const showGridSnap = () => {
|
|
674
|
+
gridBtn?.classList.toggle('active', state.gridSnap);
|
|
675
|
+
gridBtn?.setAttribute('aria-pressed', String(state.gridSnap));
|
|
676
|
+
gridMenu?.classList.toggle('active', state.gridSnap);
|
|
677
|
+
document.body.classList.toggle('grid-active', state.gridSnap);
|
|
678
|
+
};
|
|
679
|
+
const toggleGridSnap = () => {
|
|
680
|
+
state.gridSnap = !state.gridSnap;
|
|
681
|
+
showGridSnap();
|
|
682
|
+
storage.setItem(KEYS.GRID_SNAP, state.gridSnap);
|
|
683
|
+
};
|
|
684
|
+
if (storage.getItem(KEYS.GRID_SNAP) === 'true') {
|
|
685
|
+
state.gridSnap = true;
|
|
686
|
+
showGridSnap();
|
|
687
|
+
}
|
|
673
688
|
if (gridBtn) {
|
|
674
|
-
const saved = storage.getItem(KEYS.GRID_SNAP);
|
|
675
|
-
if (saved === 'true') {
|
|
676
|
-
state.gridSnap = true;
|
|
677
|
-
gridBtn.classList.add('active');
|
|
678
|
-
document.body.classList.add('grid-active');
|
|
679
|
-
}
|
|
680
689
|
gridBtn.addEventListener('click', (e) => {
|
|
681
690
|
e.stopPropagation();
|
|
682
|
-
|
|
683
|
-
gridBtn.classList.toggle('active', state.gridSnap);
|
|
684
|
-
document.body.classList.toggle('grid-active', state.gridSnap);
|
|
685
|
-
storage.setItem(KEYS.GRID_SNAP, state.gridSnap);
|
|
691
|
+
toggleGridSnap();
|
|
686
692
|
});
|
|
687
693
|
}
|
|
694
|
+
if (gridMenu) gridMenu.addEventListener('click', toggleGridSnap);
|
|
688
695
|
|
|
689
|
-
// Tile floating terminals
|
|
696
|
+
// Tile floating terminals: the rail's button and View → Tile terminals
|
|
690
697
|
const tileBtn = $('ws-tile-btn');
|
|
691
698
|
if (tileBtn) {
|
|
692
699
|
tileBtn.addEventListener('click', (e) => {
|
|
@@ -694,12 +701,19 @@ export function initWorkspaces() {
|
|
|
694
701
|
tileFloatingTerminals();
|
|
695
702
|
});
|
|
696
703
|
}
|
|
704
|
+
const tileMenu = $('menu-tile-terminals');
|
|
705
|
+
if (tileMenu) {
|
|
706
|
+
tileMenu.addEventListener('click', () => {
|
|
707
|
+
emit('panel:close-all-dropdowns');
|
|
708
|
+
tileFloatingTerminals();
|
|
709
|
+
});
|
|
710
|
+
}
|
|
697
711
|
|
|
698
|
-
// Close dropdown on click outside
|
|
712
|
+
// Close dropdown on click outside (the session pills hang inside #topright-controls, but are not the toolbar)
|
|
699
713
|
document.addEventListener('click', (e) => {
|
|
700
714
|
if (!_isOpen) return;
|
|
701
715
|
const container = $('topright-controls') || $('workspace-overlay');
|
|
702
|
-
if (container && !container.contains(e.target)) {
|
|
716
|
+
if (container && (!container.contains(e.target) || e.target.closest('#term-minimized-tray'))) {
|
|
703
717
|
closeDropdown();
|
|
704
718
|
}
|
|
705
719
|
});
|