synabun 2.0.0-beta → 2.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +65 -0
- package/NOTICE +5 -0
- package/README.md +1 -1
- package/THIRD-PARTY-LICENSES.md +51 -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/launcher.mjs +40 -0
- package/lib/claude-hooks.js +61 -8
- package/lib/client-config-repair.js +4 -3
- package/lib/data-home-migration.js +4 -0
- package/lib/desktop-pwa.js +206 -0
- package/lib/external-tools.js +221 -0
- package/lib/first-launch-protection.js +5 -0
- package/lib/packaged-runtime.js +110 -0
- package/lib/start-launcher.js +1396 -0
- package/mcp-server/package-lock.json +2 -2
- package/mcp-server/package.json +1 -3
- package/neural-interface/i18n/en.json +1540 -154
- package/neural-interface/i18n/pt-BR.json +1538 -153
- 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-envelope.js +9 -6
- 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/augmented-path.js +23 -3
- package/neural-interface/lib/browser-background-tab.js +134 -0
- package/neural-interface/lib/browser-language.js +83 -0
- package/neural-interface/lib/browser-screenshot.js +678 -61
- package/neural-interface/lib/claude-agent-bridge.js +87 -153
- package/neural-interface/lib/claude-executable.js +241 -0
- package/neural-interface/lib/claude-model-catalog.js +82 -93
- 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-binary-runtime.js +69 -337
- package/neural-interface/lib/native-loop-providers.js +40 -49
- package/neural-interface/lib/offline-page.js +54 -0
- 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/session-title-generator.js +5 -0
- package/neural-interface/lib/synabun-update-plan.js +19 -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 -253
- package/neural-interface/package.json +13 -2
- package/neural-interface/public/install-app.html +34 -0
- package/neural-interface/public/manifest.json +2 -0
- package/neural-interface/public/offline.html +402 -66
- package/neural-interface/public/onboarding.html +264 -147
- 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 +26 -64
- 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-rack-styles.js +0 -3
- package/neural-interface/public/shared/assistant/asst-render.js +219 -67
- 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 +162 -6
- 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/command-runner-model.js +30 -0
- 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 +169 -170
- 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 +62 -0
- package/neural-interface/public/shared/pwa-install.css +12 -0
- package/neural-interface/public/shared/pwa-install.js +127 -0
- 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-rules.js +160 -0
- package/neural-interface/public/shared/settings/settings-user-context.js +154 -0
- package/neural-interface/public/shared/start-bridge.js +802 -0
- package/neural-interface/public/shared/styleguide/sg-colors.js +5 -2
- package/neural-interface/public/shared/styleguide/sg-import-export.js +4 -3
- package/neural-interface/public/shared/styleguide/sg-kit.js +4 -1
- package/neural-interface/public/shared/styleguide/sg-panel.js +9 -1
- package/neural-interface/public/shared/styleguide/sg-typography.js +1 -0
- package/neural-interface/public/shared/styles.css +2219 -1117
- package/neural-interface/public/shared/synabun-mascot.js +20 -56
- package/neural-interface/public/shared/term-ready.js +307 -0
- package/neural-interface/public/shared/tunnel-view.js +167 -0
- package/neural-interface/public/shared/ui-claude-panel.js +160 -19
- package/neural-interface/public/shared/ui-command-runner.js +251 -468
- 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-judgments.js +1 -1
- package/neural-interface/public/shared/ui-keybinds.js +4 -0
- package/neural-interface/public/shared/ui-loading.js +367 -12
- package/neural-interface/public/shared/ui-menubar.js +21 -55
- 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 +491 -547
- 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 +153 -149
- 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 +3 -1
- package/neural-interface/public/variant/2d/main.js +2 -1
- package/neural-interface/public/variant/3d/main.js +2 -1
- 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/rebuild-pty.js +15 -12
- package/neural-interface/scripts/vendor-codemirror-entry.js +121 -0
- package/neural-interface/server.js +1092 -1212
- package/neural-interface/templates/rulesets/README.md +1 -1
- package/package.json +13 -7
- package/postinstall.js +11 -81
- package/setup.js +111 -6
- package/benchmarks/browser-live-results.json +0 -79
- package/benchmarks/browser-v2-results.json +0 -258
- package/docs/browser-v2.md +0 -109
- package/docs/hook-integrity.md +0 -89
- package/docs/judgments.md +0 -182
- package/docs/style-guide.md +0 -246
- 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/scripts/audit-claude-hook-usage.mjs +0 -12
- package/scripts/audit-memory-duplicates.mjs +0 -44
- package/scripts/measure-claude-hook-overhead.mjs +0 -77
- package/scripts/recover-user-config.mjs +0 -254
- 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),
|
|
@@ -16,10 +16,11 @@ import { getWhiteboardElementById } from './ui-whiteboard.js';
|
|
|
16
16
|
import { createFrameRenderer } from './utils.js';
|
|
17
17
|
import { notify, NOTIF_TYPE } from './ui-notifications.js';
|
|
18
18
|
import { getProviderMeta } from './provider-icons.js';
|
|
19
|
+
import { readinessKind, typeWhenReady } from './term-ready.js';
|
|
19
20
|
import { initRendererManager, acquireRenderer, disposeRenderer, hasWebgl, noteFocus, adoptRenderer, rendererState, rendererStats } from './term-renderer.js';
|
|
20
21
|
import { mountAssistant } from './assistant/asst-panel.js';
|
|
21
22
|
import { getAssistantSession } from './assistant/asst-api.js';
|
|
22
|
-
import { brainLabel, normalizeBrain, readStoredBrain } from './assistant/asst-state.js';
|
|
23
|
+
import { brainLabel, normalizeBrain, readStoredBrain, restoredSessionName, sessionNameGivenUp } from './assistant/asst-state.js';
|
|
23
24
|
import { initAssistant, registerAssistantHost, openAssistant, assistantOwnerOf, fetchLiveAssistantSessions, isAssistantSessionLive } from './ui-assistant.js';
|
|
24
25
|
|
|
25
26
|
const $ = (id) => document.getElementById(id);
|
|
@@ -1424,10 +1425,22 @@ function _accumReplay(sess, text) {
|
|
|
1424
1425
|
if (sess._replayBuf.length > 16384) sess._replayBuf = sess._replayBuf.slice(-8192);
|
|
1425
1426
|
}
|
|
1426
1427
|
|
|
1427
|
-
|
|
1428
|
-
|
|
1428
|
+
// `replaced`: a snapshot just became the whole screen (connect / reconnect),
|
|
1429
|
+
// so `text` is everything there is rather than more of the stream.
|
|
1430
|
+
function _notifyOutputWatchers(sess, text, replaced = false) {
|
|
1431
|
+
if (!sess?._outputWatchers?.size) return;
|
|
1432
|
+
if (!text && !replaced) return;
|
|
1429
1433
|
for (const w of [...sess._outputWatchers]) {
|
|
1430
|
-
try { w(text); } catch {}
|
|
1434
|
+
try { w(text || '', replaced); } catch {}
|
|
1435
|
+
}
|
|
1436
|
+
}
|
|
1437
|
+
|
|
1438
|
+
// A socket of this session opened (first connect or a reconnect). The wait for
|
|
1439
|
+
// readiness in _sendOnceReady starts its clock here, not when it was asked.
|
|
1440
|
+
function _notifySocketOpen(sess) {
|
|
1441
|
+
if (!sess?._openWatchers?.size) return;
|
|
1442
|
+
for (const w of [...sess._openWatchers]) {
|
|
1443
|
+
try { w(); } catch {}
|
|
1431
1444
|
}
|
|
1432
1445
|
}
|
|
1433
1446
|
|
|
@@ -1500,7 +1513,7 @@ function _handleTermWsEvent(e, ctx) {
|
|
|
1500
1513
|
else term?.write(msg.data);
|
|
1501
1514
|
}
|
|
1502
1515
|
if (NOTIF_TRACKED_CLI_PROFILES.has(profile)) _scheduleCliStatusCheck(sessionId);
|
|
1503
|
-
_notifyOutputWatchers(sess, text);
|
|
1516
|
+
_notifyOutputWatchers(sess, text, !!wantsReset);
|
|
1504
1517
|
}
|
|
1505
1518
|
if (msg.type === 'exit') {
|
|
1506
1519
|
if (sess) sess._exitReceived = true;
|
|
@@ -1538,6 +1551,7 @@ function _onTermWsOpen(sessionId, ws, fallback = null) {
|
|
|
1538
1551
|
try { (s?._terminalWriter || fallback?._terminalWriter)?.reset?.(); } catch {}
|
|
1539
1552
|
send({ type: 'hello', flow: 1 });
|
|
1540
1553
|
if (document.hidden) send({ type: 'visibility', visible: false });
|
|
1554
|
+
_notifySocketOpen(s);
|
|
1541
1555
|
const term = s?.term;
|
|
1542
1556
|
if (!term) return;
|
|
1543
1557
|
// Hidden container (restore, parked dock) → no resize now; _scheduleFit
|
|
@@ -3381,6 +3395,7 @@ async function reconnectBrowserSession(sessionId, liveData, saved) {
|
|
|
3381
3395
|
ws, viewport, ro, renderer: null,
|
|
3382
3396
|
dead: false, pinned: saved?.pinned || false,
|
|
3383
3397
|
_userRenamed: saved?.userRenamed || false,
|
|
3398
|
+
_floatColor: saved?.floatColor || null,
|
|
3384
3399
|
_isBrowser: true, _browserUrl: currentUrl, _browserTitle: liveData?.title || '',
|
|
3385
3400
|
_browserCanvas: canvas, _browserCtx: ctx, _frameRenderer: frameRenderer, _visibilityHandler,
|
|
3386
3401
|
_idleTimer: null, _screencastIdlePaused: false,
|
|
@@ -3452,7 +3467,7 @@ function _updateAssistantBrainChip(session) {
|
|
|
3452
3467
|
chip.hidden = !label;
|
|
3453
3468
|
}
|
|
3454
3469
|
|
|
3455
|
-
/** 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). */
|
|
3456
3471
|
function _syncAssistantLabel(session) {
|
|
3457
3472
|
if (!session?._isAssistant || !session._assistant) return;
|
|
3458
3473
|
if (session._assistant.getTitle() !== session.label) session._assistant.setTitle(session.label);
|
|
@@ -3469,6 +3484,11 @@ function _mountAssistantSession(sessionId, { meta = null, saved = null, brain =
|
|
|
3469
3484
|
if (existing) { if (!_restoringLayout) _focusAssistantTab(existing); return existing; }
|
|
3470
3485
|
|
|
3471
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);
|
|
3472
3492
|
const viewport = document.createElement('div');
|
|
3473
3493
|
viewport.className = 'term-viewport assistant-viewport';
|
|
3474
3494
|
viewport.dataset.sessionId = sessionId;
|
|
@@ -3480,13 +3500,13 @@ function _mountAssistantSession(sessionId, { meta = null, saved = null, brain =
|
|
|
3480
3500
|
id: sessionId,
|
|
3481
3501
|
profile: 'assistant',
|
|
3482
3502
|
cwd: resolvedBrain.project || null,
|
|
3483
|
-
label:
|
|
3503
|
+
label: named || (givenUp ? '' : saved?.label) || meta?.title || profileDef?.label || 'Assistant',
|
|
3484
3504
|
term: null, fitAddon: null, searchAddon: null,
|
|
3485
3505
|
ws: null, // no PTY socket — the panel owns /ws/assistant/:id
|
|
3486
3506
|
viewport, ro: null, renderer: null,
|
|
3487
3507
|
dead: false,
|
|
3488
3508
|
pinned: saved?.pinned || false,
|
|
3489
|
-
_userRenamed: saved?.userRenamed || !!
|
|
3509
|
+
_userRenamed: !givenUp && (saved?.userRenamed || !!named),
|
|
3490
3510
|
_floatColor: saved?.floatColor || null,
|
|
3491
3511
|
_isAssistant: true, // flag for tab-type-specific logic
|
|
3492
3512
|
_assistant: null, // controller (set right after mount)
|
|
@@ -3510,6 +3530,19 @@ function _mountAssistantSession(sessionId, { meta = null, saved = null, brain =
|
|
|
3510
3530
|
if (titleEl && titleEl.contentEditable !== 'true') titleEl.textContent = next;
|
|
3511
3531
|
saveSessionRegistry();
|
|
3512
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
|
+
},
|
|
3513
3546
|
setStatus(status) { setSessionStatusBadge(sessionId, status); },
|
|
3514
3547
|
setBrain(b) {
|
|
3515
3548
|
const s = live();
|
|
@@ -3524,7 +3557,7 @@ function _mountAssistantSession(sessionId, { meta = null, saved = null, brain =
|
|
|
3524
3557
|
requestDetach() { const idx = _sessions.findIndex(s => s.id === sessionId); if (idx >= 0 && !_detachedTabs.has(sessionId)) detachTab(idx); },
|
|
3525
3558
|
};
|
|
3526
3559
|
session._assistant = mountAssistant(viewport, { sessionId, brain: resolvedBrain, host, session: meta, label: session.label, reattach });
|
|
3527
|
-
if (session._userRenamed && session.label) session._assistant.setTitle(session.label);
|
|
3560
|
+
if (session._userRenamed && session.label) session._assistant.setTitle(session.label, { restored: true });
|
|
3528
3561
|
|
|
3529
3562
|
renderTabBar();
|
|
3530
3563
|
if (_restoringLayout) {
|
|
@@ -4488,13 +4521,29 @@ function _applyFloatColor(win, colorId) {
|
|
|
4488
4521
|
win.style.setProperty('--fh', String(c.h));
|
|
4489
4522
|
win.style.setProperty('--fs', c.s + '%');
|
|
4490
4523
|
}
|
|
4491
|
-
// Update color strip
|
|
4524
|
+
// Update color strip. Only the color: the `background` shorthand would reset
|
|
4525
|
+
// the stylesheet's background-clip, and the dot would fill its whole hit area.
|
|
4492
4526
|
const strip = win.querySelector('.term-float-color-strip');
|
|
4493
4527
|
if (strip) {
|
|
4494
|
-
strip.style.
|
|
4528
|
+
strip.style.backgroundColor = c.s === 0 ? 'rgba(255,255,255,0.15)' : `hsl(${c.h}, ${Math.round(c.s * 0.5)}%, 30%)`;
|
|
4495
4529
|
}
|
|
4496
4530
|
}
|
|
4497
4531
|
|
|
4532
|
+
/** Place the palette popup by the color button: above it, or below when there
|
|
4533
|
+
* is no room, with its first swatch over the dot and inside the viewport. */
|
|
4534
|
+
function _placeFloatColorPicker(picker, colorBtn) {
|
|
4535
|
+
const btn = colorBtn.getBoundingClientRect();
|
|
4536
|
+
const box = picker.getBoundingClientRect();
|
|
4537
|
+
const first = picker.firstElementChild?.getBoundingClientRect();
|
|
4538
|
+
const inset = first ? first.left + first.width / 2 - box.left : 0;
|
|
4539
|
+
const margin = 8, gap = 6;
|
|
4540
|
+
const left = btn.left + btn.width / 2 - inset;
|
|
4541
|
+
let top = btn.top - gap - box.height;
|
|
4542
|
+
if (top < margin) top = btn.bottom + gap;
|
|
4543
|
+
picker.style.left = Math.max(margin, Math.min(left, window.innerWidth - box.width - margin)) + 'px';
|
|
4544
|
+
picker.style.top = Math.max(margin, Math.min(top, window.innerHeight - box.height - margin)) + 'px';
|
|
4545
|
+
}
|
|
4546
|
+
|
|
4498
4547
|
function getRightPanelReservedWidth(cs = getComputedStyle(document.documentElement)) {
|
|
4499
4548
|
return parseFloat(cs.getPropertyValue('--right-panel-width'))
|
|
4500
4549
|
|| parseFloat(cs.getPropertyValue('--claude-panel-width'))
|
|
@@ -4813,19 +4862,18 @@ function detachTab(idx, opts = {}) {
|
|
|
4813
4862
|
if (existing) { existing.remove(); return; }
|
|
4814
4863
|
const picker = document.createElement('div');
|
|
4815
4864
|
picker.className = 'float-color-picker';
|
|
4816
|
-
//
|
|
4817
|
-
|
|
4818
|
-
|
|
4819
|
-
picker.style.bottom = (window.innerHeight - btnRect.top + 6) + 'px';
|
|
4820
|
-
picker.style.top = 'auto';
|
|
4865
|
+
// The session object may have been replaced by _pushSession after a
|
|
4866
|
+
// reconnection — read and write the color on the one in _sessions
|
|
4867
|
+
const live = () => _sessions.find(s => s.id === session.id) || session;
|
|
4821
4868
|
FLOAT_COLORS.forEach(c => {
|
|
4822
4869
|
const swatch = document.createElement('button');
|
|
4823
4870
|
swatch.className = 'float-color-swatch';
|
|
4824
|
-
if (c.id === (
|
|
4871
|
+
if (c.id === (live()._floatColor || 'blue')) swatch.classList.add('active');
|
|
4825
4872
|
swatch.style.background = c.s === 0 ? 'rgba(255,255,255,0.15)' : `hsl(${c.h}, ${Math.round(c.s * 0.5)}%, 25%)`;
|
|
4826
4873
|
swatch.addEventListener('click', (ev) => {
|
|
4827
4874
|
ev.stopPropagation();
|
|
4828
4875
|
session._floatColor = c.id;
|
|
4876
|
+
live()._floatColor = c.id;
|
|
4829
4877
|
_applyFloatColor(win, c.id);
|
|
4830
4878
|
saveSessionRegistry();
|
|
4831
4879
|
saveTerminalLayout();
|
|
@@ -4834,8 +4882,10 @@ function detachTab(idx, opts = {}) {
|
|
|
4834
4882
|
picker.appendChild(swatch);
|
|
4835
4883
|
});
|
|
4836
4884
|
document.body.appendChild(picker);
|
|
4837
|
-
|
|
4885
|
+
_placeFloatColorPicker(picker, colorBtn);
|
|
4886
|
+
// Close on outside click (the dot closes it through the toggle above)
|
|
4838
4887
|
const close = (ev) => {
|
|
4888
|
+
if (colorBtn.contains(ev.target) && picker.isConnected) return;
|
|
4839
4889
|
if (!picker.contains(ev.target)) { picker.remove(); document.removeEventListener('mousedown', close); }
|
|
4840
4890
|
};
|
|
4841
4891
|
setTimeout(() => document.addEventListener('mousedown', close), 0);
|
|
@@ -5722,6 +5772,8 @@ export async function initTerminal() {
|
|
|
5722
5772
|
window.addEventListener('keydown', (e) => {
|
|
5723
5773
|
if (e.key !== 'Escape') return;
|
|
5724
5774
|
const target = e.target;
|
|
5775
|
+
// The command library owns Escape for its dialogs, search and window.
|
|
5776
|
+
if (target?.closest?.('.command-runner-panel')) return;
|
|
5725
5777
|
let session = null;
|
|
5726
5778
|
if (target?.classList?.contains?.('xterm-helper-textarea')) {
|
|
5727
5779
|
const sid = target.closest('.term-viewport')?.dataset?.sessionId;
|
|
@@ -6337,8 +6389,19 @@ async function launchDetached(profile, initialMessage, autoSubmit, opts = {}) {
|
|
|
6337
6389
|
async function runCommandInNewTab({ command, cwd, label }) {
|
|
6338
6390
|
if (!command) return;
|
|
6339
6391
|
await _whenOpenIdle();
|
|
6340
|
-
|
|
6341
|
-
if (!session)
|
|
6392
|
+
let session = await openSession('shell', cwd || null);
|
|
6393
|
+
if (!session) {
|
|
6394
|
+
// Another open slipped in between the queue and this call (openSession
|
|
6395
|
+
// returns null instead of stacking): take one more turn rather than
|
|
6396
|
+
// dropping the command without a word.
|
|
6397
|
+
await _whenOpenIdle();
|
|
6398
|
+
session = await openSession('shell', cwd || null);
|
|
6399
|
+
}
|
|
6400
|
+
if (!session) {
|
|
6401
|
+
ensurePanel();
|
|
6402
|
+
showTermToast(`Could not open a terminal for ${label || 'this command'}. Try again`);
|
|
6403
|
+
return;
|
|
6404
|
+
}
|
|
6342
6405
|
if (label) {
|
|
6343
6406
|
session.label = label;
|
|
6344
6407
|
renderTabBar();
|
|
@@ -6414,140 +6477,72 @@ async function attachDetached(terminalSessionId, profile, initialMessage, autoSu
|
|
|
6414
6477
|
}
|
|
6415
6478
|
}
|
|
6416
6479
|
|
|
6417
|
-
/**
|
|
6418
|
-
*
|
|
6419
|
-
*
|
|
6420
|
-
*
|
|
6480
|
+
/** Type a message into a session once it can take it.
|
|
6481
|
+
* A shell tab (Command Runner) is ready when its line editor is: zsh, bash 5
|
|
6482
|
+
* and fish announce it (bracketed paste on), every other prompt is read off
|
|
6483
|
+
* the last line, and a prompt nothing recognises is taken once the output
|
|
6484
|
+
* goes quiet. A CLI keeps its own reading: watch for its input prompt (">" /
|
|
6485
|
+
* ❯) and fall back after 15 s, because a CLI swallows what is typed before it
|
|
6486
|
+
* is up. The rules, and the sequence wait → settle → type, are in term-ready.js.
|
|
6421
6487
|
*
|
|
6422
|
-
*
|
|
6423
|
-
*
|
|
6424
|
-
*
|
|
6425
|
-
*
|
|
6488
|
+
* The wait lives on the session, not on one socket: a socket that fails
|
|
6489
|
+
* before it opens, or is swapped by a reconnect, changes nothing, and the
|
|
6490
|
+
* readiness window only starts once a socket of the session is open. Output
|
|
6491
|
+
* that arrived before this call is read from session._replayBuf, and the
|
|
6492
|
+
* terminal's own parser state covers a prompt that only came back inside a
|
|
6493
|
+
* snapshot. Resolves { ok, reason } once the message is typed (or could not be). */
|
|
6426
6494
|
function _sendOnceReady(session, message, autoSubmit) {
|
|
6427
6495
|
if (!session?.ws) {
|
|
6428
6496
|
console.warn('[SynaBun] _sendOnceReady: no session.ws — aborting');
|
|
6429
|
-
return;
|
|
6430
|
-
}
|
|
6431
|
-
|
|
6432
|
-
|
|
6433
|
-
|
|
6434
|
-
|
|
6435
|
-
|
|
6436
|
-
|
|
6437
|
-
|
|
6438
|
-
|
|
6439
|
-
|
|
6440
|
-
|
|
6441
|
-
|
|
6442
|
-
|
|
6443
|
-
|
|
6444
|
-
|
|
6445
|
-
|
|
6446
|
-
|
|
6447
|
-
|
|
6448
|
-
|
|
6449
|
-
|
|
6450
|
-
|
|
6451
|
-
|
|
6452
|
-
|
|
6453
|
-
|
|
6454
|
-
function doSend() {
|
|
6455
|
-
if (_sent) return;
|
|
6456
|
-
_sent = true;
|
|
6457
|
-
// Clean up watcher and timer
|
|
6458
|
-
if (_listener) session._outputWatchers?.delete(_listener);
|
|
6459
|
-
if (_fallbackTimer) clearTimeout(_fallbackTimer);
|
|
6460
|
-
console.log('[SynaBun] _sendOnceReady: doSend triggered, ws.readyState =', session.ws?.readyState);
|
|
6461
|
-
// Small delay after detecting ready — let the CLI fully settle
|
|
6462
|
-
setTimeout(() => {
|
|
6463
|
-
if (session.ws?.readyState !== WebSocket.OPEN) {
|
|
6464
|
-
console.warn('[SynaBun] _sendOnceReady: WS not OPEN at send time, readyState =', session.ws?.readyState);
|
|
6465
|
-
return;
|
|
6466
|
-
}
|
|
6467
|
-
// Chunk the input to avoid ConPTY input buffer overflow on Windows.
|
|
6468
|
-
// Writing 1000+ chars in a single pty.write() can silently drop data.
|
|
6469
|
-
const full = message + '\r';
|
|
6470
|
-
const CHUNK = 256;
|
|
6471
|
-
const DELAY = 30; // ms between chunks
|
|
6472
|
-
console.log('[SynaBun] _sendOnceReady: sending prompt in chunks (' + message.length + ' chars, ' + Math.ceil(full.length / CHUNK) + ' chunks)');
|
|
6473
|
-
for (let i = 0; i < full.length; i += CHUNK) {
|
|
6474
|
-
const chunk = full.slice(i, i + CHUNK);
|
|
6475
|
-
const delay = (i / CHUNK) * DELAY;
|
|
6476
|
-
setTimeout(() => {
|
|
6477
|
-
if (session.ws?.readyState === WebSocket.OPEN) {
|
|
6478
|
-
session.ws.send(JSON.stringify({ type: 'input', data: chunk }));
|
|
6479
|
-
}
|
|
6480
|
-
}, delay);
|
|
6481
|
-
}
|
|
6482
|
-
if (autoSubmit) {
|
|
6483
|
-
// Send a second Enter after all chunks + extra delay to auto-confirm Claude Code's prompt
|
|
6484
|
-
const totalChunkTime = Math.ceil(full.length / CHUNK) * DELAY;
|
|
6485
|
-
setTimeout(() => {
|
|
6486
|
-
if (session.ws?.readyState === WebSocket.OPEN) {
|
|
6487
|
-
console.log('[SynaBun] _sendOnceReady: sending auto-submit Enter');
|
|
6488
|
-
session.ws.send(JSON.stringify({ type: 'input', data: '\r' }));
|
|
6489
|
-
}
|
|
6490
|
-
}, totalChunkTime + 4000);
|
|
6491
|
-
}
|
|
6492
|
-
}, 500);
|
|
6493
|
-
}
|
|
6494
|
-
|
|
6495
|
-
// Check if the CLI prompt already appeared in replay data that arrived
|
|
6496
|
-
// before this function was called (the core race condition fix).
|
|
6497
|
-
function checkReplayBuffer() {
|
|
6498
|
-
if (session._replayBuf) {
|
|
6499
|
-
const clean = session._replayBuf.replace(ANSI_RE, '');
|
|
6500
|
-
if (READY_PATTERNS.some(p => p.test(clean))) {
|
|
6501
|
-
console.log('[SynaBun] _sendOnceReady: ready pattern found in existing replay buffer — sending immediately');
|
|
6502
|
-
doSend();
|
|
6503
|
-
return true;
|
|
6497
|
+
return Promise.resolve({ ok: false, reason: 'no-socket' });
|
|
6498
|
+
}
|
|
6499
|
+
if (!message) return Promise.resolve({ ok: false, reason: 'empty' });
|
|
6500
|
+
|
|
6501
|
+
const kind = readinessKind(session.profile);
|
|
6502
|
+
const alive = () => !session.dead && !session._exitReceived && _sessions.includes(session);
|
|
6503
|
+
|
|
6504
|
+
return typeWhenReady({
|
|
6505
|
+
kind,
|
|
6506
|
+
text: message + '\r',
|
|
6507
|
+
socket: () => session.ws,
|
|
6508
|
+
alive,
|
|
6509
|
+
OPEN: WebSocket.OPEN,
|
|
6510
|
+
// The emulator's own state: true while the shell's line editor is reading.
|
|
6511
|
+
lineEditorActive: () => session.term?.modes?.bracketedPasteMode === true,
|
|
6512
|
+
// What already arrived (replay data lands before an attach calls this).
|
|
6513
|
+
buffered: session._replayBuf || '',
|
|
6514
|
+
subscribe: ({ output, opened, parsed }) => {
|
|
6515
|
+
(session._outputWatchers ||= new Set()).add(output);
|
|
6516
|
+
(session._openWatchers ||= new Set()).add(opened);
|
|
6517
|
+
// Bracketed paste can arrive inside a snapshot's escape data, where the
|
|
6518
|
+
// watcher only sees plain text: ask the parser after each write instead.
|
|
6519
|
+
let onParsed = null;
|
|
6520
|
+
if (kind === 'shell') {
|
|
6521
|
+
try { onParsed = session.term?.onWriteParsed?.(parsed) || null; } catch {}
|
|
6504
6522
|
}
|
|
6523
|
+
return () => {
|
|
6524
|
+
session._outputWatchers?.delete(output);
|
|
6525
|
+
session._openWatchers?.delete(opened);
|
|
6526
|
+
try { onParsed?.dispose(); } catch {}
|
|
6527
|
+
};
|
|
6528
|
+
},
|
|
6529
|
+
onReady: (reason, waitedMs) => console.log(`[SynaBun] _sendOnceReady: ${kind} ready (${reason}) after ${waitedMs} ms`),
|
|
6530
|
+
}).then((result) => {
|
|
6531
|
+
if (!result.ok) {
|
|
6532
|
+
console.warn(`[SynaBun] _sendOnceReady: not delivered (${result.reason}, ${result.sent}/${result.chunks} chunks)`);
|
|
6533
|
+
if (alive()) showTermToast('The command did not reach the terminal. Run it again');
|
|
6534
|
+
return result;
|
|
6535
|
+
}
|
|
6536
|
+
if (autoSubmit) {
|
|
6537
|
+
// A second Enter confirms Claude Code's prompt once the paste settled
|
|
6538
|
+
setTimeout(() => {
|
|
6539
|
+
if (session.ws?.readyState === WebSocket.OPEN) {
|
|
6540
|
+
session.ws.send(JSON.stringify({ type: 'input', data: '\r' }));
|
|
6541
|
+
}
|
|
6542
|
+
}, 4000);
|
|
6505
6543
|
}
|
|
6506
|
-
return
|
|
6507
|
-
}
|
|
6508
|
-
|
|
6509
|
-
function onOutput(text) {
|
|
6510
|
-
_outputBuf += text;
|
|
6511
|
-
// Strip ANSI codes before matching — CLI prompts are colorized
|
|
6512
|
-
const clean = _outputBuf.replace(ANSI_RE, '');
|
|
6513
|
-
if (READY_PATTERNS.some(p => p.test(clean))) {
|
|
6514
|
-
console.log('[SynaBun] _sendOnceReady: ready pattern matched in live output');
|
|
6515
|
-
doSend();
|
|
6516
|
-
}
|
|
6517
|
-
}
|
|
6518
|
-
|
|
6519
|
-
function attach() {
|
|
6520
|
-
// Before watching future output, check what already arrived.
|
|
6521
|
-
// The replay from ws.onopen fires synchronously before this function runs
|
|
6522
|
-
// (reconnectSession awaits, replay lands in ws.onmessage, accumulates
|
|
6523
|
-
// in session._replayBuf). If the CLI prompt is already there, send now.
|
|
6524
|
-
if (checkReplayBuffer()) return;
|
|
6525
|
-
|
|
6526
|
-
console.log('[SynaBun] _sendOnceReady: attach() — adding output watcher (no prompt in replay buffer yet)');
|
|
6527
|
-
// Session-level output watcher (fed by _handleTermWsEvent) — unlike a raw
|
|
6528
|
-
// ws 'message' listener, this survives _reconnectTerminalWs swapping session.ws.
|
|
6529
|
-
_listener = onOutput;
|
|
6530
|
-
(session._outputWatchers ||= new Set()).add(_listener);
|
|
6531
|
-
// Fallback: if no prompt detected within MAX_WAIT, send anyway
|
|
6532
|
-
_fallbackTimer = setTimeout(() => {
|
|
6533
|
-
console.warn('[SynaBun] _sendOnceReady: CLI prompt not detected, sending after timeout');
|
|
6534
|
-
doSend();
|
|
6535
|
-
}, MAX_WAIT);
|
|
6536
|
-
}
|
|
6537
|
-
|
|
6538
|
-
if (session.ws.readyState === WebSocket.OPEN) attach();
|
|
6539
|
-
else {
|
|
6540
|
-
session.ws.addEventListener('open', attach, { once: true });
|
|
6541
|
-
// Safety net: if the WS never opens, force-send after MAX_WAIT to avoid silent failure
|
|
6542
|
-
setTimeout(() => {
|
|
6543
|
-
if (!_sent) {
|
|
6544
|
-
// One last check of the replay buffer before force-sending
|
|
6545
|
-
if (checkReplayBuffer()) return;
|
|
6546
|
-
console.warn('[SynaBun] _sendOnceReady: WS never opened — forcing send attempt');
|
|
6547
|
-
doSend();
|
|
6548
|
-
}
|
|
6549
|
-
}, MAX_WAIT + 2000);
|
|
6550
|
-
}
|
|
6544
|
+
return result;
|
|
6545
|
+
});
|
|
6551
6546
|
}
|
|
6552
6547
|
|
|
6553
6548
|
/** Snapshot terminal state for workspace save */
|
|
@@ -6566,6 +6561,7 @@ export function getTerminalSnapshot() {
|
|
|
6566
6561
|
isDetached: _detachedTabs.has(s.id),
|
|
6567
6562
|
userRenamed: s._userRenamed || false,
|
|
6568
6563
|
claudeSessionId: s._claudeSessionId || null,
|
|
6564
|
+
floatColor: s._floatColor || null,
|
|
6569
6565
|
assistantSessionId: s._isAssistant ? s.id : null,
|
|
6570
6566
|
brain: s._isAssistant ? (s._brain || null) : null,
|
|
6571
6567
|
})),
|
|
@@ -6622,12 +6618,20 @@ function applyTerminalLayout(snap) {
|
|
|
6622
6618
|
}
|
|
6623
6619
|
if (!session) continue;
|
|
6624
6620
|
|
|
6621
|
+
// The snapshot's color goes onto the session before detachTab reads it:
|
|
6622
|
+
// a session a workspace restore rebuilt has none and would get a random one
|
|
6623
|
+
const savedColor = FLOAT_COLORS.some(c => c.id === dt.floatColor) ? dt.floatColor : null;
|
|
6624
|
+
if (savedColor) session._floatColor = savedColor;
|
|
6625
|
+
|
|
6625
6626
|
const idx = _sessions.indexOf(session);
|
|
6626
6627
|
if (!_detachedTabs.has(session.id)) {
|
|
6627
6628
|
detachTab(idx);
|
|
6628
6629
|
}
|
|
6629
6630
|
const tabState = _detachedTabs.get(session.id);
|
|
6630
6631
|
if (tabState) {
|
|
6632
|
+
// Already floating (browser tabs detach while reconnecting): recolor it
|
|
6633
|
+
if (savedColor) _applyFloatColor(tabState.el, savedColor);
|
|
6634
|
+
|
|
6631
6635
|
// Validate saved dimensions — enforce minimums and keep on screen
|
|
6632
6636
|
const minW = 280, minH = 160;
|
|
6633
6637
|
const w = Math.max(minW, dt.width || minW);
|
|
@@ -6745,7 +6749,7 @@ export async function restoreTerminalSnapshot(snap) {
|
|
|
6745
6749
|
}
|
|
6746
6750
|
if (liveIds.has(saved.id)) {
|
|
6747
6751
|
const live = liveMap.get(saved.id);
|
|
6748
|
-
const opts = { label: saved.label, pinned: saved.pinned, cwd: live?.cwd || null, userRenamed: saved.userRenamed, claudeSessionId: saved.claudeSessionId };
|
|
6752
|
+
const opts = { label: saved.label, pinned: saved.pinned, cwd: live?.cwd || null, userRenamed: saved.userRenamed, claudeSessionId: saved.claudeSessionId, floatColor: saved.floatColor };
|
|
6749
6753
|
if (CLI_PROFILES.has(saved.profile)) {
|
|
6750
6754
|
await reconnectSession(saved.id, saved.profile, opts);
|
|
6751
6755
|
} else {
|
|
@@ -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']] },
|