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
|
@@ -43,13 +43,17 @@ import { mountChangesBar } from './ocp-v2-changes.js';
|
|
|
43
43
|
import { mountEnvironmentPopover } from './ocp-v2-status.js';
|
|
44
44
|
import { watchWorktree, newSessionPlan, checkoutOutcome } from './ocp-v2-changes-logic.js';
|
|
45
45
|
import { mountRenderer } from './ocp-v2-render.js';
|
|
46
|
+
import { offerGreeting } from './ocp-v2-greeting.js';
|
|
46
47
|
import { mountCompose, focusCompose, appendPathToCompose } from './ocp-v2-send.js';
|
|
47
48
|
import { mountProjectBar, unmountProjectBar } from './ocp-v2-projectbar.js';
|
|
48
49
|
import { registerSidepanel, syncSidepanelLayout, focusSidepanel, openAnotherSidepanelSession, isHostedSessionFocused } from '../ui-sidepanel-windows.js';
|
|
49
50
|
import { nativeLoopWindowId } from '../ui-native-window-id.js';
|
|
50
51
|
import { requestGeneratedSessionTitle } from '../session-title.js';
|
|
52
|
+
import { wireFileLinks } from '../ui-file-links.js';
|
|
53
|
+
import { backToAssistant, buildWorkerEndCard, endWorkerRunOnClose, forgetWorkerEnd, trackWorkerEnd } from '../ui-worker-end.js';
|
|
54
|
+
import { isDispatchedRun } from '../worker-end.js';
|
|
51
55
|
import {
|
|
52
|
-
registerAutomationSession, unregisterAutomationSession, stopAutomationRun,
|
|
56
|
+
registerAutomationSession, unregisterAutomationSession, stopAutomationRun, isAutomationSession,
|
|
53
57
|
} from './ocp-v2-automation-ownership.js';
|
|
54
58
|
import {
|
|
55
59
|
registerPrimaryPanel, bindPrimarySession, isChildPanelOpen, getVisibleChildPanel, openChildSession,
|
|
@@ -92,6 +96,8 @@ const _automationRunning = new Set();
|
|
|
92
96
|
const _automationContinuedManually = new Set();
|
|
93
97
|
const _automationManualSendInFlight = new Set();
|
|
94
98
|
const _foreignActiveAutomationSessions = new Set();
|
|
99
|
+
const _workerEnds = new Map(); // sessionId -> end card of the dispatched worker it belongs to (ui-worker-end.js)
|
|
100
|
+
let _renderer = null;
|
|
95
101
|
let _visible = false;
|
|
96
102
|
let _booted = false;
|
|
97
103
|
let _bootPromise = null;
|
|
@@ -420,6 +426,9 @@ function buildPanel() {
|
|
|
420
426
|
messages.className = 'ocpv2-messages';
|
|
421
427
|
messagesContainer.appendChild(messages);
|
|
422
428
|
panel.appendChild(messagesContainer);
|
|
429
|
+
// A link to a file the code editor can open opens it there (shared with the other panels). A relative
|
|
430
|
+
// path is a path in the session's directory.
|
|
431
|
+
wireFileLinks(messagesContainer, { cwd: () => getState().cwd || '' });
|
|
423
432
|
|
|
424
433
|
// ── What the session changed on disk (session.diff) ─────────────────────
|
|
425
434
|
mountChangesBar(panel, getDefaultStore());
|
|
@@ -445,7 +454,17 @@ function buildPanel() {
|
|
|
445
454
|
});
|
|
446
455
|
|
|
447
456
|
let composer = null;
|
|
448
|
-
const renderer = mountRenderer(messages, getDefaultStore(), {
|
|
457
|
+
const renderer = _renderer = mountRenderer(messages, getDefaultStore(), {
|
|
458
|
+
// A dispatched worker's end: the last card of its session's transcript.
|
|
459
|
+
endCard: (s) => workerEndCard(s?.sessionId),
|
|
460
|
+
// SynaBun's greeting for a person who comes back to a new session: never for an automation run's.
|
|
461
|
+
onEmpty: (host, s) => offerGreeting(host, {
|
|
462
|
+
sessionId: s?.sessionId || null,
|
|
463
|
+
cwd: s?.cwd || '',
|
|
464
|
+
automation: !!s?.sessionId && (_automationRunIds.has(s.sessionId) || isAutomationSession(s.sessionId)),
|
|
465
|
+
onOpenMemory: (id) => { if (id) emit('memory:open', { id }); },
|
|
466
|
+
isCurrent: () => (getState().sessionId || null) === (s?.sessionId || null) && getState().messageOrder.length === 0,
|
|
467
|
+
}),
|
|
449
468
|
onOpenSession: (session, extra) => openCreatedSession(session, extra),
|
|
450
469
|
// Fork is a navigation: its intent begins at the click, and the fork takes
|
|
451
470
|
// the panel only while that click is still the user's latest choice.
|
|
@@ -504,7 +523,18 @@ function buildPanel() {
|
|
|
504
523
|
const sessionId = session?.sessionId;
|
|
505
524
|
if (phase === 'started') requestOpenCodeSessionTitle(session, meta);
|
|
506
525
|
if (!sessionId || !_automationRunIds.has(sessionId) || _automationRunning.has(sessionId)) return;
|
|
507
|
-
if (phase === 'started')
|
|
526
|
+
if (phase === 'started') {
|
|
527
|
+
const first = !_automationContinuedManually.has(sessionId) && !_automationManualSendInFlight.has(sessionId);
|
|
528
|
+
_automationManualSendInFlight.add(sessionId);
|
|
529
|
+
if (first) {
|
|
530
|
+
// The tab is the user's from here on (Codex's releaseAutomationForManualUse): the run's
|
|
531
|
+
// end card goes, and the router leaves the run alone, so nothing of it comes back here.
|
|
532
|
+
const runId = _automationRunIds.get(sessionId);
|
|
533
|
+
forgetWorkerEnd(runId);
|
|
534
|
+
showWorkerEnd(sessionId, null);
|
|
535
|
+
window.dispatchEvent(new CustomEvent('native-loop:dismissed', { detail: { runId } }));
|
|
536
|
+
}
|
|
537
|
+
}
|
|
508
538
|
if (phase === 'failed') _automationManualSendInFlight.delete(sessionId);
|
|
509
539
|
if (phase === 'succeeded') {
|
|
510
540
|
_automationManualSendInFlight.delete(sessionId);
|
|
@@ -2238,11 +2268,12 @@ function dismissAutomationSession(sessionId) {
|
|
|
2238
2268
|
if (!runId) return false;
|
|
2239
2269
|
window.dispatchEvent(new CustomEvent('native-loop:dismissed', { detail: { runId } }));
|
|
2240
2270
|
if (_automationRunning.has(sessionId)) {
|
|
2241
|
-
|
|
2271
|
+
// A worker that finished its task is completed; any other run is stopped.
|
|
2272
|
+
endWorkerRunOnClose(runId, () => fetch('/api/loop/stop', {
|
|
2242
2273
|
method: 'POST',
|
|
2243
2274
|
headers: { 'Content-Type': 'application/json' },
|
|
2244
2275
|
body: JSON.stringify({ runId }),
|
|
2245
|
-
}).catch(() => {});
|
|
2276
|
+
}).catch(() => {}));
|
|
2246
2277
|
}
|
|
2247
2278
|
return true;
|
|
2248
2279
|
}
|
|
@@ -2317,6 +2348,8 @@ function removeTab(sessionId, { byUser = false } = {}) {
|
|
|
2317
2348
|
const titleTimer = _titleReassertTimers.get(sessionId);
|
|
2318
2349
|
if (titleTimer) clearTimeout(titleTimer);
|
|
2319
2350
|
_titleReassertTimers.delete(sessionId);
|
|
2351
|
+
forgetWorkerEnd(_automationRunIds.get(sessionId));
|
|
2352
|
+
_workerEnds.delete(sessionId);
|
|
2320
2353
|
_automationRunIds.delete(sessionId);
|
|
2321
2354
|
unregisterAutomationSession(sessionId);
|
|
2322
2355
|
_automationRunning.delete(sessionId);
|
|
@@ -2354,22 +2387,56 @@ function createTabPill(sessionId) {
|
|
|
2354
2387
|
if (event.target.closest('.term-minimized-pill-close')) return;
|
|
2355
2388
|
handlePillClick(sessionId);
|
|
2356
2389
|
});
|
|
2357
|
-
pill.querySelector('.term-minimized-pill-close')?.addEventListener('click',
|
|
2390
|
+
pill.querySelector('.term-minimized-pill-close')?.addEventListener('click', (event) => {
|
|
2358
2391
|
event.stopPropagation();
|
|
2359
|
-
|
|
2360
|
-
// continued the terminal automation manually; backend cleanup is best-effort.
|
|
2361
|
-
// Asked first when prompts are still waiting in that session; declined:
|
|
2362
|
-
// the tab stays and nothing is deleted.
|
|
2363
|
-
if (!(await mayCloseTab(sessionId))) return;
|
|
2364
|
-
const wasRunning = sessionId === getState().sessionId && !!getState().running;
|
|
2365
|
-
closeSessionBackend(sessionId, { wasRunning, context: 'pill close' }).catch(() => {});
|
|
2366
|
-
try { await handlePillClose(sessionId, { byUser: true }); }
|
|
2367
|
-
catch (err) { console.warn('[ocp-v2-panel] local pill close failed', err); }
|
|
2392
|
+
closeTabByUser(sessionId, 'pill close');
|
|
2368
2393
|
});
|
|
2369
2394
|
tray.appendChild(pill);
|
|
2370
2395
|
return pill;
|
|
2371
2396
|
}
|
|
2372
2397
|
|
|
2398
|
+
// Mirror header X. Native transcripts are preserved unless the user has
|
|
2399
|
+
// continued the terminal automation manually; backend cleanup is best-effort.
|
|
2400
|
+
// Asked first when prompts are still waiting in that session; declined: the
|
|
2401
|
+
// tab stays, nothing is deleted, and this resolves false.
|
|
2402
|
+
async function closeTabByUser(sessionId, context) {
|
|
2403
|
+
if (!(await mayCloseTab(sessionId))) return false;
|
|
2404
|
+
const wasRunning = sessionId === getState().sessionId && !!getState().running;
|
|
2405
|
+
closeSessionBackend(sessionId, { wasRunning, context }).catch(() => {});
|
|
2406
|
+
try { await handlePillClose(sessionId, { byUser: true }); }
|
|
2407
|
+
catch (err) { console.warn(`[ocp-v2-panel] local ${context} failed`, err); }
|
|
2408
|
+
return true;
|
|
2409
|
+
}
|
|
2410
|
+
|
|
2411
|
+
// The end of a worker the Assistant dispatched: its button closes the tab the
|
|
2412
|
+
// way the tab's X does (a warm worker that finished its task is completed, one
|
|
2413
|
+
// still at work is stopped; its record stays for the Assistant) and shows the
|
|
2414
|
+
// Assistant session that dispatched it.
|
|
2415
|
+
function workerEndCard(sessionId) {
|
|
2416
|
+
const view = sessionId ? _workerEnds.get(sessionId) : null;
|
|
2417
|
+
if (!view) return null;
|
|
2418
|
+
return {
|
|
2419
|
+
sig: view.sig,
|
|
2420
|
+
build: () => buildWorkerEndCard(view, {
|
|
2421
|
+
classes: {
|
|
2422
|
+
card: 'ocpv2-post-plan-card ocpv2-worker-end', title: 'ocpv2-post-plan-header', detail: 'ocpv2-post-plan-body',
|
|
2423
|
+
meta: 'ocpv2-post-plan-saved', note: 'ocpv2-post-plan-note', actions: 'ocpv2-post-plan-actions', button: 'ocpv2-post-plan-btn primary',
|
|
2424
|
+
},
|
|
2425
|
+
onBack: async () => {
|
|
2426
|
+
if (!(await closeTabByUser(sessionId, 'worker end close'))) return false;
|
|
2427
|
+
backToAssistant(view.assistantSessionId);
|
|
2428
|
+
return true;
|
|
2429
|
+
},
|
|
2430
|
+
}),
|
|
2431
|
+
};
|
|
2432
|
+
}
|
|
2433
|
+
|
|
2434
|
+
function showWorkerEnd(sessionId, view) {
|
|
2435
|
+
if (view) _workerEnds.set(sessionId, view);
|
|
2436
|
+
else if (!_workerEnds.delete(sessionId)) return;
|
|
2437
|
+
_renderer?.render();
|
|
2438
|
+
}
|
|
2439
|
+
|
|
2373
2440
|
function escapeHtml(s) {
|
|
2374
2441
|
return String(s || '').replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"');
|
|
2375
2442
|
}
|
|
@@ -2539,9 +2606,12 @@ export async function attachOpenCodeAutomation(run, { focus = !!run?.focus, isCa
|
|
|
2539
2606
|
// Establish ownership before connect()/boot(): native provider events use a
|
|
2540
2607
|
// separate socket and may report a Task child while this attach is awaiting.
|
|
2541
2608
|
const previousRunId = _automationRunIds.get(sessionId);
|
|
2609
|
+
const running = run.status === 'starting' || run.status === 'running';
|
|
2610
|
+
// The user continued this run's session by hand after the run ended: the tab stays theirs.
|
|
2611
|
+
const released = !running && previousRunId === run.runId
|
|
2612
|
+
&& (_automationContinuedManually.has(sessionId) || _automationManualSendInFlight.has(sessionId));
|
|
2542
2613
|
_automationRunIds.set(sessionId, run.runId);
|
|
2543
2614
|
registerAutomationSession(sessionId, run.runId);
|
|
2544
|
-
const running = run.status === 'starting' || run.status === 'running';
|
|
2545
2615
|
if (running || previousRunId !== run.runId) {
|
|
2546
2616
|
_automationContinuedManually.delete(sessionId);
|
|
2547
2617
|
_automationManualSendInFlight.delete(sessionId);
|
|
@@ -2565,6 +2635,16 @@ export async function attachOpenCodeAutomation(run, { focus = !!run?.focus, isCa
|
|
|
2565
2635
|
await handlePillClose(sessionId).catch(() => {});
|
|
2566
2636
|
return { ok: false, reason: 'dismissed' };
|
|
2567
2637
|
}
|
|
2638
|
+
if (released) {
|
|
2639
|
+
// The dock's Focus raises it; the run is let go again, and nothing in the tab changes.
|
|
2640
|
+
window.dispatchEvent(new CustomEvent('native-loop:dismissed', { detail: { runId: run.runId } }));
|
|
2641
|
+
if (focus && _nav.latest() === attachBegan && _tabSessionIds.includes(sessionId)) {
|
|
2642
|
+
const nav = _nav.observe();
|
|
2643
|
+
if (!_visible) await showPrimaryOpencodePanel();
|
|
2644
|
+
await switchToSession(sessionId, _tabInfoCache.get(sessionId) || null, { nav });
|
|
2645
|
+
}
|
|
2646
|
+
return { ok: true, sessionId, released: true };
|
|
2647
|
+
}
|
|
2568
2648
|
ensureTab(sessionId, info);
|
|
2569
2649
|
if (focus && _nav.latest() === attachBegan) {
|
|
2570
2650
|
// (Its own navigation, observed after boot landed: a click made while the
|
|
@@ -2577,13 +2657,15 @@ export async function attachOpenCodeAutomation(run, { focus = !!run?.focus, isCa
|
|
|
2577
2657
|
await rehydrateActiveSession().catch(() => {});
|
|
2578
2658
|
}
|
|
2579
2659
|
if (getState().sessionId === sessionId) setRunning(running);
|
|
2580
|
-
|
|
2660
|
+
// (A dispatched worker's failure, with its reason, is on its end card.)
|
|
2661
|
+
if (run.status === 'failed' && run.error && !isDispatchedRun(run) && getState().sessionId === sessionId
|
|
2581
2662
|
&& _tabInfoCache.get(sessionId)?._automationLifecycleError !== run.error) {
|
|
2582
2663
|
const cachedInfo = _tabInfoCache.get(sessionId) || info;
|
|
2583
2664
|
cachedInfo._automationLifecycleError = run.error;
|
|
2584
2665
|
_tabInfoCache.set(sessionId, _stoppedShares.clean(cachedInfo));
|
|
2585
2666
|
pushError({ message: run.error });
|
|
2586
2667
|
}
|
|
2668
|
+
trackWorkerEnd(run, { apply: (view) => showWorkerEnd(sessionId, view) });
|
|
2587
2669
|
renderPill();
|
|
2588
2670
|
return { ok: true, sessionId };
|
|
2589
2671
|
}
|
|
@@ -212,9 +212,13 @@ function render(_container, store, opts = {}) {
|
|
|
212
212
|
);
|
|
213
213
|
const questions = requestsVisibleIn(s.pendingQuestions, s.sessionId, isKnownDescendantSession);
|
|
214
214
|
const retry = retryBannerView(s.sessionStatus);
|
|
215
|
-
|
|
215
|
+
// The panel's last card for the session on screen (a dispatched worker's end): { sig, build } or null.
|
|
216
|
+
const endCard = typeof opts.endCard === 'function' ? opts.endCard(s) : null;
|
|
217
|
+
const hasInteractiveCards = permissions.length > 0 || questions.length > 0 || !!s.showPostPlanActions || !!endCard;
|
|
216
218
|
if (s.messageOrder.length === 0 && !hasInteractiveCards && !s.errors.length && !retry && !revertBanner) {
|
|
217
219
|
_reconcile(_container, [{ key: '__empty__', sig: 'empty', build: _buildEmpty }]);
|
|
220
|
+
// The panel may put SynaBun's greeting in it (ocp-v2-greeting.js).
|
|
221
|
+
try { opts.onEmpty?.(_container.querySelector(':scope > .ocpv2-empty'), s); } catch { /* the empty state stands on its own */ }
|
|
218
222
|
return;
|
|
219
223
|
}
|
|
220
224
|
|
|
@@ -346,6 +350,8 @@ function render(_container, store, opts = {}) {
|
|
|
346
350
|
});
|
|
347
351
|
}
|
|
348
352
|
|
|
353
|
+
if (endCard) desired.push({ key: 'end', sig: `end:${endCard.sig}`, build: endCard.build });
|
|
354
|
+
|
|
349
355
|
_reconcile(_container, desired);
|
|
350
356
|
}
|
|
351
357
|
|
|
@@ -33,6 +33,7 @@ import {
|
|
|
33
33
|
} from './ocp-v2-send-logic.js';
|
|
34
34
|
import { hydrateTranscript } from './ocp-v2-rehydrate.js';
|
|
35
35
|
import { captureBinding, trackBinding, isBindingToken, searchOnBinding } from './ocp-v2-binding.js';
|
|
36
|
+
import { takeGreetingId } from './ocp-v2-greeting.js';
|
|
36
37
|
|
|
37
38
|
const STOR_MODE = 'opencode-v2-mode'; // the last agent picked (build / plan / a custom one)
|
|
38
39
|
const STOR_HISTORY = 'ocp-v2-prompt-history';
|
|
@@ -1449,6 +1450,10 @@ export function mountCompose(rootEl, store = getDefaultStore(), opts = {}) {
|
|
|
1449
1450
|
variant: turn.variant || undefined,
|
|
1450
1451
|
cwd: options.cwd || turn.cwd || undefined,
|
|
1451
1452
|
mcpProfile: turn.mcpProfile || undefined,
|
|
1453
|
+
// A person's own message (the greeting engine keeps when they were last here), and the
|
|
1454
|
+
// greeting this session opened with, told to the model once.
|
|
1455
|
+
human: true,
|
|
1456
|
+
greetingId: takeGreetingId(turnSessionId) || undefined,
|
|
1452
1457
|
});
|
|
1453
1458
|
if (res?.error) return failSend(shouldSuppressAbortError(res.error) ? '' : res.error);
|
|
1454
1459
|
// Delivered: that stands, whatever the panel shows by now.
|
|
@@ -1097,6 +1097,46 @@ const CSS = `
|
|
|
1097
1097
|
cursor: default;
|
|
1098
1098
|
}
|
|
1099
1099
|
|
|
1100
|
+
/* A dispatched worker's end (ui-worker-end.js): the post-plan card's frame. Complete keeps its
|
|
1101
|
+
green; the other ends take the panel's own amber, error red or neutral. The title says the state. */
|
|
1102
|
+
.ocpv2-worker-end .ocpv2-post-plan-body { overflow-wrap: anywhere; }
|
|
1103
|
+
.ocpv2-worker-end .ocpv2-post-plan-btn:focus-visible { outline: 2px solid rgba(255,255,255,0.7); outline-offset: 2px; }
|
|
1104
|
+
.ocpv2-worker-end[data-tone="warn"] {
|
|
1105
|
+
border-color: rgba(232,180,80,0.32);
|
|
1106
|
+
background: rgba(232,180,80,0.04);
|
|
1107
|
+
box-shadow: 0 12px 32px rgba(0,0,0,0.22), 0 0 0 1px rgba(232,180,80,0.08) inset;
|
|
1108
|
+
}
|
|
1109
|
+
.ocpv2-worker-end[data-tone="warn"] .ocpv2-post-plan-header {
|
|
1110
|
+
color: rgba(255,200,120,0.95);
|
|
1111
|
+
background: rgba(232,180,80,0.14);
|
|
1112
|
+
border-color: rgba(232,180,80,0.28);
|
|
1113
|
+
}
|
|
1114
|
+
.ocpv2-worker-end[data-tone="err"] {
|
|
1115
|
+
border-color: rgba(220,100,100,0.32);
|
|
1116
|
+
background: rgba(220,100,100,0.05);
|
|
1117
|
+
box-shadow: 0 12px 32px rgba(0,0,0,0.22), 0 0 0 1px rgba(220,100,100,0.08) inset;
|
|
1118
|
+
}
|
|
1119
|
+
.ocpv2-worker-end[data-tone="err"] .ocpv2-post-plan-header {
|
|
1120
|
+
color: rgba(255,166,166,0.92);
|
|
1121
|
+
background: rgba(220,100,100,0.12);
|
|
1122
|
+
border-color: rgba(220,100,100,0.28);
|
|
1123
|
+
}
|
|
1124
|
+
.ocpv2-worker-end[data-tone="off"] {
|
|
1125
|
+
border-color: rgba(255,255,255,0.12);
|
|
1126
|
+
background: rgba(255,255,255,0.03);
|
|
1127
|
+
box-shadow: 0 12px 32px rgba(0,0,0,0.22);
|
|
1128
|
+
}
|
|
1129
|
+
.ocpv2-worker-end[data-tone="off"] .ocpv2-post-plan-header {
|
|
1130
|
+
color: rgba(255,255,255,0.7);
|
|
1131
|
+
background: rgba(255,255,255,0.06);
|
|
1132
|
+
border-color: rgba(255,255,255,0.12);
|
|
1133
|
+
}
|
|
1134
|
+
.ocpv2-worker-end:not([data-tone="ok"]) .ocpv2-post-plan-saved { color: rgba(255,255,255,0.45); }
|
|
1135
|
+
.ocpv2-worker-end:not([data-tone="ok"]) .ocpv2-post-plan-btn.primary {
|
|
1136
|
+
background: rgba(255,255,255,0.085);
|
|
1137
|
+
border-color: rgba(255,255,255,0.2);
|
|
1138
|
+
}
|
|
1139
|
+
|
|
1100
1140
|
/* Stub shown in place of raw question-tool JSON while the AskUser card handles it */
|
|
1101
1141
|
.ocpv2-part-question-stub {
|
|
1102
1142
|
font-size: 10.5px;
|
|
@@ -486,8 +486,9 @@ export const api = {
|
|
|
486
486
|
sessionDelete: (sessionId) => request({ type: 'session:delete', sessionId }),
|
|
487
487
|
sessionMessages:(sessionId) => request({ type: 'session:messages', sessionId }),
|
|
488
488
|
sessionContext: ({ sessionId, cwd } = {}) => request({ type: 'session:context', sessionId, cwd }),
|
|
489
|
-
|
|
490
|
-
|
|
489
|
+
// `human` / `greetingId`: lib/greeting/ (a server that does not know them ignores them).
|
|
490
|
+
send: ({ sessionId, parts, model, agent, mode, variant, cwd, mcpProfile, noAbortPrev, human, greetingId }) =>
|
|
491
|
+
request({ type: 'message:send', sessionId, parts, model, agent, mode, variant, cwd, mcpProfile, noAbortPrev: !!noAbortPrev, ...(human ? { human: true } : {}), ...(greetingId ? { greetingId } : {}) }, 30 * 60 * 1000),
|
|
491
492
|
abort: (sessionId) => request({ type: 'message:abort', sessionId }),
|
|
492
493
|
compact: ({ sessionId, cwd, mcpProfile, model } = {}) => request({ type: 'session:compact', sessionId, cwd, mcpProfile, model }, 5 * 60 * 1000),
|
|
493
494
|
permissionReply: ({ sessionId, permissionId, response, cwd, message }) =>
|
|
@@ -1,4 +1,16 @@
|
|
|
1
|
-
import { initPwaInstall,
|
|
1
|
+
import { canPromptPwaInstall, initPwaInstall, installGuideMode, installGuideRequested, isStandalone,
|
|
2
|
+
leavePwaInstallHandoff, requestPwaInstall } from './pwa-install.js';
|
|
3
|
+
|
|
4
|
+
// The wizard (first run) and the main UI (setup done) open the browser's dialog from a press the user makes
|
|
5
|
+
// anyway, so a browser that has one shows this page only when Apps > Install as App asked for it. true, false,
|
|
6
|
+
// or null for a read that did not answer: only an answer sends the user on, null keeps the guide.
|
|
7
|
+
async function setupComplete() {
|
|
8
|
+
try {
|
|
9
|
+
const res = await fetch('/api/setup/onboarding', { signal: AbortSignal.timeout(3000) });
|
|
10
|
+
const { setupComplete: done } = res.ok ? await res.json() : {};
|
|
11
|
+
return typeof done === 'boolean' ? done : null;
|
|
12
|
+
} catch { return null; }
|
|
13
|
+
}
|
|
2
14
|
|
|
3
15
|
if (isStandalone()) location.replace('/');
|
|
4
16
|
else {
|
|
@@ -8,6 +20,7 @@ else {
|
|
|
8
20
|
'install-intro': 'O servidor está rodando em segundo plano. Confirme a instalação uma vez no navegador para abrir o SynaBun em sua própria janela.',
|
|
9
21
|
'install-chromium': 'Chrome ou Edge: escolha Instalar como app abaixo ou a opção Instalar app no menu do navegador.',
|
|
10
22
|
'install-safari': 'Safari no macOS: escolha Arquivo → Adicionar ao Dock. Mantenha o nome SynaBun.',
|
|
23
|
+
'install-other': 'O SynaBun é instalado como app pelo Chrome, pelo Edge ou pelo Safari no macOS. Abra-o em um deles para instalar.',
|
|
11
24
|
'install-later': 'Após instalar, abra novamente o iniciador nativo do SynaBun. Ele inicia ou reutiliza o servidor e abre o PWA instalado. Fechar a janela mantém o servidor ativo. Use Apps → Stop Server para pará-lo.',
|
|
12
25
|
'install-button': 'Instalar como app', 'install-continue': 'Continuar para o SynaBun',
|
|
13
26
|
} : {};
|
|
@@ -17,6 +30,7 @@ else {
|
|
|
17
30
|
const instructions = pt
|
|
18
31
|
? 'Use a opção Instalar app do Chrome/Edge ou Arquivo → Adicionar ao Dock no Safari. A instalação exige sua confirmação no navegador.'
|
|
19
32
|
: 'Use Chrome/Edge’s Install app menu or Safari’s File → Add to Dock. Installation requires your browser confirmation.';
|
|
33
|
+
// Before the read below: the event can arrive while it is on its way.
|
|
20
34
|
initPwaInstall({ changed: value => {
|
|
21
35
|
status.textContent = value === 'installed'
|
|
22
36
|
? (pt ? 'Instalado. Abra novamente o iniciador do SynaBun para usar o PWA.' : 'Installed. Reopen the SynaBun launcher to use the PWA.')
|
|
@@ -31,4 +45,18 @@ else {
|
|
|
31
45
|
} catch { status.textContent = instructions; }
|
|
32
46
|
});
|
|
33
47
|
if ('serviceWorker' in navigator) navigator.serviceWorker.register('/sw.js').catch(() => {});
|
|
48
|
+
|
|
49
|
+
// One plain action or one plain instruction, never both: the button only where the browser has a dialog to open.
|
|
50
|
+
const canPrompt = canPromptPwaInstall();
|
|
51
|
+
const show = done => {
|
|
52
|
+
let mode = installGuideMode({ canPrompt, setupComplete: done, requested: installGuideRequested(location.search), vendor: navigator.vendor });
|
|
53
|
+
// The main UI opens the dialog only for the mark this leaves: where it cannot be left, the button stays.
|
|
54
|
+
if (mode === 'handoff' && !leavePwaInstallHandoff()) mode = 'button';
|
|
55
|
+
if (mode === 'wizard' || mode === 'handoff') return location.replace('/');
|
|
56
|
+
document.getElementById(mode === 'button' ? 'install-chromium' : `install-${mode}`).hidden = false;
|
|
57
|
+
document.getElementById('install-button').hidden = mode !== 'button';
|
|
58
|
+
document.querySelector('main').hidden = false;
|
|
59
|
+
};
|
|
60
|
+
if (canPrompt) setupComplete().then(show);
|
|
61
|
+
else show(null);
|
|
34
62
|
}
|
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
#pwa-install { display: grid; place-items: center; overflow: auto; padding: 24px; color: var(--t-bright); }
|
|
3
3
|
#pwa-install main { width: min(560px, 100%); padding: 24px; background: var(--s-panel); border: 1px solid var(--b-light); border-radius: var(--r-card); }
|
|
4
4
|
#pwa-install h1 { font-family: var(--ff-heading); font-size: calc(28px * var(--font-scale)); font-weight: 600; line-height: 1.2; margin: 16px 0; }
|
|
5
|
-
#pwa-install
|
|
6
|
-
#pwa-install
|
|
7
|
-
#pwa-install
|
|
5
|
+
#pwa-install [hidden] { display: none !important; }
|
|
6
|
+
#pwa-install p { font-size: var(--fs-sm); line-height: 1.5; }
|
|
7
|
+
#pwa-install p + p { margin-top: 16px; }
|
|
8
8
|
#pwa-install .actions { display: flex; flex-wrap: wrap; align-items: center; gap: 16px; margin-top: 24px; }
|
|
9
9
|
#pwa-install button { background: var(--s-active); border: 1px solid var(--b-visible); border-radius: var(--r-small); color: var(--t-bright); font: inherit; padding: 8px 12px; cursor: pointer; }
|
|
10
10
|
#pwa-install button:hover { background: var(--s-hover); }
|
|
@@ -1,9 +1,14 @@
|
|
|
1
|
-
/** Shared by the
|
|
1
|
+
/** Shared by the Install as App menu, the first-run wizard, the native launcher's guide page and the main UI it hands over to. */
|
|
2
2
|
export function isStandalone(win = window, nav = navigator) {
|
|
3
3
|
return win.matchMedia('(display-mode: standalone)').matches
|
|
4
4
|
|| win.matchMedia('(display-mode: window-controls-overlay)').matches || !!nav.standalone;
|
|
5
5
|
}
|
|
6
6
|
|
|
7
|
+
/** Chromium only: Safari and Firefox have no install event, so there is nothing a page can open there. */
|
|
8
|
+
export function canPromptPwaInstall(win = window) {
|
|
9
|
+
return 'onbeforeinstallprompt' in win;
|
|
10
|
+
}
|
|
11
|
+
|
|
7
12
|
export function initPwaInstall({ win = window, nav = navigator, changed = () => {} } = {}) {
|
|
8
13
|
win.addEventListener('beforeinstallprompt', event => {
|
|
9
14
|
event.preventDefault();
|
|
@@ -26,3 +31,97 @@ export async function requestPwaInstall(win = window) {
|
|
|
26
31
|
win._pwaInstallPrompt = null; // Browser install events are single-use, including dismissal.
|
|
27
32
|
return outcome;
|
|
28
33
|
}
|
|
34
|
+
|
|
35
|
+
const OFFERED_KEY = 'synabun-pwa-install-offered';
|
|
36
|
+
|
|
37
|
+
function wasOffered(win) {
|
|
38
|
+
if (win._pwaInstallOffered) return true;
|
|
39
|
+
try { return win.sessionStorage.getItem(OFFERED_KEY) === '1'; } catch { return false; }
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* The first-run wizard asks nothing itself: it opens the browser's own install dialog once, from a click the
|
|
44
|
+
* user makes anyway. Call this synchronously in that click, before any await: prompt() needs the click's user
|
|
45
|
+
* activation and uses it up, so never call it from a click that opens a popup, the clipboard or a file picker.
|
|
46
|
+
* Nobody waits for the answer. Without an event it does nothing and the next click tries again; once the dialog
|
|
47
|
+
* has opened, this tab never opens it again (sessionStorage: a reload of the wizard does not ask twice).
|
|
48
|
+
*/
|
|
49
|
+
export function offerPwaInstallOnce({ win = window, nav = navigator } = {}) {
|
|
50
|
+
const prompt = win._pwaInstallPrompt;
|
|
51
|
+
if (!prompt || isStandalone(win, nav) || wasOffered(win)) return false;
|
|
52
|
+
if (nav.userActivation && !nav.userActivation.isActive) return false; // A scripted click: keep the event for a real one.
|
|
53
|
+
win._pwaInstallPrompt = null; // Single-use, whatever the user answers.
|
|
54
|
+
try { Promise.resolve(prompt.prompt()).catch(() => {}); } catch { return false; }
|
|
55
|
+
win._pwaInstallOffered = true;
|
|
56
|
+
try { win.sessionStorage.setItem(OFFERED_KEY, '1'); } catch {}
|
|
57
|
+
return true;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const HANDOFF_KEY = 'synabun-pwa-install-handoff';
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* The guide page's half of the handoff: it leaves a mark for the main UI and goes there instead of asking.
|
|
64
|
+
* sessionStorage, so the mark is this tab's alone. False when it could not be left: the guide stays then.
|
|
65
|
+
*/
|
|
66
|
+
export function leavePwaInstallHandoff(win = window) {
|
|
67
|
+
try { win.sessionStorage.setItem(HANDOFF_KEY, '1'); return true; } catch { return false; }
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// A press that follows a link, or belongs to a shortcut (reload, new tab, DevTools), would spend the one dialog
|
|
71
|
+
// on a page that is leaving or busy. The next press gets it.
|
|
72
|
+
function keepsItsPress(event) {
|
|
73
|
+
return !!(event.ctrlKey || event.metaKey || event.altKey) || /^F\d+$/.test(event.key || '')
|
|
74
|
+
|| !!event.target?.closest?.('a[href]');
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* The main UI's half. It takes the mark, so only the one page load the guide handed over is armed: not a
|
|
79
|
+
* reload, and never a normal tab. Armed, the first click or key press anywhere opens the browser's dialog
|
|
80
|
+
* through offerPwaInstallOnce(), in the capture phase because prompt() must run inside the press and the UI
|
|
81
|
+
* stops many of its events. Without an event yet the next press tries. `leave` names presses to keep out of
|
|
82
|
+
* it (the menu item that opens the dialog itself). Returns the function that disarms it.
|
|
83
|
+
*/
|
|
84
|
+
export function armPwaInstallHandoff({ win = window, nav = navigator, leave = () => false } = {}) {
|
|
85
|
+
let marked = false;
|
|
86
|
+
try {
|
|
87
|
+
marked = win.sessionStorage.getItem(HANDOFF_KEY) === '1';
|
|
88
|
+
win.sessionStorage.removeItem(HANDOFF_KEY);
|
|
89
|
+
} catch {}
|
|
90
|
+
if (!marked || isStandalone(win, nav) || wasOffered(win)) return () => {};
|
|
91
|
+
const disarm = () => ['click', 'keydown'].forEach(type => win.removeEventListener(type, press, true));
|
|
92
|
+
const press = event => {
|
|
93
|
+
if (keepsItsPress(event) || leave(event)) return;
|
|
94
|
+
if (offerPwaInstallOnce({ win, nav })) disarm();
|
|
95
|
+
};
|
|
96
|
+
['click', 'keydown'].forEach(type => win.addEventListener(type, press, true));
|
|
97
|
+
return disarm;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const GUIDE_URL = '/install-app.html';
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Where Apps > Install as App goes when the browser has no dialog ready. The mark says the user asked for the
|
|
104
|
+
* page, so it shows instead of handing over. Safari can add the page it is on to the Dock and the launcher
|
|
105
|
+
* matches that URL without a query, so only a browser with the install event gets the mark.
|
|
106
|
+
*/
|
|
107
|
+
export function installGuideUrl(win = window) {
|
|
108
|
+
return canPromptPwaInstall(win) ? `${GUIDE_URL}?from=menu` : GUIDE_URL;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
export function installGuideRequested(search = '') {
|
|
112
|
+
return new URLSearchParams(search).get('from') === 'menu';
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* What the launcher's guide page is, for this browser and this install:
|
|
117
|
+
* 'app' (already the installed app: leave), 'wizard' (first run where the wizard opens the dialog: leave),
|
|
118
|
+
* 'handoff' (setup done and nobody asked for the page: leave, the main UI opens the dialog),
|
|
119
|
+
* 'button' (one button that opens the browser's dialog: the page was asked for, or the setup read did not
|
|
120
|
+
* answer), 'safari' or 'other' (plain instructions).
|
|
121
|
+
*/
|
|
122
|
+
export function installGuideMode({ standalone = false, canPrompt = false, setupComplete = null, requested = false, vendor = '' } = {}) {
|
|
123
|
+
if (standalone) return 'app';
|
|
124
|
+
if (!canPrompt) return /apple/i.test(vendor) ? 'safari' : 'other';
|
|
125
|
+
if (setupComplete === false) return 'wizard';
|
|
126
|
+
return setupComplete === true && !requested ? 'handoff' : 'button';
|
|
127
|
+
}
|