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
|
@@ -1,8 +1,15 @@
|
|
|
1
1
|
import { mountMemoryMaintenance } from './ui-memory-maintenance.js';
|
|
2
2
|
import { buildJudgmentsTab, wireJudgmentsTab } from './ui-judgments.js';
|
|
3
3
|
import { buildWhatsAppTab, wireWhatsAppTab } from './ui-whatsapp.js';
|
|
4
|
+
import { buildCodeEditorTab, wireCodeEditorTab } from './settings/settings-code-editor-pane.js';
|
|
4
5
|
import { SETTINGS_GROUPS, SETTINGS_PAGES, DEFAULT_SETTINGS_PAGE, pageKeys, getSettingsPage, resolveSettingsTarget, homeOfControl } from './settings/settings-ia.js';
|
|
5
6
|
import * as kit from './settings/settings-kit.js';
|
|
7
|
+
import { RULES_HOST_LABELS, describeRulesHost, describeRulesInstall, describeRulesInstallAll } from './settings/settings-rules.js';
|
|
8
|
+
import { readUserContextResponse, describeUserContext, describeUserContextSaveFailure } from './settings/settings-user-context.js';
|
|
9
|
+
import { readGreetingStatus, describeGreeting, describeGreetingSaveFailure } from './settings/settings-greeting.js';
|
|
10
|
+
import { renderGreeting, clearGreeting } from './greeting/greeting-card.js';
|
|
11
|
+
import { createTunnelCard, drawInto, mcpKeyNeededText, readTunnel } from './ui-tunnel.js';
|
|
12
|
+
import { remoteMcpAddress } from './tunnel-view.js';
|
|
6
13
|
import { initI18n, isReady as i18nReady, SUPPORTED_LOCALES, LOCALE_NAMES, SYSTEM_LOCALE, getLocale, getLocaleChoice, setLocaleChoice, getSystemLocale, englishTranslator, loadEnglishTranslator } from './i18n.js';
|
|
7
14
|
import { wireSettingsShell, wireSettingsSearch, collectSearchRows, revealSettingsTarget, followSettingsTarget, onSettingsTakeover, applyStoredSections, storeSectionState } from './settings/settings-shell.js';
|
|
8
15
|
import { buildSettingsSearchIndex } from './settings/settings-search.js';
|
|
@@ -11,7 +18,7 @@ import { buildSettingsSearchIndex } from './settings/settings-search.js';
|
|
|
11
18
|
// ═══════════════════════════════════════════
|
|
12
19
|
//
|
|
13
20
|
// The most complex shared UI module. Builds a floating settings panel with
|
|
14
|
-
//
|
|
21
|
+
// twelve task pages in five groups (./settings/settings-ia.js). A page is a
|
|
15
22
|
// title, a one-line purpose and section cards (./settings/settings-kit.js);
|
|
16
23
|
// each builder below owns one pane of a page. Variant-registered tabs (the 2D
|
|
17
24
|
// Graphics tab) join the Appearance page. Old tab ids still open the right page.
|
|
@@ -44,7 +51,8 @@ const TAB_ICONS = {
|
|
|
44
51
|
collections: '<svg viewBox="0 0 24 24"><ellipse cx="12" cy="5" rx="9" ry="3"/><path d="M21 12c0 1.66-4.03 3-9 3s-9-1.34-9-3"/><path d="M3 5v14c0 1.66 4.03 3 9 3s9-1.34 9-3V5"/></svg>',
|
|
45
52
|
projects: '<svg viewBox="0 0 24 24"><path d="M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z"/></svg>',
|
|
46
53
|
memory: '<svg viewBox="0 0 24 24"><path d="M12 2a7 7 0 0 0-7 7c0 2.38 1.19 4.47 3 5.74V17a1 1 0 0 0 1 1h6a1 1 0 0 0 1-1v-2.26c1.81-1.27 3-3.36 3-5.74a7 7 0 0 0-7-7z"/><line x1="9" y1="21" x2="15" y2="21"/><line x1="10" y1="24" x2="14" y2="24"/></svg>',
|
|
47
|
-
|
|
54
|
+
// TypeSafe's official mark (traced from their 400px favicon, filled: the nav CSS strokes every other icon)
|
|
55
|
+
judgments: '<svg viewBox="0 0 24 24"><path style="fill:currentColor;stroke:none" fill-rule="evenodd" d="M11.88 0 3.74 5.35 3.71 14.28 7.47 16.85 7.55 21.12 12.04 24 20.26 18.62 20.29 9.53 16.53 6.96 16.45 2.86 12.2 0.05ZM12.93 2.36 12.8 2.45 12.8 4.55 14.94 5.89 14.95 3.74ZM10.99 2.44 5.24 6.23 5.24 12.35 5.36 12.47 7.47 11.06 7.54 7.02 11.2 4.55 11.2 2.53ZM11.96 5.98 9.75 7.44 11.96 8.93 14.25 7.44ZM9.14 8.96 9.02 9.05 9.02 11.06 11.16 12.45 11.2 10.34ZM14.86 8.96 12.83 10.34 12.76 14.38 9.05 16.85 9.02 19.03 9.14 19.17 14.98 15.33 14.98 9.05ZM16.63 8.96 16.51 9.05 16.51 15.24 16.71 15.53 18.64 16.76 18.76 16.61 18.76 10.42ZM8.18 12.5 5.99 13.96 8.26 15.45 10.5 13.96ZM15.58 16.84 9.85 20.55 9.79 20.74 12.04 22.18 17.91 18.37 17.99 18.2 17.35 17.75 15.98 16.87Z"/></svg>',
|
|
48
56
|
interface: '<svg viewBox="0 0 24 24"><rect x="3" y="3" width="18" height="18" rx="2"/><line x1="9" y1="3" x2="9" y2="21"/><line x1="9" y1="9" x2="21" y2="9"/></svg>',
|
|
49
57
|
graphics: '<svg viewBox="0 0 24 24"><polygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2"/></svg>',
|
|
50
58
|
icons: '<svg viewBox="0 0 24 24"><rect x="3" y="3" width="7" height="7" rx="1"/><rect x="14" y="3" width="7" height="7" rx="1"/><rect x="3" y="14" width="7" height="7" rx="1"/><rect x="14" y="14" width="7" height="7" rx="1"/></svg>',
|
|
@@ -60,6 +68,7 @@ const TAB_ICONS = {
|
|
|
60
68
|
discord: '<svg viewBox="0 0 24 24"><path d="M20.317 4.37a19.791 19.791 0 0 0-4.885-1.515.074.074 0 0 0-.079.037c-.21.375-.444.864-.608 1.25a18.27 18.27 0 0 0-5.487 0 12.64 12.64 0 0 0-.617-1.25.077.077 0 0 0-.079-.037A19.736 19.736 0 0 0 3.677 4.37a.07.07 0 0 0-.032.027C.533 9.046-.32 13.58.099 18.057a.082.082 0 0 0 .031.057 19.9 19.9 0 0 0 5.993 3.03.078.078 0 0 0 .084-.028c.462-.63.874-1.295 1.226-1.994a.076.076 0 0 0-.041-.106 13.107 13.107 0 0 1-1.872-.892.077.077 0 0 1-.008-.128 10.2 10.2 0 0 0 .372-.292.074.074 0 0 1 .077-.01c3.928 1.793 8.18 1.793 12.062 0a.074.074 0 0 1 .078.01c.12.098.246.198.373.292a.077.077 0 0 1-.006.127 12.299 12.299 0 0 1-1.873.892.077.077 0 0 0-.041.107c.36.698.772 1.362 1.225 1.993a.076.076 0 0 0 .084.028 19.839 19.839 0 0 0 6.002-3.03.077.077 0 0 0 .032-.054c.5-5.177-.838-9.674-3.549-13.66a.061.061 0 0 0-.031-.03zM8.02 15.33c-1.183 0-2.157-1.085-2.157-2.419 0-1.333.956-2.419 2.157-2.419 1.21 0 2.176 1.095 2.157 2.42 0 1.333-.956 2.418-2.157 2.418zm7.975 0c-1.183 0-2.157-1.085-2.157-2.419 0-1.333.955-2.419 2.157-2.419 1.21 0 2.176 1.095 2.157 2.42 0 1.333-.946 2.418-2.157 2.418z"/></svg>',
|
|
61
69
|
whatsapp: '<svg viewBox="0 0 24 24"><path d="M3.5 20.5l1.3-3.9A8.5 8.5 0 1 1 8 19.6z"/><path d="M9.5 8.5c.4 2.6 2.9 5.2 5.5 5.6l1-1.2 1.6.8c-.3 1-1.1 1.6-2.1 1.5-2.9-.3-5.8-3.2-6.1-6.1-.1-1 .5-1.8 1.5-2.1l.8 1.6z"/></svg>',
|
|
62
70
|
morelogin: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round"><path d="M12 11.5a1 1 0 0 0-1 1c0 1.8-.3 3.6-1.2 5.2"/><path d="M7.8 14.5a8 8 0 0 0-.6 4.3"/><path d="M12 7.5a4.5 4.5 0 0 1 4.5 4.5c0 .6 0 1.2-.1 1.8"/><path d="M5 9.2a7 7 0 0 1 11-1.9"/><path d="M15 13a3 3 0 0 1-3 3"/></svg>',
|
|
71
|
+
editor: '<svg viewBox="0 0 24 24"><polyline points="16 18 22 12 16 6"/><polyline points="8 6 2 12 8 18"/><line x1="13.5" y1="5" x2="10.5" y2="19"/></svg>',
|
|
63
72
|
};
|
|
64
73
|
|
|
65
74
|
// ── Shared icon constants ──
|
|
@@ -1719,7 +1728,163 @@ function buildPermissionsTab(toolCategories, toolPermissions) {
|
|
|
1719
1728
|
</div>`;
|
|
1720
1729
|
}
|
|
1721
1730
|
|
|
1722
|
-
|
|
1731
|
+
// ── Automation behavior: the Assistant's user context ──
|
|
1732
|
+
// settings/settings-user-context.js reads the server's answer and decides what is true; these draw it. The
|
|
1733
|
+
// same markup is built with the page and redrawn after a save, so a state never looks different the second time.
|
|
1734
|
+
|
|
1735
|
+
/** What GET /api/assistant/user-context answered, as a reading (never throws: no answer is a reading too). */
|
|
1736
|
+
// ── The greeting (Settings → Automations → Greeting) ──
|
|
1737
|
+
// settings/settings-greeting.js reads the server's answers and decides what is true; these ask and draw.
|
|
1738
|
+
async function loadGreetingReading() {
|
|
1739
|
+
try {
|
|
1740
|
+
const res = await fetch('/api/greeting');
|
|
1741
|
+
const body = await res.json().catch(() => null);
|
|
1742
|
+
return readGreetingStatus({ status: res.status, body });
|
|
1743
|
+
} catch (err) {
|
|
1744
|
+
return readGreetingStatus({ status: 0, error: err?.message });
|
|
1745
|
+
}
|
|
1746
|
+
}
|
|
1747
|
+
|
|
1748
|
+
/** What a new session would open with right now. `write`: ask the writer for the sentence now (one small call). */
|
|
1749
|
+
async function fetchGreetingPreview({ write = false } = {}) {
|
|
1750
|
+
try {
|
|
1751
|
+
const res = await fetch('/api/greeting/preview', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(write ? { write: true } : {}) });
|
|
1752
|
+
const body = await res.json().catch(() => null);
|
|
1753
|
+
if (res.ok && body?.ok === true && body.preview && typeof body.preview === 'object') return body.preview;
|
|
1754
|
+
return { failed: String(body?.reason || body?.error || `HTTP ${res.status}`) };
|
|
1755
|
+
} catch (err) {
|
|
1756
|
+
return { failed: err?.message || 'no answer' };
|
|
1757
|
+
}
|
|
1758
|
+
}
|
|
1759
|
+
|
|
1760
|
+
/** The section after an answer: the note, the switch, what stands in its way, and the preview (the card every panel shows). */
|
|
1761
|
+
function drawGreetingSection(overlay, view) {
|
|
1762
|
+
const q = (selector) => overlay.querySelector(selector);
|
|
1763
|
+
const note = q('#gr-status-note');
|
|
1764
|
+
if (note) { note.hidden = !view.note; note.textContent = view.note ? view.note.text : ''; note.dataset.grState = view.state; }
|
|
1765
|
+
const row = q('[data-gr-row]');
|
|
1766
|
+
if (row) { row.classList.toggle('enabled', view.on); row.classList.toggle('uc-disabled', view.disabled); }
|
|
1767
|
+
const toggle = q('[data-gr-toggle]');
|
|
1768
|
+
if (toggle) { toggle.classList.toggle('on', view.on); toggle.setAttribute('aria-pressed', view.on ? 'true' : 'false'); toggle.disabled = view.disabled; }
|
|
1769
|
+
const notes = q('#gr-notes');
|
|
1770
|
+
if (notes) notes.innerHTML = userContextNotesHTML(view.notes);
|
|
1771
|
+
const box = q('#gr-preview');
|
|
1772
|
+
if (!box) return;
|
|
1773
|
+
const preview = view.preview;
|
|
1774
|
+
box.hidden = !preview.shown;
|
|
1775
|
+
box.dataset.grPreviewState = preview.state;
|
|
1776
|
+
const project = q('#gr-preview-project');
|
|
1777
|
+
if (project) project.textContent = preview.project;
|
|
1778
|
+
const lead = q('#gr-preview-lead');
|
|
1779
|
+
if (lead) { lead.textContent = preview.lead || preview.empty; lead.hidden = !(preview.lead || preview.empty); }
|
|
1780
|
+
const card = q('#gr-preview-card');
|
|
1781
|
+
if (card) {
|
|
1782
|
+
if (preview.greeting) renderGreeting(card, preview.greeting, { t: kit.tx, onOpenMemory: (id) => { if (id) emit('memory:open', { id }); } });
|
|
1783
|
+
else clearGreeting(card);
|
|
1784
|
+
}
|
|
1785
|
+
const spend = q('#gr-preview-spend');
|
|
1786
|
+
if (spend) { spend.textContent = preview.spend; spend.hidden = !preview.spend; }
|
|
1787
|
+
const write = q('#gr-write');
|
|
1788
|
+
if (write) write.hidden = !preview.canWrite;
|
|
1789
|
+
}
|
|
1790
|
+
|
|
1791
|
+
async function loadUserContextReading() {
|
|
1792
|
+
try {
|
|
1793
|
+
const res = await fetch('/api/assistant/user-context');
|
|
1794
|
+
const body = await res.json().catch(() => null);
|
|
1795
|
+
return readUserContextResponse({ status: res.status, body });
|
|
1796
|
+
} catch (err) {
|
|
1797
|
+
return readUserContextResponse({ status: 0, error: err?.message });
|
|
1798
|
+
}
|
|
1799
|
+
}
|
|
1800
|
+
|
|
1801
|
+
/** What stands in the way of a layer: one line each, with the Settings page that fixes it when there is one. */
|
|
1802
|
+
const userContextNotesHTML = (notes = []) => notes.map(note => `<div class="uc-layer-note" data-tone="${note.tone === 'warn' ? 'warn' : 'info'}">${kit.esc(note.text)}${
|
|
1803
|
+
note.link ? ` <button type="button" class="cc-copy-btn stg-text-btn uc-note-link" data-uc-goto="${kit.esc(JSON.stringify(note.link.target))}">${kit.esc(note.link.label)}</button>` : ''
|
|
1804
|
+
}</div>`).join('');
|
|
1805
|
+
|
|
1806
|
+
/** The digest's lines, or the one sentence that stands in for them (nothing known yet, the layer is off). */
|
|
1807
|
+
const userContextDigestHTML = (digest) => (digest.rows.length
|
|
1808
|
+
? digest.rows.map(row => `<button type="button" class="uc-digest-row" role="listitem" data-uc-memory="${kit.esc(row.id)}" title="${kit.te('settings.redesign.hooks.uc.openMemory')}"><span class="uc-digest-statement">${kit.esc(row.statement)}</span><span class="uc-digest-facet">${kit.esc(row.facet)}${row.seed ? ` · ${kit.te('settings.redesign.hooks.uc.seed')}` : ''}</span></button>`).join('')
|
|
1809
|
+
: `<div class="stg-help uc-digest-message" role="listitem" style="margin:0">${kit.esc(digest.message)}</div>`);
|
|
1810
|
+
|
|
1811
|
+
/** Put a view (describeUserContext) on the section: the note, each switch and its notes, the digest. */
|
|
1812
|
+
function drawUserContext(overlay, view) {
|
|
1813
|
+
const section = overlay.querySelector('#stg-sec-automation-behavior');
|
|
1814
|
+
if (!section) return;
|
|
1815
|
+
const note = section.querySelector('#uc-status-note');
|
|
1816
|
+
if (note) { note.hidden = !view.note; note.textContent = view.note?.text || ''; note.dataset.ucState = view.state; }
|
|
1817
|
+
const layers = section.querySelector('#uc-layers');
|
|
1818
|
+
if (layers) { layers.dataset.ucReady = view.ready ? '1' : '0'; layers.dataset.ucState = view.state; }
|
|
1819
|
+
for (const [key, row] of Object.entries(view.layers)) {
|
|
1820
|
+
const toggle = section.querySelector(`.cc-toggle[data-uc-layer="${key}"]`);
|
|
1821
|
+
if (!toggle) continue;
|
|
1822
|
+
toggle.classList.toggle('on', row.on);
|
|
1823
|
+
toggle.setAttribute('aria-pressed', row.on ? 'true' : 'false');
|
|
1824
|
+
toggle.disabled = row.disabled;
|
|
1825
|
+
toggle.setAttribute('aria-describedby', `uc-layer-${key}-help uc-layer-${key}-notes${view.note ? ' uc-status-note' : ''}`);
|
|
1826
|
+
const item = toggle.closest('.cc-integration-item');
|
|
1827
|
+
item?.classList.toggle('enabled', row.on);
|
|
1828
|
+
item?.classList.toggle('uc-disabled', row.disabled);
|
|
1829
|
+
const notes = section.querySelector(`#uc-layer-${key}-notes`);
|
|
1830
|
+
if (notes) notes.innerHTML = userContextNotesHTML(row.notes);
|
|
1831
|
+
}
|
|
1832
|
+
const applies = section.querySelector('#uc-applies');
|
|
1833
|
+
if (applies) { applies.hidden = !view.applies; applies.textContent = view.applies; }
|
|
1834
|
+
const digest = section.querySelector('#uc-digest');
|
|
1835
|
+
if (digest) {
|
|
1836
|
+
digest.hidden = !view.digest.shown;
|
|
1837
|
+
digest.dataset.ucDigestState = view.digest.state;
|
|
1838
|
+
const count = digest.querySelector('#uc-digest-count');
|
|
1839
|
+
if (count) count.textContent = view.digest.count;
|
|
1840
|
+
const rows = digest.querySelector('#uc-digest-rows');
|
|
1841
|
+
if (rows) rows.innerHTML = view.digest.shown ? userContextDigestHTML(view.digest) : '';
|
|
1842
|
+
const more = digest.querySelector('#uc-digest-more');
|
|
1843
|
+
if (more) { more.hidden = !view.digest.omitted; more.textContent = view.digest.omitted; }
|
|
1844
|
+
}
|
|
1845
|
+
}
|
|
1846
|
+
|
|
1847
|
+
/**
|
|
1848
|
+
* Does the server that answered refuse MCP from outside while no access key is set? One that brings its own
|
|
1849
|
+
* tunnel program does (lib/request-gate.js mcpWithoutKey); one started before that change, still running until
|
|
1850
|
+
* it is restarted, leaves the endpoint open to whoever has the address. Its tunnel status tells the two apart,
|
|
1851
|
+
* and the words about a missing key say what is true for the server that is running.
|
|
1852
|
+
*/
|
|
1853
|
+
function closesKeylessMcp(tunnelStatus) {
|
|
1854
|
+
return !!tunnelStatus?.cloudflared;
|
|
1855
|
+
}
|
|
1856
|
+
|
|
1857
|
+
/**
|
|
1858
|
+
* Settings → Automations → Online access, the tunnel, for a reading of tunnel-view.js (the same reading the Share
|
|
1859
|
+
* menu draws). Two pieces, each drawn again by itself when the reading changes: the row (the state as a word
|
|
1860
|
+
* with its dot, the address while one is up, the switch), and what belongs under it (a download's progress; the
|
|
1861
|
+
* sentence about the state; after a failure its reason and a way to try again; for a running tunnel the remote
|
|
1862
|
+
* MCP address to copy, or what is missing for one, an access key, and which cloudflared runs).
|
|
1863
|
+
*/
|
|
1864
|
+
function tunnelRowHtml(view) {
|
|
1865
|
+
const tone = { ok: 'ok', busy: 'busy', err: 'err', warn: 'warn' }[view.tone] || '';
|
|
1866
|
+
return `
|
|
1867
|
+
<div class="cc-integration-info">
|
|
1868
|
+
<div class="cc-integration-label" id="cc-tunnel-label"><span class="stg-pill"${tone ? ` data-state="${tone}"` : ''} role="status">${escapeHtml(view.label)}${view.progress ? ` · ${escapeHtml(view.progress.text)}` : ''}</span></div>
|
|
1869
|
+
${view.kind === 'running' ? `<div class="cc-integration-path" id="cc-tunnel-url">${escapeHtml(view.url)}</div>` : ''}
|
|
1870
|
+
</div>
|
|
1871
|
+
${view.action ? `<button type="button" class="cc-toggle${view.on ? ' on' : ''}" id="cc-tunnel-toggle" role="switch" aria-checked="${view.on ? 'true' : 'false'}" ${kit.inv('AUT049')} aria-label="${kit.L('AUT049')}" title="${escapeHtml(view.actionLabel)}"></button>` : ''}`;
|
|
1872
|
+
}
|
|
1873
|
+
|
|
1874
|
+
function tunnelExtraHtml(view, mcpKeyInfo) {
|
|
1875
|
+
const running = view.kind === 'running';
|
|
1876
|
+
const address = running ? remoteMcpAddress(view.url, mcpKeyInfo?.key) : null;
|
|
1877
|
+
return [
|
|
1878
|
+
view.progress ? `<div class="stg-tunnel-progress" role="progressbar" aria-label="${escapeHtml(view.label)}" aria-valuemin="0" aria-valuemax="100" aria-valuenow="${view.progress.percent}"><span style="width:${view.progress.percent}%"></span></div>` : '',
|
|
1879
|
+
!running && view.detail ? `<div class="stg-help" id="cc-tunnel-detail"${view.kind === 'failed' ? ' role="alert"' : ''}>${escapeHtml(view.detail)}</div>` : '',
|
|
1880
|
+
view.kind === 'failed' ? `<button type="button" class="cc-copy-btn" id="cc-tunnel-retry">${escapeHtml(view.actionLabel)}</button>` : '',
|
|
1881
|
+
address ? `<button type="button" class="cc-copy-btn" id="cc-tunnel-copy-url" ${kit.inv('AUT050')} title="${kit.H('AUT050')}">${COPY_ICON} ${kit.te('settings.redesign.hooks.copyMcpUrl')}</button>` : '',
|
|
1882
|
+
running && !address ? `<div class="stg-help" id="cc-tunnel-key-needed">${escapeHtml(mcpKeyNeededText())}</div>` : '',
|
|
1883
|
+
running && view.copy ? `<div class="stg-help" id="cc-tunnel-copy">${escapeHtml(view.copy)}${view.copyNote ? ` ${escapeHtml(view.copyNote)}` : ''}</div>` : '',
|
|
1884
|
+
].filter(Boolean).join('');
|
|
1885
|
+
}
|
|
1886
|
+
|
|
1887
|
+
function buildConnectionsTab(ccIntegrations, ccSkills, tunnelStatus, mcpKeyInfo, openclawBridge, greeting, toolPermissions, toolCategories, userContext = null) {
|
|
1723
1888
|
const gh = ccIntegrations.global.hooks || {};
|
|
1724
1889
|
const ssOn = !!gh.SessionStart;
|
|
1725
1890
|
const psOn = !!gh.UserPromptSubmit;
|
|
@@ -1731,7 +1896,6 @@ function buildConnectionsTab(ccIntegrations, ccSkills, tunnelStatus, mcpKeyInfo,
|
|
|
1731
1896
|
const subStopOn = !!gh.SubagentStop;
|
|
1732
1897
|
const hf = ccIntegrations.hookFeatures || {};
|
|
1733
1898
|
const cmOn = hf.conversationMemory !== false;
|
|
1734
|
-
const grOn = hf.greeting === true;
|
|
1735
1899
|
const ulOn = hf.userLearning !== false;
|
|
1736
1900
|
const ulThreshold = hf.userLearningThreshold || 8;
|
|
1737
1901
|
const ulMaxNudges = hf.userLearningMaxNudges || 3;
|
|
@@ -1764,7 +1928,7 @@ function buildConnectionsTab(ccIntegrations, ccSkills, tunnelStatus, mcpKeyInfo,
|
|
|
1764
1928
|
{ label: 'Gemini Web', on: false },
|
|
1765
1929
|
];
|
|
1766
1930
|
const openaiItems = [
|
|
1767
|
-
{ label: 'Codex CLI', on:
|
|
1931
|
+
{ label: 'Codex CLI', on: compat.codex !== false },
|
|
1768
1932
|
{ label: 'Cursor', on: false },
|
|
1769
1933
|
{ label: 'ChatGPT Web', on: false },
|
|
1770
1934
|
];
|
|
@@ -1795,91 +1959,35 @@ function buildConnectionsTab(ccIntegrations, ccSkills, tunnelStatus, mcpKeyInfo,
|
|
|
1795
1959
|
</div>`;
|
|
1796
1960
|
};
|
|
1797
1961
|
|
|
1798
|
-
//
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
-
|
|
1805
|
-
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
<span class="cc-greeting-drag-handle" title="${h('dragToReorder')}">⋮⋮</span>
|
|
1812
|
-
<input class="cc-greeting-reminder-input label" placeholder="${h('label')}" aria-label="${h('label')}" value="${escapeHtml(reminder.label || '')}">
|
|
1813
|
-
<input class="cc-greeting-reminder-input cmd" placeholder="${h('command')}" aria-label="${h('command')}" value="${escapeHtml(reminder.command || '')}">
|
|
1814
|
-
<button type="button" class="cc-greeting-reminder-remove" title="${h('remove')}" aria-label="${h('remove')}">×</button>
|
|
1815
|
-
</div>`;
|
|
1816
|
-
const remindersHTML = (firstCfg.reminders || []).map(buildReminderRow).join('');
|
|
1817
|
-
const variable = (name, id) => `<tr><td><code>{${name}}</code></td><td id="${prefix}-cs-${id}"></td></tr>`;
|
|
1818
|
-
|
|
1819
|
-
return kit.card({
|
|
1820
|
-
id: `${prefix}-greeting-config`, level: 'h4', collapsible: true, collapsed: true,
|
|
1821
|
-
title: h(`greeting.title.${kind}`), purpose: h(`greeting.purpose.${kind}`),
|
|
1822
|
-
status: `<button type="button" class="cc-toggle${enabled ? ' on' : ''}" ${toggleAttrs} ${kit.inv(idToggle)} aria-label="${kit.L(idToggle)}" title="${kit.H(idToggle)}"></button>`,
|
|
1823
|
-
body: `
|
|
1824
|
-
<div id="${bodyId}" style="display:${enabled ? 'block' : 'none'}">
|
|
1825
|
-
<div class="stg-field" ${kit.inv(idProject)}>
|
|
1826
|
-
<label id="${prefix}-greeting-project-caption">${kit.L(idProject)}</label>
|
|
1827
|
-
<div class="cc-dropdown" id="${prefix}-greeting-project-dropdown" role="group" aria-labelledby="${prefix}-greeting-project-caption">
|
|
1828
|
-
<button class="cc-dropdown-trigger" type="button" aria-haspopup="listbox" aria-describedby="${prefix}-greeting-project-help">
|
|
1829
|
-
<span class="cc-dropdown-value" id="${prefix}-greeting-project-label">${projectLabel(firstKey)}</span>
|
|
1830
|
-
<svg class="cc-dropdown-arrow" viewBox="0 0 24 24" aria-hidden="true"><polyline points="6 9 12 15 18 9"/></svg>
|
|
1831
|
-
</button>
|
|
1832
|
-
<div class="cc-dropdown-menu" id="${prefix}-greeting-project-menu">
|
|
1833
|
-
${projectKeys.map((key) => `<div class="cc-dropdown-item${key === firstKey ? ' active' : ''}" data-value="${key}">${projectLabel(key)}</div>`).join('')}
|
|
1834
|
-
</div>
|
|
1835
|
-
<input type="hidden" id="${prefix}-greeting-project" value="${firstKey}">
|
|
1836
|
-
</div>
|
|
1837
|
-
<div class="stg-help" id="${prefix}-greeting-project-help">${kit.H(idProject)}</div>
|
|
1838
|
-
</div>
|
|
1839
|
-
<div class="cc-greeting-field stg-field" ${kit.inv(idTemplate)}>
|
|
1840
|
-
<label class="cc-greeting-label" for="${prefix}-greeting-template">${kit.L(idTemplate)}</label>
|
|
1841
|
-
<textarea class="cc-greeting-textarea" id="${prefix}-greeting-template" placeholder="${h('greeting.templatePlaceholder')}" aria-describedby="${prefix}-greeting-template-help">${escapeHtml(firstCfg.greetingTemplate || '')}</textarea>
|
|
1842
|
-
<div class="stg-help" id="${prefix}-greeting-template-help">${kit.H(idTemplate)}</div>
|
|
1843
|
-
<div class="cc-greeting-cheatsheet-toggle" id="${prefix}-greeting-cheatsheet-toggle" ${kit.inv('AUT047')} role="button" tabindex="0">
|
|
1844
|
-
<svg viewBox="0 0 24 24" aria-hidden="true" style="width:11px;height:11px;fill:none;stroke:currentColor;stroke-width:2;vertical-align:-1px"><circle cx="12" cy="12" r="10"/><line x1="12" y1="16" x2="12" y2="12"/><line x1="12" y1="8" x2="12.01" y2="8"/></svg>
|
|
1845
|
-
${kit.L('AUT047')}
|
|
1846
|
-
</div>
|
|
1847
|
-
<div class="cc-greeting-cheatsheet" id="${prefix}-greeting-cheatsheet" style="display:none">
|
|
1848
|
-
<table class="cc-cheatsheet-table">
|
|
1849
|
-
<thead><tr><th>${h('greeting.placeholder')}</th><th>${h('greeting.showsAs')}</th></tr></thead>
|
|
1850
|
-
<tbody>
|
|
1851
|
-
${variable('time_greeting', 'time')}
|
|
1852
|
-
${variable('project_label', 'label')}
|
|
1853
|
-
${variable('project_name', 'name')}
|
|
1854
|
-
${variable('branch', 'branch')}
|
|
1855
|
-
${variable('date', 'date')}
|
|
1856
|
-
</tbody>
|
|
1857
|
-
</table>
|
|
1858
|
-
<div class="cc-cheatsheet-example">
|
|
1859
|
-
<div class="cc-greeting-label" style="margin-bottom:3px">${h('greeting.example')}</div>
|
|
1860
|
-
<div class="cc-cheatsheet-preview" id="${prefix}-cs-preview"></div>
|
|
1962
|
+
// The greeting (lib/greeting/): one switch for every agent, and what a new session would open with right now.
|
|
1963
|
+
// `greeting` is the view of what the server answered (describeGreeting). Without the engine (a server started
|
|
1964
|
+
// before it existed, one where it did not start, no answer) the switch shows off and disabled under a line
|
|
1965
|
+
// that says which of those it is. The preview is filled in once Settings is open (drawGreetingSection).
|
|
1966
|
+
const gr = greeting || describeGreeting(null, null, { tx: kit.tx });
|
|
1967
|
+
const greetingBlock = `
|
|
1968
|
+
<div class="stg-help uc-status-note" id="gr-status-note" role="status" data-gr-state="${gr.state}" data-stg-data${gr.note ? '' : ' hidden'}>${gr.note ? kit.esc(gr.note.text) : ''}</div>
|
|
1969
|
+
<div class="cc-hook-toggles" id="gr-switch" data-gr-ready="${gr.ready ? '1' : '0'}">
|
|
1970
|
+
<div class="cc-integration-item${gr.on ? ' enabled' : ''}${gr.disabled ? ' uc-disabled' : ''}" data-gr-row>
|
|
1971
|
+
<div class="cc-integration-info">
|
|
1972
|
+
<div class="cc-integration-label" id="gr-enabled-label">${kit.L('AUT058')}</div>
|
|
1973
|
+
<div class="cc-integration-path" id="gr-enabled-help">${kit.H('AUT058')}</div>
|
|
1974
|
+
<div class="uc-layer-notes" id="gr-notes" data-stg-data>${userContextNotesHTML(gr.notes)}</div>
|
|
1861
1975
|
</div>
|
|
1976
|
+
<button type="button" class="cc-toggle${gr.on ? ' on' : ''}" data-gr-toggle ${kit.inv('AUT058')} aria-pressed="${gr.on ? 'true' : 'false'}" aria-labelledby="gr-enabled-label" aria-describedby="gr-enabled-help gr-notes gr-status-note"${gr.disabled ? ' disabled' : ''}></button>
|
|
1862
1977
|
</div>
|
|
1978
|
+
</div>
|
|
1979
|
+
<div class="stg-help uc-save-status" id="gr-save-status" role="alert" data-stg-data hidden></div>
|
|
1980
|
+
<div class="uc-digest gr-preview" id="gr-preview" data-gr-preview-state="${gr.preview.state}" ${kit.inv('AUT059')}${gr.preview.shown ? '' : ' hidden'}>
|
|
1981
|
+
<div class="uc-digest-head">
|
|
1982
|
+
<span class="uc-digest-title" id="gr-preview-title">${kit.L('AUT059')}</span>
|
|
1983
|
+
<span class="uc-digest-count" id="gr-preview-project" data-stg-data>${kit.esc(gr.preview.project)}</span>
|
|
1863
1984
|
</div>
|
|
1864
|
-
<div class="
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
</div>
|
|
1868
|
-
<
|
|
1869
|
-
|
|
1870
|
-
<div class="cc-greeting-reminder-list" id="${prefix}-greeting-reminders" role="group" aria-labelledby="${prefix}-greeting-reminders-label">${remindersHTML}</div>
|
|
1871
|
-
<div class="stg-help">${h('greeting.remindersHelp')}</div>
|
|
1872
|
-
<button type="button" class="conn-add-btn" id="${prefix}-greeting-add-reminder" ${kit.inv(idAdd)} title="${kit.H(idAdd)}" style="margin-top:6px">
|
|
1873
|
-
<svg viewBox="0 0 24 24" aria-hidden="true"><line x1="12" y1="5" x2="12" y2="19"/><line x1="5" y1="12" x2="19" y2="12"/></svg>
|
|
1874
|
-
${kit.L(idAdd)}
|
|
1875
|
-
</button>
|
|
1876
|
-
</div>
|
|
1877
|
-
<button type="button" class="conn-add-btn stg-save-btn" id="${prefix}-greeting-save" ${kit.inv(idSave)} title="${kit.H(idSave)}" style="width:100%;margin-top:4px;border-style:solid;background:rgba(79,195,247,0.08);border-color:rgba(79,195,247,0.25);color:rgba(79,195,247,0.9)">
|
|
1878
|
-
${kit.L(idSave)}
|
|
1879
|
-
</button>
|
|
1880
|
-
</div>`,
|
|
1881
|
-
});
|
|
1882
|
-
}
|
|
1985
|
+
<div class="stg-help gr-preview-help" style="margin:2px 0 0">${kit.H('AUT059')}</div>
|
|
1986
|
+
<div class="stg-help gr-preview-lead" id="gr-preview-lead" role="status" data-stg-data>${kit.esc(gr.preview.lead || gr.preview.empty)}</div>
|
|
1987
|
+
<div class="gr-preview-card" id="gr-preview-card" data-stg-data></div>
|
|
1988
|
+
<div class="stg-help gr-preview-spend" id="gr-preview-spend" data-stg-data${gr.preview.spend ? '' : ' hidden'}>${kit.esc(gr.preview.spend)}</div>
|
|
1989
|
+
<button type="button" class="conn-add-btn" id="gr-write" ${kit.inv('AUT060')} title="${kit.H('AUT060')}" style="margin:0;width:auto;padding:4px 10px;border-style:solid"${gr.preview.canWrite ? '' : ' hidden'}>${kit.L('AUT060')}</button>
|
|
1990
|
+
</div>`;
|
|
1883
1991
|
|
|
1884
1992
|
const feature = (invId, key, on, extra = '') => `
|
|
1885
1993
|
<div class="cc-integration-item${on ? ' enabled' : ''}" data-feature="${key}">
|
|
@@ -1890,26 +1998,65 @@ function buildConnectionsTab(ccIntegrations, ccSkills, tunnelStatus, mcpKeyInfo,
|
|
|
1890
1998
|
<button type="button" class="cc-toggle${on ? ' on' : ''}" data-cc-feature="${key}" ${kit.inv(invId)} aria-labelledby="cc-feature-${key}-label" aria-describedby="cc-feature-${key}-help"></button>
|
|
1891
1999
|
</div>`;
|
|
1892
2000
|
|
|
2001
|
+
// The Assistant's user context: three layers and the digest a new conversation starts with.
|
|
2002
|
+
// `userContext` is the view of what the server answered (describeUserContext). Without the engine (a server
|
|
2003
|
+
// started before it existed, one where it did not start, no answer) the rows show switched off and disabled
|
|
2004
|
+
// under a line that says which of those it is: public/ is served live, lib/ only after a restart.
|
|
2005
|
+
const uc = userContext || describeUserContext(null, { tx: kit.tx });
|
|
2006
|
+
const layer = (invId, key) => {
|
|
2007
|
+
const row = uc.layers[key];
|
|
2008
|
+
return `
|
|
2009
|
+
<div class="cc-integration-item${row.on ? ' enabled' : ''}${row.disabled ? ' uc-disabled' : ''}" data-uc-layer-row="${key}">
|
|
2010
|
+
<div class="cc-integration-info">
|
|
2011
|
+
<div class="cc-integration-label" id="uc-layer-${key}-label">${kit.L(invId)}</div>
|
|
2012
|
+
<div class="cc-integration-path" id="uc-layer-${key}-help">${kit.H(invId)}</div>
|
|
2013
|
+
<div class="uc-layer-notes" id="uc-layer-${key}-notes" data-stg-data>${userContextNotesHTML(row.notes)}</div>
|
|
2014
|
+
</div>
|
|
2015
|
+
<button type="button" class="cc-toggle${row.on ? ' on' : ''}" data-uc-layer="${key}" ${kit.inv(invId)} aria-pressed="${row.on ? 'true' : 'false'}" aria-labelledby="uc-layer-${key}-label" aria-describedby="uc-layer-${key}-help uc-layer-${key}-notes${uc.note ? ' uc-status-note' : ''}"${row.disabled ? ' disabled' : ''}></button>
|
|
2016
|
+
</div>`;
|
|
2017
|
+
};
|
|
2018
|
+
const userContextBlock = `
|
|
2019
|
+
<div class="stg-help uc-status-note" id="uc-status-note" role="status" data-uc-state="${uc.state}" data-stg-data${uc.note ? '' : ' hidden'}>${uc.note ? kit.esc(uc.note.text) : ''}</div>
|
|
2020
|
+
<div class="cc-hook-toggles" id="uc-layers" data-uc-ready="${uc.ready ? '1' : '0'}" data-uc-state="${uc.state}">
|
|
2021
|
+
${layer('AUT053', 'knowledge')}
|
|
2022
|
+
${layer('AUT054', 'nudges')}
|
|
2023
|
+
${layer('AUT055', 'trace')}
|
|
2024
|
+
</div>
|
|
2025
|
+
<div class="stg-help uc-save-status" id="uc-save-status" role="alert" data-stg-data hidden></div>
|
|
2026
|
+
<div class="stg-help uc-applies" id="uc-applies"${uc.applies ? '' : ' hidden'}>${kit.esc(uc.applies)}</div>
|
|
2027
|
+
<div class="uc-digest" id="uc-digest" data-uc-digest-state="${uc.digest.state}" ${kit.inv('AUT056')}${uc.digest.shown ? '' : ' hidden'}>
|
|
2028
|
+
<div class="uc-digest-head">
|
|
2029
|
+
<span class="uc-digest-title" id="uc-digest-title">${kit.L('AUT056')}</span>
|
|
2030
|
+
<span class="uc-digest-count" id="uc-digest-count" data-stg-data>${kit.esc(uc.digest.count)}</span>
|
|
2031
|
+
</div>
|
|
2032
|
+
<div class="stg-help uc-digest-help" style="margin:2px 0 0">${kit.H('AUT056')}</div>
|
|
2033
|
+
<div class="uc-digest-rows" id="uc-digest-rows" role="list" aria-labelledby="uc-digest-title" data-stg-data>${uc.digest.shown ? userContextDigestHTML(uc.digest) : ''}</div>
|
|
2034
|
+
<div class="stg-help" id="uc-digest-more" style="margin:0 0 8px"${uc.digest.omitted ? '' : ' hidden'}>${kit.esc(uc.digest.omitted)}</div>
|
|
2035
|
+
<button type="button" class="conn-add-btn" id="uc-digest-all" ${kit.inv('AUT057')} title="${kit.H('AUT057')}" style="margin:0;width:auto;padding:4px 10px;border-style:solid">${kit.L('AUT057')}</button>
|
|
2036
|
+
</div>`;
|
|
2037
|
+
|
|
1893
2038
|
return `
|
|
1894
2039
|
<div class="stg-pane" data-stg-pane="hooks">
|
|
1895
2040
|
|
|
1896
2041
|
${kit.card({
|
|
1897
2042
|
id: 'stg-sec-greetings', section: 'automations.greetings',
|
|
1898
2043
|
body: `
|
|
1899
|
-
${
|
|
1900
|
-
ids: ['AUT001', 'AUT002', 'AUT003', 'AUT004', 'AUT005', 'AUT009', 'AUT010'] })}
|
|
1901
|
-
${buildGreetingSection({ kind: 'codex', enabled: codexGreetingConfig?.enabled === true, config: codexGreetingConfig, prefix: 'codex', bodyId: 'codex-greeting-body', toggleAttrs: 'data-codex-greeting-toggle="enabled"',
|
|
1902
|
-
ids: ['AUT011', 'AUT012', 'AUT013', 'AUT014', 'AUT015', 'AUT016', 'AUT017'] })}
|
|
1903
|
-
${buildGreetingSection({ kind: 'opencode', enabled: opencodeGreetingConfig?.enabled === true, config: opencodeGreetingConfig, prefix: 'opencode', bodyId: 'opencode-greeting-body', toggleAttrs: 'data-opencode-greeting-toggle="enabled"',
|
|
1904
|
-
ids: ['AUT018', 'AUT019', 'AUT020', 'AUT021', 'AUT022', 'AUT026', 'AUT027'] })}`,
|
|
2044
|
+
${greetingBlock}`,
|
|
1905
2045
|
})}
|
|
1906
2046
|
|
|
1907
2047
|
${kit.card({
|
|
1908
2048
|
id: 'stg-sec-automation-behavior', section: 'automations.automation_behavior',
|
|
1909
|
-
status: providerBadge({ cli: true, vscode: true, web: false, cowork: false }),
|
|
1910
2049
|
body: `
|
|
1911
|
-
|
|
1912
|
-
|
|
2050
|
+
${userContextBlock}
|
|
2051
|
+
|
|
2052
|
+
${kit.card({
|
|
2053
|
+
id: 'stg-sec-hooks', level: 'h4', collapsible: true, collapsed: true, advanced: true, attrs: 'data-cc-target="global"',
|
|
2054
|
+
title: h('hooksTitle'), purpose: h('hooksPurpose'),
|
|
2055
|
+
// These hooks are Claude Code's own (~/.claude/settings.json, hooks/claude-code): no other assistant runs them.
|
|
2056
|
+
status: `<span class="cc-hooks-badge${allOn ? ' all-on' : ''}" id="cc-hooks-badge">${onCount}/${hookRows.length}</span>${providerBadge({ cli: true, vscode: true, web: false, cowork: false, codex: false })}`,
|
|
2057
|
+
body: `
|
|
2058
|
+
<div class="stg-help" style="margin:0 0 10px">${h('claudeOnly')}</div>
|
|
2059
|
+
<div class="cc-hook-toggles">
|
|
1913
2060
|
${feature('AUT036', 'conversationMemory', cmOn)}
|
|
1914
2061
|
${feature('AUT039', 'userLearning', ulOn, `
|
|
1915
2062
|
<div class="cc-ul-threshold" id="cc-ul-threshold" style="display:${ulOn ? 'flex' : 'none'};align-items:center;gap:6px;margin-top:5px;flex-wrap:wrap">
|
|
@@ -1922,14 +2069,8 @@ function buildConnectionsTab(ccIntegrations, ccSkills, tunnelStatus, mcpKeyInfo,
|
|
|
1922
2069
|
</div>`)}
|
|
1923
2070
|
${feature('AUT040', 'taskRecallGate', taskRecallOn)}
|
|
1924
2071
|
${feature('AUT041', 'subagentRemember', srOn)}
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
${kit.card({
|
|
1928
|
-
id: 'stg-sec-hooks', level: 'h4', collapsible: true, collapsed: true, advanced: true, attrs: 'data-cc-target="global"',
|
|
1929
|
-
title: h('hooksTitle'), purpose: h('hooksPurpose'),
|
|
1930
|
-
status: `<span class="cc-hooks-badge${allOn ? ' all-on' : ''}" id="cc-hooks-badge">${onCount}/${hookRows.length}</span>${providerBadge({ cli: true, vscode: true, web: false, cowork: false })}`,
|
|
1931
|
-
body: `
|
|
1932
|
-
<div class="cc-hook-toggles">
|
|
2072
|
+
</div>
|
|
2073
|
+
<div class="cc-hook-toggles" style="margin-top:10px">
|
|
1933
2074
|
${hookRows.map(r => `
|
|
1934
2075
|
<div class="cc-integration-item${r.on ? ' enabled' : ''}" data-hook="${r.key}">
|
|
1935
2076
|
<div class="cc-integration-info">
|
|
@@ -1947,20 +2088,14 @@ function buildConnectionsTab(ccIntegrations, ccSkills, tunnelStatus, mcpKeyInfo,
|
|
|
1947
2088
|
status: providerBadge({ cli: true, vscode: true, web: true, webNote: h('compat.viaAddress'), cowork: false }),
|
|
1948
2089
|
body: `
|
|
1949
2090
|
${kit.subhead(h('tunnel.title'))}
|
|
1950
|
-
<div class="cc-integration-item${tunnelStatus.
|
|
1951
|
-
|
|
1952
|
-
<div class="cc-integration-label" id="cc-tunnel-label">${tunnelStatus.running ? h('running') : tunnelStatus.available ? h('ready') : h('tunnel.notInstalled')}</div>
|
|
1953
|
-
<div class="cc-integration-path" id="cc-tunnel-url">${tunnelStatus.url ? escapeHtml(tunnelStatus.url) : (tunnelStatus.available ? h('exposeMcpToClaudeWebVia') : h('tunnel.install'))}</div>
|
|
1954
|
-
</div>
|
|
1955
|
-
${tunnelStatus.available ? `<button type="button" class="cc-toggle${tunnelStatus.running ? ' on' : ''}" id="cc-tunnel-toggle" ${kit.inv('AUT049')} aria-label="${kit.L('AUT049')}" title="${kit.H('AUT049')}"></button>` : ''}
|
|
1956
|
-
</div>
|
|
1957
|
-
${tunnelStatus.url ? `<button type="button" class="cc-copy-btn" id="cc-tunnel-copy-url" ${kit.inv('AUT050')} title="${kit.H('AUT050')}" style="margin-top:4px">${copyIcon} ${h('copyMcpUrl')}</button>` : ''}
|
|
2091
|
+
<div class="cc-integration-item${readTunnel(tunnelStatus).on ? ' enabled' : ''}" id="cc-tunnel-row" ${kit.inv('AUT048')}>${tunnelRowHtml(readTunnel(tunnelStatus))}</div>
|
|
2092
|
+
<div class="stg-tunnel-extra" id="cc-tunnel-extra">${tunnelExtraHtml(readTunnel(tunnelStatus), mcpKeyInfo)}</div>
|
|
1958
2093
|
|
|
1959
2094
|
<div style="margin-top:10px">
|
|
1960
2095
|
<div class="cc-integration-item${mcpKeyInfo.hasKey ? ' enabled' : ''}" id="cc-apikey-row">
|
|
1961
2096
|
<div class="cc-integration-info">
|
|
1962
2097
|
<div class="cc-integration-label">${h('apikey.title')}</div>
|
|
1963
|
-
<div class="cc-integration-path" id="cc-apikey-status">${mcpKeyInfo.hasKey ? escapeHtml(mcpKeyInfo.maskedKey) : h('apikey.none')}</div>
|
|
2098
|
+
<div class="cc-integration-path" id="cc-apikey-status">${mcpKeyInfo.hasKey ? escapeHtml(mcpKeyInfo.maskedKey) : h(closesKeylessMcp(tunnelStatus) ? 'apikey.noneClosed' : 'apikey.none')}</div>
|
|
1964
2099
|
</div>
|
|
1965
2100
|
<button type="button" class="conn-add-btn" id="cc-apikey-generate" ${kit.inv('AUT042')} title="${kit.H('AUT042')}" style="margin:0;padding:3px 8px;width:auto;border-style:solid;background:rgba(255,255,255,0.03)">${mcpKeyInfo.hasKey ? h('regenerate') : h('generateKey')}</button>
|
|
1966
2101
|
</div>
|
|
@@ -3042,12 +3177,11 @@ export async function openSettingsModal(options = {}) {
|
|
|
3042
3177
|
let connections = [];
|
|
3043
3178
|
let ccIntegrations = { global: { installed: false }, projects: [] };
|
|
3044
3179
|
let ccSkills = [];
|
|
3045
|
-
let tunnelStatus =
|
|
3180
|
+
let tunnelStatus = null; // GET /api/tunnel/status; null until it answered (tunnel-view.js reads either)
|
|
3046
3181
|
let mcpKeyInfo = { hasKey: false };
|
|
3047
3182
|
let _bridgeResult = null;
|
|
3048
|
-
|
|
3049
|
-
let
|
|
3050
|
-
let opencodeGreetingConfig = { enabled: false, defaults: {}, projects: {}, global: {} };
|
|
3183
|
+
// The greeting (lib/greeting/): what its endpoint answered, as a reading (no answer is a reading too).
|
|
3184
|
+
let greetingReading = readGreetingStatus({ status: 0 });
|
|
3051
3185
|
let setupStatus = { claude: {}, gemini: {}, codex: {}, opencode: {}, paths: {} };
|
|
3052
3186
|
let cliConfig = {};
|
|
3053
3187
|
let toolPermissions = {};
|
|
@@ -3057,9 +3191,12 @@ export async function openSettingsModal(options = {}) {
|
|
|
3057
3191
|
let customIconsData = { extensions: {}, filenames: {} };
|
|
3058
3192
|
let youtubeConfig = { secrets: {}, configured: {}, pipeline: {} };
|
|
3059
3193
|
let typesafeConfig = null;
|
|
3194
|
+
// The Assistant's user context (lib/assistant-user-context.js): what its endpoint answered, as a reading.
|
|
3195
|
+
// No answer is a reading too: the section says what is true instead of showing switches that do nothing.
|
|
3196
|
+
let userContextReading = readUserContextResponse({ status: 0 });
|
|
3060
3197
|
|
|
3061
3198
|
try {
|
|
3062
|
-
const [settingsRes, connRes, ccRes, skillsRes, tunnelRes, keyRes, bridgeRes, greetRes,
|
|
3199
|
+
const [settingsRes, connRes, ccRes, skillsRes, tunnelRes, keyRes, bridgeRes, greetRes, setupRes, cliRes, toolPermsRes, toolCatsRes, discordRes, skinsRes, iconsRes, youtubeRes, typesafeRes, userContextRes] = await Promise.allSettled([
|
|
3063
3200
|
fetch('/api/settings').then(r => r.json()),
|
|
3064
3201
|
fetch('/api/connections').then(r => r.json()),
|
|
3065
3202
|
fetch('/api/claude-code/integrations').then(r => r.json()),
|
|
@@ -3067,9 +3204,7 @@ export async function openSettingsModal(options = {}) {
|
|
|
3067
3204
|
fetch('/api/tunnel/status').then(r => r.json()),
|
|
3068
3205
|
fetch('/api/mcp-key').then(r => r.json()),
|
|
3069
3206
|
fetch('/api/bridges/openclaw').then(r => r.json()),
|
|
3070
|
-
|
|
3071
|
-
fetch('/api/codex-panel/greeting/config').then(r => r.json()),
|
|
3072
|
-
fetch('/api/opencode-panel/greeting/config').then(r => r.json()),
|
|
3207
|
+
loadGreetingReading(),
|
|
3073
3208
|
fetch('/api/setup/status').then(r => r.json()),
|
|
3074
3209
|
fetch('/api/cli/config').then(r => r.json()),
|
|
3075
3210
|
fetch('/api/claude-code/tool-permissions').then(r => r.json()),
|
|
@@ -3079,8 +3214,10 @@ export async function openSettingsModal(options = {}) {
|
|
|
3079
3214
|
fetch('/api/file-icons').then(r => r.json()),
|
|
3080
3215
|
fetch('/api/youtube/config').then(r => r.json()),
|
|
3081
3216
|
fetch('/api/typesafe/config').then(r => r.json()),
|
|
3217
|
+
loadUserContextReading(),
|
|
3082
3218
|
]);
|
|
3083
3219
|
if (typesafeRes.status === 'fulfilled' && typesafeRes.value.ok) typesafeConfig = typesafeRes.value;
|
|
3220
|
+
if (userContextRes.status === 'fulfilled') userContextReading = userContextRes.value;
|
|
3084
3221
|
if (settingsRes.status === 'fulfilled') settings = settingsRes.value;
|
|
3085
3222
|
if (connRes.status === 'fulfilled' && connRes.value.connections) connections = connRes.value.connections;
|
|
3086
3223
|
if (ccRes.status === 'fulfilled' && ccRes.value.ok) ccIntegrations = ccRes.value;
|
|
@@ -3088,9 +3225,7 @@ export async function openSettingsModal(options = {}) {
|
|
|
3088
3225
|
if (tunnelRes.status === 'fulfilled' && tunnelRes.value.ok) tunnelStatus = tunnelRes.value;
|
|
3089
3226
|
if (keyRes.status === 'fulfilled' && keyRes.value.ok) mcpKeyInfo = keyRes.value;
|
|
3090
3227
|
if (bridgeRes.status === 'fulfilled' && bridgeRes.value.ok) _bridgeResult = bridgeRes.value;
|
|
3091
|
-
if (greetRes.status === 'fulfilled'
|
|
3092
|
-
if (codexGreetRes.status === 'fulfilled' && codexGreetRes.value.ok) codexGreetingConfig = codexGreetRes.value.config;
|
|
3093
|
-
if (opencodeGreetRes.status === 'fulfilled' && opencodeGreetRes.value.ok) opencodeGreetingConfig = opencodeGreetRes.value.config;
|
|
3228
|
+
if (greetRes.status === 'fulfilled') greetingReading = greetRes.value;
|
|
3094
3229
|
if (setupRes.status === 'fulfilled' && setupRes.value.ok) setupStatus = setupRes.value;
|
|
3095
3230
|
if (cliRes.status === 'fulfilled' && cliRes.value.ok) cliConfig = cliRes.value.config;
|
|
3096
3231
|
if (toolPermsRes.status === 'fulfilled' && toolPermsRes.value.ok) toolPermissions = toolPermsRes.value.tools;
|
|
@@ -3151,7 +3286,7 @@ export async function openSettingsModal(options = {}) {
|
|
|
3151
3286
|
<div class="settings-content">
|
|
3152
3287
|
${buildPagesHTML({
|
|
3153
3288
|
server: buildServerTab(settings, connections),
|
|
3154
|
-
hooks: buildConnectionsTab(ccIntegrations, ccSkills, tunnelStatus, mcpKeyInfo, openclawBridge,
|
|
3289
|
+
hooks: buildConnectionsTab(ccIntegrations, ccSkills, tunnelStatus, mcpKeyInfo, openclawBridge, describeGreeting(greetingReading, null, { tx: kit.tx, t: kit.tx }), toolPermissions, toolCategories, describeUserContext(userContextReading, { tx: kit.tx })),
|
|
3155
3290
|
terminal: buildTerminalTab(cliConfig),
|
|
3156
3291
|
opencode: buildOpencodeTab(),
|
|
3157
3292
|
notifications: buildNotificationsTab(),
|
|
@@ -3167,6 +3302,7 @@ export async function openSettingsModal(options = {}) {
|
|
|
3167
3302
|
whatsapp: buildWhatsAppTab(),
|
|
3168
3303
|
morelogin: buildMoreLoginTab(),
|
|
3169
3304
|
permissions: buildPermissionsTab(toolCategories, toolPermissions),
|
|
3305
|
+
editor: buildCodeEditorTab(),
|
|
3170
3306
|
social: buildSocialTab(toolCategories, toolPermissions),
|
|
3171
3307
|
youtube: buildYoutubeTab(youtubeConfig),
|
|
3172
3308
|
language: buildLanguageTab(),
|
|
@@ -4259,6 +4395,9 @@ export async function openSettingsModal(options = {}) {
|
|
|
4259
4395
|
// ── WhatsApp tab — self-loads its status (never part of the blocking fetch above) ──
|
|
4260
4396
|
wireWhatsAppTab(overlay, { toast: showCCToast });
|
|
4261
4397
|
|
|
4398
|
+
// ── Code editor: AI autocomplete (loads and saves itself) ──
|
|
4399
|
+
wireCodeEditorTab(overlay);
|
|
4400
|
+
|
|
4262
4401
|
// ── Discord tab handlers ──
|
|
4263
4402
|
{
|
|
4264
4403
|
// Token save
|
|
@@ -4681,6 +4820,152 @@ export async function openSettingsModal(options = {}) {
|
|
|
4681
4820
|
toggle.addEventListener('click', () => { ccToggleHook('global', null, toggle, overlay.querySelector('.iface-section[data-cc-target="global"]'), toggle.dataset.ccHook); });
|
|
4682
4821
|
});
|
|
4683
4822
|
|
|
4823
|
+
// ── The Assistant's user context: layers and the digest preview ──
|
|
4824
|
+
const ucRowsEl = overlay.querySelector('#uc-digest-rows');
|
|
4825
|
+
const redrawUserContext = (reading = userContextReading) => drawUserContext(overlay, describeUserContext(reading, { tx: kit.tx }));
|
|
4826
|
+
// What could not be saved, said next to the switches (role="alert"); empty clears it.
|
|
4827
|
+
const setUserContextSaveStatus = (text = '') => {
|
|
4828
|
+
const status = overlay.querySelector('#uc-save-status');
|
|
4829
|
+
if (status) { status.hidden = !text; status.textContent = text; }
|
|
4830
|
+
};
|
|
4831
|
+
// A line of the digest opens its memory (the owner corrects line 1 there and the digest follows).
|
|
4832
|
+
ucRowsEl?.addEventListener('click', (e) => {
|
|
4833
|
+
const row = e.target.closest('[data-uc-memory]');
|
|
4834
|
+
if (!row) return;
|
|
4835
|
+
const id = row.dataset.ucMemory;
|
|
4836
|
+
close();
|
|
4837
|
+
emit('memory:open', { id });
|
|
4838
|
+
});
|
|
4839
|
+
overlay.querySelector('#uc-digest-all')?.addEventListener('click', () => {
|
|
4840
|
+
close();
|
|
4841
|
+
emit('explorer:show-category', { category: 'user-knowledge', fallback: 'communication-style' });
|
|
4842
|
+
});
|
|
4843
|
+
// A note that names another Settings page (learning needs AI decisions) goes there.
|
|
4844
|
+
overlay.querySelector('#uc-layers')?.addEventListener('click', (e) => {
|
|
4845
|
+
const link = e.target.closest('[data-uc-goto]');
|
|
4846
|
+
if (!link) return;
|
|
4847
|
+
try { applySettingsOpenOptions(overlay, JSON.parse(link.dataset.ucGoto)); } catch { /* a malformed target goes nowhere */ }
|
|
4848
|
+
});
|
|
4849
|
+
// The switch moves at once and follows the server's answer: the stored settings when the save went through,
|
|
4850
|
+
// back where it was (with the reason next to the switches) when it did not. Saves go one after the other, in
|
|
4851
|
+
// the order of the clicks, so a second click made before the first was answered is kept, not dropped.
|
|
4852
|
+
let userContextSaves = Promise.resolve();
|
|
4853
|
+
const userContextWanted = {}; // layer → what its last click asked for, until that save is answered
|
|
4854
|
+
const userContextWaiting = {}; // layer → saves not answered yet
|
|
4855
|
+
const userContextBusy = () => Object.values(userContextWaiting).some(count => count > 0);
|
|
4856
|
+
// What the page shows while saves are on their way: the stored settings with the clicks on top.
|
|
4857
|
+
const userContextShown = () => (userContextReading.state === 'ready'
|
|
4858
|
+
? { ...userContextReading, payload: { ...userContextReading.payload, settings: { ...userContextReading.payload.settings, ...userContextWanted } } }
|
|
4859
|
+
: userContextReading);
|
|
4860
|
+
const markUserContextWaiting = () => overlay.querySelectorAll('.cc-toggle[data-uc-layer]').forEach(other => { other.style.opacity = userContextWaiting[other.dataset.ucLayer] > 0 ? '0.4' : ''; });
|
|
4861
|
+
overlay.querySelectorAll('.cc-toggle[data-uc-layer]').forEach(toggle => {
|
|
4862
|
+
toggle.addEventListener('click', () => {
|
|
4863
|
+
if (toggle.disabled || userContextReading.state !== 'ready') return;
|
|
4864
|
+
const layerKey = toggle.dataset.ucLayer;
|
|
4865
|
+
const wanted = !toggle.classList.contains('on');
|
|
4866
|
+
const name = overlay.querySelector(`#uc-layer-${layerKey}-label`)?.textContent?.trim() || layerKey;
|
|
4867
|
+
userContextWanted[layerKey] = wanted;
|
|
4868
|
+
userContextWaiting[layerKey] = (userContextWaiting[layerKey] || 0) + 1;
|
|
4869
|
+
setUserContextSaveStatus('');
|
|
4870
|
+
redrawUserContext(userContextShown());
|
|
4871
|
+
markUserContextWaiting();
|
|
4872
|
+
userContextSaves = userContextSaves.then(async () => {
|
|
4873
|
+
let failure = null;
|
|
4874
|
+
try {
|
|
4875
|
+
const res = await fetch('/api/assistant/user-context', { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ [layerKey]: wanted }) });
|
|
4876
|
+
const body = await res.json().catch(() => null);
|
|
4877
|
+
const reading = readUserContextResponse({ status: res.status, body });
|
|
4878
|
+
if (res.ok && reading.state === 'ready') userContextReading = reading;
|
|
4879
|
+
else failure = { status: res.status, body };
|
|
4880
|
+
} catch (err) {
|
|
4881
|
+
failure = { status: 0, error: err?.message };
|
|
4882
|
+
}
|
|
4883
|
+
userContextWaiting[layerKey] -= 1;
|
|
4884
|
+
// Every click on this switch was answered: it shows the stored value again (a later click still on its way keeps its wish).
|
|
4885
|
+
if (userContextWaiting[layerKey] <= 0) delete userContextWanted[layerKey];
|
|
4886
|
+
if (!overlay.isConnected) return;
|
|
4887
|
+
if (failure) setUserContextSaveStatus(describeUserContextSaveFailure({ name, wanted, ...failure }, { tx: kit.tx }));
|
|
4888
|
+
redrawUserContext(userContextShown());
|
|
4889
|
+
markUserContextWaiting();
|
|
4890
|
+
});
|
|
4891
|
+
});
|
|
4892
|
+
});
|
|
4893
|
+
// Switched in another window (or by a hand edit another Settings saved): this one follows.
|
|
4894
|
+
const offUserContextSync = on('sync:assistant:user-context-changed', async () => {
|
|
4895
|
+
if (userContextBusy() || userContextReading.state !== 'ready' || !overlay.isConnected) return;
|
|
4896
|
+
const reading = await loadUserContextReading();
|
|
4897
|
+
if (reading.state !== 'ready' || userContextBusy() || !overlay.isConnected) return;
|
|
4898
|
+
userContextReading = reading;
|
|
4899
|
+
redrawUserContext();
|
|
4900
|
+
});
|
|
4901
|
+
overlay.addEventListener('settings-close', () => offUserContextSync(), { once: true });
|
|
4902
|
+
|
|
4903
|
+
// ── Greeting (lib/greeting/): the one switch, and what a new session would open with right now ──
|
|
4904
|
+
// The switch moves at once and goes back, with the reason in the line under it, when the save fails;
|
|
4905
|
+
// quick clicks are saved in the order they were made. The preview is asked when Settings opens and
|
|
4906
|
+
// after every change; "Write it now" asks the writer for the sentence (one small call).
|
|
4907
|
+
let greetingPreview = null;
|
|
4908
|
+
let greetingSaves = Promise.resolve();
|
|
4909
|
+
let greetingClosed = false; // Settings was closed: an answer that arrives late draws nothing
|
|
4910
|
+
const drawGreeting = () => drawGreetingSection(overlay, describeGreeting(greetingReading, greetingPreview, { tx: kit.tx, t: kit.tx }));
|
|
4911
|
+
const refreshGreetingPreview = async ({ write = false } = {}) => {
|
|
4912
|
+
if (greetingReading.state !== 'ready') { drawGreeting(); return; }
|
|
4913
|
+
if (!greetingPreview) greetingPreview = { loading: true };
|
|
4914
|
+
drawGreeting();
|
|
4915
|
+
const answer = await fetchGreetingPreview({ write });
|
|
4916
|
+
if (greetingClosed) return;
|
|
4917
|
+
greetingPreview = answer;
|
|
4918
|
+
drawGreeting();
|
|
4919
|
+
};
|
|
4920
|
+
const greetingToggle = overlay.querySelector('[data-gr-toggle]');
|
|
4921
|
+
if (greetingToggle) {
|
|
4922
|
+
greetingToggle.addEventListener('click', () => {
|
|
4923
|
+
if (greetingToggle.disabled || greetingReading.state !== 'ready') return;
|
|
4924
|
+
const before = greetingReading;
|
|
4925
|
+
const wanted = before.payload.settings.enabled === false;
|
|
4926
|
+
greetingReading = { ...before, payload: { ...before.payload, settings: { ...before.payload.settings, enabled: wanted }, enabled: wanted && before.payload.killed !== true } };
|
|
4927
|
+
const status = overlay.querySelector('#gr-save-status');
|
|
4928
|
+
if (status) { status.hidden = true; status.textContent = ''; }
|
|
4929
|
+
drawGreeting();
|
|
4930
|
+
greetingSaves = greetingSaves.then(async () => {
|
|
4931
|
+
let res = null;
|
|
4932
|
+
let body = null;
|
|
4933
|
+
let error = null;
|
|
4934
|
+
try {
|
|
4935
|
+
res = await fetch('/api/greeting', { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ enabled: wanted }) });
|
|
4936
|
+
body = await res.json().catch(() => null);
|
|
4937
|
+
} catch (err) { error = err?.message || String(err); }
|
|
4938
|
+
const next = readGreetingStatus({ status: res?.status || 0, body, error });
|
|
4939
|
+
if (next.state === 'ready') greetingReading = next;
|
|
4940
|
+
else {
|
|
4941
|
+
greetingReading = before;
|
|
4942
|
+
if (status) { status.textContent = describeGreetingSaveFailure({ status: res?.status || 0, body, error }, { tx: kit.tx }); status.hidden = false; }
|
|
4943
|
+
}
|
|
4944
|
+
drawGreeting();
|
|
4945
|
+
if (next.state === 'ready') await refreshGreetingPreview();
|
|
4946
|
+
});
|
|
4947
|
+
});
|
|
4948
|
+
}
|
|
4949
|
+
const greetingWrite = overlay.querySelector('#gr-write');
|
|
4950
|
+
if (greetingWrite) {
|
|
4951
|
+
greetingWrite.addEventListener('click', async () => {
|
|
4952
|
+
if (greetingWrite.disabled) return;
|
|
4953
|
+
const label = greetingWrite.textContent;
|
|
4954
|
+
greetingWrite.disabled = true;
|
|
4955
|
+
greetingWrite.textContent = kit.tx('settings.redesign.hooks.greet.preview.writing');
|
|
4956
|
+
try { await refreshGreetingPreview({ write: true }); }
|
|
4957
|
+
finally { greetingWrite.disabled = false; greetingWrite.textContent = label; }
|
|
4958
|
+
});
|
|
4959
|
+
}
|
|
4960
|
+
// Switched in another window: an open Settings follows.
|
|
4961
|
+
const offGreetingSync = on('sync:greeting:changed', async () => {
|
|
4962
|
+
greetingReading = await loadGreetingReading();
|
|
4963
|
+
if (greetingClosed) return;
|
|
4964
|
+
await refreshGreetingPreview();
|
|
4965
|
+
});
|
|
4966
|
+
overlay.addEventListener('settings-close', () => { greetingClosed = true; offGreetingSync(); }, { once: true });
|
|
4967
|
+
refreshGreetingPreview();
|
|
4968
|
+
|
|
4684
4969
|
// Feature toggles
|
|
4685
4970
|
overlay.querySelectorAll('.cc-toggle[data-cc-feature]').forEach(toggle => {
|
|
4686
4971
|
toggle.addEventListener('click', async () => {
|
|
@@ -4691,11 +4976,6 @@ export async function openSettingsModal(options = {}) {
|
|
|
4691
4976
|
const data = await res.json();
|
|
4692
4977
|
if (data.ok) {
|
|
4693
4978
|
toggle.classList.toggle('on'); const row = toggle.closest('.cc-integration-item'); if (row) row.classList.toggle('enabled', !isOn);
|
|
4694
|
-
// Show/hide greeting body when greeting feature is toggled
|
|
4695
|
-
if (feature === 'greeting') {
|
|
4696
|
-
const gcBody = overlay.querySelector('#cc-greeting-body');
|
|
4697
|
-
if (gcBody) gcBody.style.display = !isOn ? 'block' : 'none';
|
|
4698
|
-
}
|
|
4699
4979
|
// Show/hide user learning threshold when feature is toggled
|
|
4700
4980
|
if (feature === 'userLearning') {
|
|
4701
4981
|
const ulThreshold = overlay.querySelector('#cc-ul-threshold');
|
|
@@ -4708,64 +4988,6 @@ export async function openSettingsModal(options = {}) {
|
|
|
4708
4988
|
});
|
|
4709
4989
|
});
|
|
4710
4990
|
|
|
4711
|
-
overlay.querySelectorAll('.cc-toggle[data-codex-greeting-toggle]').forEach(toggle => {
|
|
4712
|
-
toggle.addEventListener('click', async () => {
|
|
4713
|
-
const isOn = toggle.classList.contains('on');
|
|
4714
|
-
try {
|
|
4715
|
-
toggle.style.opacity = '0.4';
|
|
4716
|
-
toggle.style.pointerEvents = 'none';
|
|
4717
|
-
const res = await fetch('/api/codex-panel/greeting/config', {
|
|
4718
|
-
method: 'PUT',
|
|
4719
|
-
headers: { 'Content-Type': 'application/json' },
|
|
4720
|
-
body: JSON.stringify({ enabled: !isOn }),
|
|
4721
|
-
});
|
|
4722
|
-
const data = await res.json();
|
|
4723
|
-
if (!data.ok) {
|
|
4724
|
-
alert(data.error || kit.tx('settings.redesign.hooks.failedToToggleCodexGreeting'));
|
|
4725
|
-
return;
|
|
4726
|
-
}
|
|
4727
|
-
codexGreetingConfig.enabled = !isOn;
|
|
4728
|
-
toggle.classList.toggle('on', !isOn);
|
|
4729
|
-
const body = overlay.querySelector('#codex-greeting-body');
|
|
4730
|
-
if (body) body.style.display = !isOn ? 'block' : 'none';
|
|
4731
|
-
} catch (err) {
|
|
4732
|
-
alert(kit.tx('settings.redesign.hooks.failed2', { message: err.message }));
|
|
4733
|
-
} finally {
|
|
4734
|
-
toggle.style.opacity = '';
|
|
4735
|
-
toggle.style.pointerEvents = '';
|
|
4736
|
-
}
|
|
4737
|
-
});
|
|
4738
|
-
});
|
|
4739
|
-
|
|
4740
|
-
overlay.querySelectorAll('.cc-toggle[data-opencode-greeting-toggle]').forEach(toggle => {
|
|
4741
|
-
toggle.addEventListener('click', async () => {
|
|
4742
|
-
const isOn = toggle.classList.contains('on');
|
|
4743
|
-
try {
|
|
4744
|
-
toggle.style.opacity = '0.4';
|
|
4745
|
-
toggle.style.pointerEvents = 'none';
|
|
4746
|
-
const res = await fetch('/api/opencode-panel/greeting/config', {
|
|
4747
|
-
method: 'PUT',
|
|
4748
|
-
headers: { 'Content-Type': 'application/json' },
|
|
4749
|
-
body: JSON.stringify({ enabled: !isOn }),
|
|
4750
|
-
});
|
|
4751
|
-
const data = await res.json();
|
|
4752
|
-
if (!data.ok) {
|
|
4753
|
-
alert(data.error || kit.tx('settings.redesign.hooks.failedToToggleOpencodeGreeting'));
|
|
4754
|
-
return;
|
|
4755
|
-
}
|
|
4756
|
-
opencodeGreetingConfig.enabled = !isOn;
|
|
4757
|
-
toggle.classList.toggle('on', !isOn);
|
|
4758
|
-
const body = overlay.querySelector('#opencode-greeting-body');
|
|
4759
|
-
if (body) body.style.display = !isOn ? 'block' : 'none';
|
|
4760
|
-
} catch (err) {
|
|
4761
|
-
alert(kit.tx('settings.redesign.hooks.failed2', { message: err.message }));
|
|
4762
|
-
} finally {
|
|
4763
|
-
toggle.style.opacity = '';
|
|
4764
|
-
toggle.style.pointerEvents = '';
|
|
4765
|
-
}
|
|
4766
|
-
});
|
|
4767
|
-
});
|
|
4768
|
-
|
|
4769
4991
|
// ── Tool Permission toggles ──
|
|
4770
4992
|
function updateToolBadges() {
|
|
4771
4993
|
// Update per-category counts across all tabs (Automations + Social)
|
|
@@ -4872,242 +5094,6 @@ export async function openSettingsModal(options = {}) {
|
|
|
4872
5094
|
}
|
|
4873
5095
|
}
|
|
4874
5096
|
|
|
4875
|
-
// ── Greeting editor handlers ──
|
|
4876
|
-
function wireGreetingEditor({ prefix, configRef, saveBaseUrl, onSave }) {
|
|
4877
|
-
const gcSelect = overlay.querySelector(`#${prefix}-greeting-project`);
|
|
4878
|
-
const gcTemplate = overlay.querySelector(`#${prefix}-greeting-template`);
|
|
4879
|
-
const gcShowReminders = overlay.querySelector(`#${prefix}-greeting-show-reminders`);
|
|
4880
|
-
const gcShowLastSession = overlay.querySelector(`#${prefix}-greeting-show-last-session`);
|
|
4881
|
-
const gcReminderList = overlay.querySelector(`#${prefix}-greeting-reminders`);
|
|
4882
|
-
const gcAddReminder = overlay.querySelector(`#${prefix}-greeting-add-reminder`);
|
|
4883
|
-
const gcSave = overlay.querySelector(`#${prefix}-greeting-save`);
|
|
4884
|
-
if (!gcSelect || !gcTemplate || !gcReminderList || !gcSave) return;
|
|
4885
|
-
|
|
4886
|
-
function getGreetingCfg(key) {
|
|
4887
|
-
const gc = configRef() || { defaults: {}, projects: {}, global: {} };
|
|
4888
|
-
if (key === 'global') return { ...gc.defaults, ...gc.global };
|
|
4889
|
-
return { ...gc.defaults, ...(gc.projects[key] || {}) };
|
|
4890
|
-
}
|
|
4891
|
-
|
|
4892
|
-
function reminderRowHTML(reminder) {
|
|
4893
|
-
return `<div class="cc-greeting-reminder-row">
|
|
4894
|
-
<span class="cc-greeting-drag-handle" title="${kit.te('settings.redesign.hooks.dragToReorder')}">⋮⋮</span>
|
|
4895
|
-
<input class="cc-greeting-reminder-input label" placeholder="${kit.te('settings.redesign.hooks.label')}" value="${escapeHtml(reminder.label || '')}">
|
|
4896
|
-
<input class="cc-greeting-reminder-input cmd" placeholder="${kit.te('settings.redesign.hooks.command')}" value="${escapeHtml(reminder.command || '')}">
|
|
4897
|
-
<button class="cc-greeting-reminder-remove" title="${kit.te('settings.redesign.hooks.remove')}">×</button>
|
|
4898
|
-
</div>`;
|
|
4899
|
-
}
|
|
4900
|
-
|
|
4901
|
-
function wireReminderRemoveButtons() {
|
|
4902
|
-
gcReminderList.querySelectorAll('.cc-greeting-reminder-remove').forEach((btn) => {
|
|
4903
|
-
btn.onclick = () => {
|
|
4904
|
-
const row = btn.closest('.cc-greeting-reminder-row');
|
|
4905
|
-
if (!row) return;
|
|
4906
|
-
row.style.opacity = '0';
|
|
4907
|
-
row.style.transition = 'opacity 0.15s';
|
|
4908
|
-
setTimeout(() => row.remove(), 150);
|
|
4909
|
-
};
|
|
4910
|
-
});
|
|
4911
|
-
}
|
|
4912
|
-
|
|
4913
|
-
function wireReminderDragHandles() {
|
|
4914
|
-
gcReminderList.querySelectorAll('.cc-greeting-drag-handle').forEach((handle) => {
|
|
4915
|
-
handle.onmousedown = (event) => {
|
|
4916
|
-
event.preventDefault();
|
|
4917
|
-
const row = handle.closest('.cc-greeting-reminder-row');
|
|
4918
|
-
if (!row) return;
|
|
4919
|
-
row.style.opacity = '0.5';
|
|
4920
|
-
const rows = [...gcReminderList.querySelectorAll('.cc-greeting-reminder-row')];
|
|
4921
|
-
const startY = event.clientY;
|
|
4922
|
-
const startIdx = rows.indexOf(row);
|
|
4923
|
-
const onMove = (moveEvent) => {
|
|
4924
|
-
const dy = moveEvent.clientY - startY;
|
|
4925
|
-
const rowH = row.offsetHeight + 4;
|
|
4926
|
-
const shift = Math.round(dy / rowH);
|
|
4927
|
-
const newIdx = Math.max(0, Math.min(rows.length - 1, startIdx + shift));
|
|
4928
|
-
if (newIdx !== rows.indexOf(row)) {
|
|
4929
|
-
const ref = gcReminderList.children[newIdx];
|
|
4930
|
-
if (newIdx > rows.indexOf(row)) gcReminderList.insertBefore(row, ref?.nextSibling || null);
|
|
4931
|
-
else gcReminderList.insertBefore(row, ref);
|
|
4932
|
-
}
|
|
4933
|
-
};
|
|
4934
|
-
const onUp = () => {
|
|
4935
|
-
row.style.opacity = '';
|
|
4936
|
-
document.removeEventListener('mousemove', onMove);
|
|
4937
|
-
document.removeEventListener('mouseup', onUp);
|
|
4938
|
-
};
|
|
4939
|
-
document.addEventListener('mousemove', onMove);
|
|
4940
|
-
document.addEventListener('mouseup', onUp);
|
|
4941
|
-
};
|
|
4942
|
-
});
|
|
4943
|
-
}
|
|
4944
|
-
|
|
4945
|
-
function populateGreetingFields(key) {
|
|
4946
|
-
const cfg = getGreetingCfg(key);
|
|
4947
|
-
gcTemplate.value = cfg.greetingTemplate || '';
|
|
4948
|
-
if (gcShowReminders) gcShowReminders.checked = !!cfg.showReminders;
|
|
4949
|
-
if (gcShowLastSession) gcShowLastSession.checked = !!cfg.showLastSession;
|
|
4950
|
-
gcReminderList.innerHTML = (cfg.reminders || []).map((reminder) => reminderRowHTML(reminder)).join('');
|
|
4951
|
-
wireReminderRemoveButtons();
|
|
4952
|
-
wireReminderDragHandles();
|
|
4953
|
-
}
|
|
4954
|
-
|
|
4955
|
-
const gcDropdown = overlay.querySelector(`#${prefix}-greeting-project-dropdown`);
|
|
4956
|
-
const gcDropdownLabel = overlay.querySelector(`#${prefix}-greeting-project-label`);
|
|
4957
|
-
const gcDropdownMenu = overlay.querySelector(`#${prefix}-greeting-project-menu`);
|
|
4958
|
-
if (gcDropdown && gcDropdownMenu) {
|
|
4959
|
-
gcDropdown.querySelector('.cc-dropdown-trigger')?.addEventListener('click', () => {
|
|
4960
|
-
gcDropdown.classList.toggle('open');
|
|
4961
|
-
});
|
|
4962
|
-
gcDropdownMenu.querySelectorAll('.cc-dropdown-item').forEach((item) => {
|
|
4963
|
-
item.addEventListener('click', () => {
|
|
4964
|
-
const val = item.dataset.value;
|
|
4965
|
-
gcSelect.value = val;
|
|
4966
|
-
gcSelect.dispatchEvent(new Event('change'));
|
|
4967
|
-
if (gcDropdownLabel) gcDropdownLabel.textContent = item.textContent;
|
|
4968
|
-
gcDropdownMenu.querySelectorAll('.cc-dropdown-item').forEach((entry) => entry.classList.remove('active'));
|
|
4969
|
-
item.classList.add('active');
|
|
4970
|
-
gcDropdown.classList.remove('open');
|
|
4971
|
-
populateGreetingFields(val);
|
|
4972
|
-
});
|
|
4973
|
-
});
|
|
4974
|
-
document.addEventListener('click', (event) => {
|
|
4975
|
-
if (!gcDropdown.contains(event.target)) gcDropdown.classList.remove('open');
|
|
4976
|
-
});
|
|
4977
|
-
}
|
|
4978
|
-
|
|
4979
|
-
gcAddReminder?.addEventListener('click', () => {
|
|
4980
|
-
const tmp = document.createElement('div');
|
|
4981
|
-
tmp.innerHTML = reminderRowHTML({ label: '', command: '' });
|
|
4982
|
-
const newRow = tmp.firstElementChild;
|
|
4983
|
-
gcReminderList.appendChild(newRow);
|
|
4984
|
-
wireReminderRemoveButtons();
|
|
4985
|
-
wireReminderDragHandles();
|
|
4986
|
-
newRow.querySelector('.label')?.focus();
|
|
4987
|
-
});
|
|
4988
|
-
|
|
4989
|
-
gcSave.addEventListener('click', async () => {
|
|
4990
|
-
const project = gcSelect.value || 'global';
|
|
4991
|
-
const reminders = [];
|
|
4992
|
-
gcReminderList.querySelectorAll('.cc-greeting-reminder-row').forEach((row) => {
|
|
4993
|
-
const label = row.querySelector('.label')?.value?.trim() || '';
|
|
4994
|
-
const command = row.querySelector('.cmd')?.value?.trim() || '';
|
|
4995
|
-
if (label || command) reminders.push({ label, command });
|
|
4996
|
-
});
|
|
4997
|
-
const body = {
|
|
4998
|
-
greetingTemplate: gcTemplate.value || '',
|
|
4999
|
-
showReminders: gcShowReminders?.checked ?? false,
|
|
5000
|
-
showLastSession: gcShowLastSession?.checked ?? false,
|
|
5001
|
-
reminders,
|
|
5002
|
-
};
|
|
5003
|
-
gcSave.style.opacity = '0.5';
|
|
5004
|
-
gcSave.style.pointerEvents = 'none';
|
|
5005
|
-
try {
|
|
5006
|
-
const res = await fetch(`${saveBaseUrl}/${encodeURIComponent(project)}`, {
|
|
5007
|
-
method: 'PUT',
|
|
5008
|
-
headers: { 'Content-Type': 'application/json' },
|
|
5009
|
-
body: JSON.stringify(body),
|
|
5010
|
-
});
|
|
5011
|
-
const data = await res.json();
|
|
5012
|
-
if (!data.ok) {
|
|
5013
|
-
alert(data.error || kit.tx('settings.redesign.hooks.failedToSave'));
|
|
5014
|
-
return;
|
|
5015
|
-
}
|
|
5016
|
-
onSave(project, body);
|
|
5017
|
-
showCCToast(kit.tx('settings.redesign.hooks.greetingConfigSaved'));
|
|
5018
|
-
} catch (err) {
|
|
5019
|
-
alert(kit.tx('settings.redesign.hooks.failed2', { message: err.message }));
|
|
5020
|
-
} finally {
|
|
5021
|
-
gcSave.style.opacity = '';
|
|
5022
|
-
gcSave.style.pointerEvents = '';
|
|
5023
|
-
}
|
|
5024
|
-
});
|
|
5025
|
-
|
|
5026
|
-
wireReminderRemoveButtons();
|
|
5027
|
-
wireReminderDragHandles();
|
|
5028
|
-
|
|
5029
|
-
const csToggle = overlay.querySelector(`#${prefix}-greeting-cheatsheet-toggle`);
|
|
5030
|
-
const csPanel = overlay.querySelector(`#${prefix}-greeting-cheatsheet`);
|
|
5031
|
-
if (csToggle && csPanel) {
|
|
5032
|
-
const updateCheatsheetPreview = () => {
|
|
5033
|
-
const hour = new Date().getHours();
|
|
5034
|
-
const timeGreeting = hour >= 5 && hour < 12 ? kit.tx('settings.redesign.hooks.goodMorning') : hour >= 12 && hour < 17 ? kit.tx('settings.redesign.hooks.goodAfternoon') : kit.tx('settings.redesign.hooks.goodEvening');
|
|
5035
|
-
const dateStr = new Date().toLocaleDateString('en-US', { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' });
|
|
5036
|
-
const key = gcSelect.value || 'global';
|
|
5037
|
-
const gc = configRef() || {};
|
|
5038
|
-
const projCfg = key === 'global' ? gc.global : gc.projects?.[key];
|
|
5039
|
-
const label = projCfg?.label || key;
|
|
5040
|
-
const name = key === 'global' ? 'global' : key;
|
|
5041
|
-
|
|
5042
|
-
overlay.querySelector(`#${prefix}-cs-time`).textContent = timeGreeting;
|
|
5043
|
-
overlay.querySelector(`#${prefix}-cs-label`).textContent = label;
|
|
5044
|
-
overlay.querySelector(`#${prefix}-cs-name`).textContent = name;
|
|
5045
|
-
overlay.querySelector(`#${prefix}-cs-branch`).textContent = kit.tx('settings.redesign.hooks.dev');
|
|
5046
|
-
overlay.querySelector(`#${prefix}-cs-date`).textContent = dateStr;
|
|
5047
|
-
|
|
5048
|
-
const tpl = gcTemplate.value || '{time_greeting}! Working on **{project_label}** ({branch} branch). {date}.';
|
|
5049
|
-
const resolved = tpl
|
|
5050
|
-
.replace(/\{time_greeting\}/g, timeGreeting)
|
|
5051
|
-
.replace(/\{project_label\}/g, label)
|
|
5052
|
-
.replace(/\{project_name\}/g, name)
|
|
5053
|
-
.replace(/\{branch\}/g, 'dev')
|
|
5054
|
-
.replace(/\{date\}/g, dateStr)
|
|
5055
|
-
.replace(/\*\*(.+?)\*\*/g, '<strong>$1</strong>')
|
|
5056
|
-
.replace(/`(.+?)`/g, '<code>$1</code>');
|
|
5057
|
-
overlay.querySelector(`#${prefix}-cs-preview`).innerHTML = resolved;
|
|
5058
|
-
};
|
|
5059
|
-
|
|
5060
|
-
csToggle.addEventListener('click', () => {
|
|
5061
|
-
const open = csPanel.style.display !== 'none';
|
|
5062
|
-
csPanel.style.display = open ? 'none' : 'block';
|
|
5063
|
-
csToggle.classList.toggle('open', !open);
|
|
5064
|
-
if (!open) updateCheatsheetPreview();
|
|
5065
|
-
});
|
|
5066
|
-
gcSelect.addEventListener('change', () => {
|
|
5067
|
-
if (csPanel.style.display !== 'none') updateCheatsheetPreview();
|
|
5068
|
-
});
|
|
5069
|
-
gcTemplate.addEventListener('input', () => {
|
|
5070
|
-
if (csPanel.style.display !== 'none') updateCheatsheetPreview();
|
|
5071
|
-
});
|
|
5072
|
-
}
|
|
5073
|
-
}
|
|
5074
|
-
|
|
5075
|
-
wireGreetingEditor({
|
|
5076
|
-
prefix: 'cc',
|
|
5077
|
-
configRef: () => greetingConfig,
|
|
5078
|
-
saveBaseUrl: '/api/greeting/config',
|
|
5079
|
-
onSave: (project, body) => {
|
|
5080
|
-
const target = project === 'global'
|
|
5081
|
-
? (greetingConfig.global || (greetingConfig.global = {}))
|
|
5082
|
-
: (greetingConfig.projects[project] || (greetingConfig.projects[project] = {}));
|
|
5083
|
-
Object.assign(target, body);
|
|
5084
|
-
},
|
|
5085
|
-
});
|
|
5086
|
-
|
|
5087
|
-
wireGreetingEditor({
|
|
5088
|
-
prefix: 'codex',
|
|
5089
|
-
configRef: () => codexGreetingConfig,
|
|
5090
|
-
saveBaseUrl: '/api/codex-panel/greeting/config',
|
|
5091
|
-
onSave: (project, body) => {
|
|
5092
|
-
const target = project === 'global'
|
|
5093
|
-
? (codexGreetingConfig.global || (codexGreetingConfig.global = {}))
|
|
5094
|
-
: (codexGreetingConfig.projects[project] || (codexGreetingConfig.projects[project] = {}));
|
|
5095
|
-
Object.assign(target, body);
|
|
5096
|
-
},
|
|
5097
|
-
});
|
|
5098
|
-
|
|
5099
|
-
wireGreetingEditor({
|
|
5100
|
-
prefix: 'opencode',
|
|
5101
|
-
configRef: () => opencodeGreetingConfig,
|
|
5102
|
-
saveBaseUrl: '/api/opencode-panel/greeting/config',
|
|
5103
|
-
onSave: (project, body) => {
|
|
5104
|
-
const target = project === 'global'
|
|
5105
|
-
? (opencodeGreetingConfig.global || (opencodeGreetingConfig.global = {}))
|
|
5106
|
-
: (opencodeGreetingConfig.projects[project] || (opencodeGreetingConfig.projects[project] = {}));
|
|
5107
|
-
Object.assign(target, body);
|
|
5108
|
-
},
|
|
5109
|
-
});
|
|
5110
|
-
|
|
5111
5097
|
// Per-project toggles
|
|
5112
5098
|
overlay.querySelectorAll('.cc-enable-btn[data-cc-project-toggle]').forEach(btn => {
|
|
5113
5099
|
btn.addEventListener('click', () => { const idx = btn.dataset.ccProjectToggle; const panel = overlay.querySelector(`.cc-panel[data-cc-idx="${idx}"]`); const projectPath = panel?.dataset.ccPath; if (projectPath) ccToggleHook('project', projectPath, btn, panel); });
|
|
@@ -5199,13 +5185,9 @@ export async function openSettingsModal(options = {}) {
|
|
|
5199
5185
|
// every action. `null` means the route is missing or failed, which is what
|
|
5200
5186
|
// a server that was not restarted after the update answers: the tab keeps
|
|
5201
5187
|
// View and Copy and says what to do, and offers no control that cannot work.
|
|
5188
|
+
// What a state is called, the action it calls for and the sentence next to it come from
|
|
5189
|
+
// settings/settings-rules.js, which the onboarding wizard reads too.
|
|
5202
5190
|
const RESTART_HINT = kit.tx('settings.redesign.setup.restartSynabunToFinishThisUpdate');
|
|
5203
|
-
const RULES_HOST_LABELS = { claude: 'Claude Code', codex: 'Codex', opencode: 'OpenCode', gemini: 'Gemini', cursor: 'Cursor', coexistence: 'coexistence' };
|
|
5204
|
-
const RULES_BADGES = {
|
|
5205
|
-
installed: [kit.tx('settings.redesign.setup.installed'), true], newer: [kit.tx('settings.redesign.setup.newer'), true], outdated: [kit.tx('settings.redesign.setup.updateAvailable'), false], modified: [kit.tx('settings.redesign.setup.edited2'), false],
|
|
5206
|
-
conflict: [kit.tx('settings.redesign.setup.conflict'), false], error: [kit.tx('settings.redesign.setup.error'), false], shadowed: [kit.tx('settings.redesign.setup.shadowed'), false],
|
|
5207
|
-
'not-installed': [kit.tx('settings.redesign.setup.notInstalled'), false], manual: [kit.tx('settings.redesign.setup.copyOnly'), false],
|
|
5208
|
-
};
|
|
5209
5191
|
let rulesStatus = setupStatus.rules?.ok ? setupStatus.rules : null;
|
|
5210
5192
|
// What the last "install for all" wrote and where, shown under that button until the next rules action.
|
|
5211
5193
|
let installNote = '';
|
|
@@ -5277,40 +5259,18 @@ export async function openSettingsModal(options = {}) {
|
|
|
5277
5259
|
const row = el('row');
|
|
5278
5260
|
|
|
5279
5261
|
rulesRenderers.push(() => {
|
|
5280
|
-
|
|
5281
|
-
|
|
5282
|
-
|
|
5283
|
-
|
|
5284
|
-
|
|
5285
|
-
|
|
5286
|
-
|
|
5287
|
-
|
|
5288
|
-
|
|
5289
|
-
|
|
5290
|
-
|
|
5291
|
-
|
|
5292
|
-
row.classList.toggle('enabled', good);
|
|
5293
|
-
pathEl.textContent = info.installedVersion ? `${info.path} (v${info.installedVersion})` : (info.path || '');
|
|
5294
|
-
pathEl.title = info.path || '';
|
|
5295
|
-
// The action the state calls for; conflict, error and shadowed only explain themselves.
|
|
5296
|
-
const action = info.detected === false ? '' : ({ 'not-installed': kit.tx('settings.redesign.setup.install'), outdated: kit.tx('settings.redesign.setup.update'), modified: kit.tx('settings.redesign.setup.replace') }[info.state] || '');
|
|
5297
|
-
// A file without SynaBun's markers is the user's own: the server refuses to replace it, so no button.
|
|
5298
|
-
const canAct = !!action && info.replaceable !== false;
|
|
5299
|
-
primary.textContent = canAct ? action : '';
|
|
5300
|
-
setShown(primary, canAct);
|
|
5301
|
-
setShown(removeBtn, ['installed', 'newer', 'outdated', 'modified', 'shadowed'].includes(info.state));
|
|
5302
|
-
// An edited copy is the user's: nothing replaces or removes it without the confirm behind each button.
|
|
5303
|
-
const editedNote = kit.tx('settings.redesign.setup.thisCopyWasEditedSoSynabun');
|
|
5304
|
-
const stateNote = info.detected === false ? kit.tx('settings.redesign.setup.wasNotFoundOnThisMachine', { label: label })
|
|
5305
|
-
: info.state === 'modified' ? (info.replaceable === false ? (info.detail || '') : editedNote)
|
|
5306
|
-
: info.state === 'outdated' ? (rulesStatus.version ? kit.tx('settings.redesign.setup.versionOfTheRulesIsAvailable', { version: rulesStatus.version }) : kit.tx('settings.redesign.setup.aNewerVersionOfTheRules'))
|
|
5307
|
-
: ['conflict', 'error', 'shadowed', 'newer'].includes(info.state) ? (info.error || info.detail || '')
|
|
5308
|
-
: '';
|
|
5309
|
-
// A removal that could not finish left rules behind: say which, until a remove or an install completes.
|
|
5310
|
-
const partial = info.partialRemoval?.error ? kit.tx('settings.redesign.setup.theLastRemovalDidNotFinish', { error: info.partialRemoval.error }) : '';
|
|
5311
|
-
const note = partial && stateNote && !partial.includes(stateNote) ? `${partial} ${stateNote}` : (partial || stateNote);
|
|
5312
|
-
message.textContent = note;
|
|
5313
|
-
setShown(message, !!note);
|
|
5262
|
+
// No status (the route is missing): "Unavailable", the restart hint as the path, and no action.
|
|
5263
|
+
const view = describeRulesHost(rulesStatus?.hosts?.[host], { label, version: rulesStatus?.version, tx: kit.tx });
|
|
5264
|
+
badge.className = `setup-status-badge ${view.good ? 'active' : 'inactive'}`;
|
|
5265
|
+
badge.textContent = view.badge;
|
|
5266
|
+
row.classList.toggle('enabled', view.good);
|
|
5267
|
+
pathEl.textContent = view.pathText;
|
|
5268
|
+
pathEl.title = view.path;
|
|
5269
|
+
primary.textContent = view.actionLabel;
|
|
5270
|
+
setShown(primary, !!view.action);
|
|
5271
|
+
setShown(removeBtn, view.removable);
|
|
5272
|
+
message.textContent = view.note;
|
|
5273
|
+
setShown(message, !!view.note);
|
|
5314
5274
|
});
|
|
5315
5275
|
|
|
5316
5276
|
primary.addEventListener('click', () => {
|
|
@@ -5318,9 +5278,7 @@ export async function openSettingsModal(options = {}) {
|
|
|
5318
5278
|
const force = state === 'modified';
|
|
5319
5279
|
if (force && !confirm(kit.tx('settings.redesign.setup.replaceYourEditedRulesWithSynabun', { label: label }))) return;
|
|
5320
5280
|
cachedText = '';
|
|
5321
|
-
runRulesAction(primary, () => installRules(host, { force }), (result) => (result.
|
|
5322
|
-
? kit.tx(state === 'not-installed' ? 'settings.redesign.setup.rulesInstalled2' : 'settings.redesign.setup.rulesUpdated2', { label })
|
|
5323
|
-
: kit.tx('settings.redesign.setup.rulesAreAlreadyCurrent', { label: label })));
|
|
5281
|
+
runRulesAction(primary, () => installRules(host, { force }), (result) => describeRulesInstall(result, state, { label, tx: kit.tx }));
|
|
5324
5282
|
});
|
|
5325
5283
|
removeBtn.addEventListener('click', () => {
|
|
5326
5284
|
const info = rulesStatus?.hosts?.[host];
|
|
@@ -5399,15 +5357,10 @@ export async function openSettingsModal(options = {}) {
|
|
|
5399
5357
|
});
|
|
5400
5358
|
installAllBtn.addEventListener('click', () => {
|
|
5401
5359
|
runRulesAction(installAllBtn, () => installAllRules(), (result) => {
|
|
5402
|
-
|
|
5403
|
-
|
|
5404
|
-
|
|
5405
|
-
|
|
5406
|
-
if (where) installNote = kit.tx('settings.redesign.setup.rulesInstalledAt', { where: where });
|
|
5407
|
-
const failed = rows.filter(([, row]) => row.ok === false);
|
|
5408
|
-
if (failed.length) return kit.tx('settings.redesign.setup.installedWithFailure', { count: rows.length - failed.length, host: hostOf[failed[0][0]] || failed[0][0], error: failed[0][1].error || failed[0][1].state });
|
|
5409
|
-
const map = rows.map(([host]) => hostOf[host] || host).join(', ');
|
|
5410
|
-
return kit.tx(rows.some(([, row]) => row.changed) ? 'settings.redesign.setup.rulesInstalledFor' : 'settings.redesign.setup.rulesAlreadyCurrentFor', { map: map });
|
|
5360
|
+
// The sentence for the toast, and under the button the file each tool reads.
|
|
5361
|
+
const report = describeRulesInstallAll(result, { tx: kit.tx });
|
|
5362
|
+
installNote = report.note;
|
|
5363
|
+
return report.message;
|
|
5411
5364
|
});
|
|
5412
5365
|
});
|
|
5413
5366
|
}
|
|
@@ -5418,6 +5371,15 @@ export async function openSettingsModal(options = {}) {
|
|
|
5418
5371
|
wireCopyBtn('setup-claude-cli-copy', () => setupStatus.claude?.cliCommand || ccIntegrations.mcp?.cliCommand || '', kit.tx('settings.redesign.setup.copyCliCommand'));
|
|
5419
5372
|
wireRulesControls('claude', 'claude');
|
|
5420
5373
|
|
|
5374
|
+
// What an MCP client runs: `node <script>` for an npm or Git install, the
|
|
5375
|
+
// application's own executable for a packaged one. The server says which.
|
|
5376
|
+
const mcpLaunch = () => {
|
|
5377
|
+
const paths = setupStatus.paths || {};
|
|
5378
|
+
return paths.mcpCommand && paths.mcpCommand !== 'node' && Array.isArray(paths.mcpArgs)
|
|
5379
|
+
? { command: paths.mcpCommand, args: paths.mcpArgs }
|
|
5380
|
+
: { command: 'node', args: [paths.mcpIndexPath || '<path-to>/mcp-server/run.mjs'] };
|
|
5381
|
+
};
|
|
5382
|
+
|
|
5421
5383
|
// ── Gemini ──
|
|
5422
5384
|
wireSetupMcpToggle('gemini', '/api/setup/gemini/mcp', '~/.gemini/settings.json');
|
|
5423
5385
|
// Config preview
|
|
@@ -5430,7 +5392,7 @@ export async function openSettingsModal(options = {}) {
|
|
|
5430
5392
|
cachedConfig = JSON.stringify(data.config, null, 2);
|
|
5431
5393
|
preview.textContent = cachedConfig;
|
|
5432
5394
|
} else if (data.ok) {
|
|
5433
|
-
cachedConfig = JSON.stringify({ mcpServers: { SynaBun: {
|
|
5395
|
+
cachedConfig = JSON.stringify({ mcpServers: { SynaBun: { ...mcpLaunch(), env: { DOTENV_PATH: setupStatus.paths?.envPath || '<path-to>/synabun/.env' } } } }, null, 2);
|
|
5434
5396
|
preview.textContent = cachedConfig;
|
|
5435
5397
|
} else { preview.textContent = kit.tx('settings.redesign.setup.couldNotLoadConfig'); }
|
|
5436
5398
|
}).catch(() => { preview.textContent = kit.tx('settings.redesign.setup.failedToLoad'); });
|
|
@@ -5452,10 +5414,11 @@ export async function openSettingsModal(options = {}) {
|
|
|
5452
5414
|
cachedConfig = data.toml;
|
|
5453
5415
|
preview.textContent = cachedConfig;
|
|
5454
5416
|
} else if (data.ok) {
|
|
5455
|
-
const
|
|
5417
|
+
const launch = mcpLaunch();
|
|
5456
5418
|
const ep = setupStatus.paths?.envPath || '<path-to>/synabun/.env';
|
|
5457
5419
|
const dataHome = setupStatus.paths?.dataHome || '<path-to>/synabun';
|
|
5458
|
-
|
|
5420
|
+
// A JSON string is a TOML basic string: the same quotes and escapes.
|
|
5421
|
+
cachedConfig = `[mcp_servers.SynaBun]\ncommand = ${JSON.stringify(launch.command)}\nargs = [${launch.args.map(arg => JSON.stringify(arg)).join(', ')}]\nenv = { DOTENV_PATH = "${ep}", SYNABUN_DATA_HOME = "${dataHome}", MEMORY_DATA_DIR = "${dataHome}/mcp-data", SYNABUN_PROFILE = "full", SYNABUN_BROWSER_FAST = "1", SYNABUN_BROWSER_COMPACT = "1", SYNABUN_TOOL_CATALOG_MODE = "deferred" }`;
|
|
5459
5422
|
preview.textContent = cachedConfig;
|
|
5460
5423
|
} else { preview.textContent = kit.tx('settings.redesign.setup.couldNotLoadConfig'); }
|
|
5461
5424
|
}).catch(() => { preview.textContent = kit.tx('settings.redesign.setup.failedToLoad'); });
|
|
@@ -5471,7 +5434,6 @@ export async function openSettingsModal(options = {}) {
|
|
|
5471
5434
|
const preview = overlay.querySelector('#setup-opencode-config-preview');
|
|
5472
5435
|
let cachedConfig = '';
|
|
5473
5436
|
if (preview) {
|
|
5474
|
-
const mp = setupStatus.paths?.mcpIndexPath || '<path-to>/mcp-server/run.mjs';
|
|
5475
5437
|
const ep = setupStatus.paths?.envPath || '<path-to>/synabun/.env';
|
|
5476
5438
|
fetch('/api/opencode/mcp').then(r => r.json()).then(data => {
|
|
5477
5439
|
if (data.ok && data.data && Object.keys(data.data).length) {
|
|
@@ -5482,8 +5444,7 @@ export async function openSettingsModal(options = {}) {
|
|
|
5482
5444
|
mcp: {
|
|
5483
5445
|
SynaBun: {
|
|
5484
5446
|
type: 'stdio',
|
|
5485
|
-
|
|
5486
|
-
args: [mp],
|
|
5447
|
+
...mcpLaunch(),
|
|
5487
5448
|
env: { DOTENV_PATH: ep },
|
|
5488
5449
|
},
|
|
5489
5450
|
},
|
|
@@ -6465,69 +6426,48 @@ export async function openSettingsModal(options = {}) {
|
|
|
6465
6426
|
|
|
6466
6427
|
}
|
|
6467
6428
|
|
|
6468
|
-
// ──
|
|
6469
|
-
|
|
6470
|
-
|
|
6471
|
-
|
|
6472
|
-
|
|
6473
|
-
|
|
6474
|
-
|
|
6475
|
-
|
|
6476
|
-
|
|
6477
|
-
|
|
6478
|
-
|
|
6479
|
-
|
|
6480
|
-
|
|
6481
|
-
|
|
6482
|
-
|
|
6483
|
-
|
|
6484
|
-
|
|
6485
|
-
|
|
6486
|
-
|
|
6487
|
-
|
|
6488
|
-
|
|
6489
|
-
|
|
6490
|
-
|
|
6491
|
-
|
|
6492
|
-
|
|
6493
|
-
|
|
6494
|
-
|
|
6495
|
-
|
|
6496
|
-
|
|
6497
|
-
|
|
6498
|
-
|
|
6499
|
-
|
|
6500
|
-
|
|
6501
|
-
|
|
6502
|
-
|
|
6503
|
-
|
|
6504
|
-
|
|
6505
|
-
|
|
6506
|
-
|
|
6507
|
-
|
|
6508
|
-
|
|
6509
|
-
|
|
6510
|
-
} else if (attempts >= 30) { clearInterval(poll); if (label) label.textContent = kit.tx('settings.redesign.hooks.timeout'); if (urlEl) urlEl.textContent = kit.tx('settings.redesign.hooks.failedToGetTunnelUrlCheck'); }
|
|
6511
|
-
} catch {}
|
|
6512
|
-
}, 1000);
|
|
6513
|
-
}
|
|
6514
|
-
} else { alert(data.error || kit.tx('settings.redesign.hooks.failed')); }
|
|
6515
|
-
} catch (err) { alert(kit.tx('settings.redesign.hooks.failed2', { message: err.message })); }
|
|
6516
|
-
finally { tunnelToggle.style.opacity = ''; tunnelToggle.style.pointerEvents = ''; }
|
|
6517
|
-
});
|
|
6518
|
-
}
|
|
6519
|
-
|
|
6520
|
-
// Copy tunnel MCP URL
|
|
6521
|
-
const tunnelCopy = overlay.querySelector('#cc-tunnel-copy-url');
|
|
6522
|
-
if (tunnelCopy) {
|
|
6523
|
-
tunnelCopy.addEventListener('click', () => {
|
|
6524
|
-
const urlEl = overlay.querySelector('#cc-tunnel-url'); const baseUrl = urlEl?.textContent || '';
|
|
6525
|
-
if (!baseUrl) { alert(kit.tx('settings.redesign.hooks.tunnelUrlNotAvailable')); return; }
|
|
6526
|
-
const tunnelMcpUrl = mcpKeyInfo.key ? baseUrl + '/mcp/' + mcpKeyInfo.key : baseUrl + '/mcp';
|
|
6527
|
-
navigator.clipboard.writeText(tunnelMcpUrl);
|
|
6528
|
-
tunnelCopy.innerHTML = '<svg viewBox="0 0 24 24" style="width:12px;height:12px"><polyline points="20 6 9 17 4 12"/></svg> ' + kit.te('settings.redesign.hooks.copied') + '';
|
|
6529
|
-
setTimeout(() => { tunnelCopy.innerHTML = '<svg viewBox="0 0 24 24" style="width:12px;height:12px"><rect x="9" y="9" width="13" height="13" rx="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/></svg> ' + kit.te('settings.redesign.hooks.copyMcpUrl') + ''; }, 2000);
|
|
6530
|
-
});
|
|
6429
|
+
// ── Public address (the Cloudflare tunnel) ──
|
|
6430
|
+
// One block, drawn again whenever the server says something new: ui-tunnel.js asks again by itself while the
|
|
6431
|
+
// tunnel program is being downloaded or a start waits for its address, for as long as this dialog is open.
|
|
6432
|
+
// A start on a machine that has no cloudflared fetches it and goes on by itself: one press.
|
|
6433
|
+
let redrawTunnelBox = () => {};
|
|
6434
|
+
let latestTunnelStatus = () => tunnelStatus;
|
|
6435
|
+
const tunnelRow = overlay.querySelector('#cc-tunnel-row');
|
|
6436
|
+
const tunnelExtra = overlay.querySelector('#cc-tunnel-extra');
|
|
6437
|
+
if (tunnelRow && tunnelExtra) {
|
|
6438
|
+
const drawTunnel = (view) => {
|
|
6439
|
+
tunnelRow.classList.toggle('enabled', view.on);
|
|
6440
|
+
drawInto(tunnelRow, tunnelRowHtml(view));
|
|
6441
|
+
drawInto(tunnelExtra, tunnelExtraHtml(view, mcpKeyInfo));
|
|
6442
|
+
};
|
|
6443
|
+
const tunnelCard = createTunnelCard({ alive: () => tunnelRow.isConnected, onChange: drawTunnel });
|
|
6444
|
+
latestTunnelStatus = () => tunnelCard.status() || tunnelStatus;
|
|
6445
|
+
redrawTunnelBox = () => drawTunnel(tunnelCard.view());
|
|
6446
|
+
tunnelCard.show(tunnelStatus);
|
|
6447
|
+
const onTunnelClick = async (event) => {
|
|
6448
|
+
const button = event.target.closest('button');
|
|
6449
|
+
if (!button || !(tunnelRow.contains(button) || tunnelExtra.contains(button))) return;
|
|
6450
|
+
if (button.id === 'cc-tunnel-toggle' || button.id === 'cc-tunnel-retry') {
|
|
6451
|
+
const action = tunnelCard.view().action;
|
|
6452
|
+
if (action === 'start' || action === 'retry') {
|
|
6453
|
+
// Shown at once; the server answers immediately too, and then says what it is doing.
|
|
6454
|
+
tunnelCard.show({ ...(tunnelCard.status() || {}), state: 'starting', starting: true, url: null, error: null });
|
|
6455
|
+
await tunnelCard.start();
|
|
6456
|
+
} else if (action === 'stop' || action === 'cancel') {
|
|
6457
|
+
await tunnelCard.stop();
|
|
6458
|
+
}
|
|
6459
|
+
} else if (button.id === 'cc-tunnel-copy-url') {
|
|
6460
|
+
// The remote MCP address is the public one with the access key in it: there is none without a key.
|
|
6461
|
+
const address = remoteMcpAddress(tunnelCard.view().url, mcpKeyInfo?.key);
|
|
6462
|
+
if (!address) { redrawTunnelBox(); return; }
|
|
6463
|
+
navigator.clipboard.writeText(address);
|
|
6464
|
+
const before = button.innerHTML;
|
|
6465
|
+
button.innerHTML = '<svg viewBox="0 0 24 24" style="width:12px;height:12px"><polyline points="20 6 9 17 4 12"/></svg> ' + kit.te('settings.redesign.hooks.copied') + '';
|
|
6466
|
+
setTimeout(() => { if (button.isConnected) button.innerHTML = before; }, 2000);
|
|
6467
|
+
}
|
|
6468
|
+
};
|
|
6469
|
+
tunnelRow.addEventListener('click', onTunnelClick);
|
|
6470
|
+
tunnelExtra.addEventListener('click', onTunnelClick);
|
|
6531
6471
|
}
|
|
6532
6472
|
|
|
6533
6473
|
// ── API key generate / copy / revoke ──
|
|
@@ -6612,6 +6552,7 @@ export async function openSettingsModal(options = {}) {
|
|
|
6612
6552
|
if (statusEl) statusEl.textContent = '***' + data.key.slice(-8);
|
|
6613
6553
|
if (row) row.classList.add('enabled');
|
|
6614
6554
|
apikeyGenBtn.textContent = kit.tx('settings.redesign.hooks.regenerate');
|
|
6555
|
+
redrawTunnelBox();
|
|
6615
6556
|
} else { alert(data.error || kit.tx('settings.redesign.hooks.failedToGenerateKey')); }
|
|
6616
6557
|
} catch (err) { alert(kit.tx('settings.redesign.hooks.failed2', { message: err.message })); }
|
|
6617
6558
|
finally { apikeyGenBtn.style.opacity = ''; apikeyGenBtn.style.pointerEvents = ''; }
|
|
@@ -6629,15 +6570,18 @@ export async function openSettingsModal(options = {}) {
|
|
|
6629
6570
|
const apikeyRevokeBtn = overlay.querySelector('#cc-apikey-revoke');
|
|
6630
6571
|
if (apikeyRevokeBtn) {
|
|
6631
6572
|
apikeyRevokeBtn.addEventListener('click', async () => {
|
|
6632
|
-
|
|
6573
|
+
// What a revoke leaves behind depends on the server that runs: see closesKeylessMcp.
|
|
6574
|
+
const closed = closesKeylessMcp(latestTunnelStatus());
|
|
6575
|
+
if (!confirm(kit.tx(closed ? 'settings.redesign.hooks.revokeKeyClosed' : 'settings.redesign.hooks.revokeThisApiKeyTheMcp'))) return;
|
|
6633
6576
|
try {
|
|
6634
6577
|
const res = await fetch('/api/mcp-key', { method: 'DELETE' }); const data = await res.json();
|
|
6635
6578
|
if (data.ok) {
|
|
6636
6579
|
mcpKeyInfo = { hasKey: false };
|
|
6637
6580
|
const reveal = overlay.querySelector('#cc-apikey-reveal'); if (reveal) reveal.style.display = 'none';
|
|
6638
|
-
const statusEl = overlay.querySelector('#cc-apikey-status'); if (statusEl) statusEl.textContent = kit.tx('settings.redesign.hooks.
|
|
6581
|
+
const statusEl = overlay.querySelector('#cc-apikey-status'); if (statusEl) statusEl.textContent = kit.tx(closed ? 'settings.redesign.hooks.apikey.noneClosed' : 'settings.redesign.hooks.apikey.none');
|
|
6639
6582
|
const row = overlay.querySelector('#cc-apikey-row'); if (row) row.classList.remove('enabled');
|
|
6640
6583
|
const genBtn = overlay.querySelector('#cc-apikey-generate'); if (genBtn) genBtn.textContent = kit.tx('settings.redesign.hooks.generateKey');
|
|
6584
|
+
redrawTunnelBox();
|
|
6641
6585
|
}
|
|
6642
6586
|
} catch (err) { alert(kit.tx('settings.redesign.hooks.failed2', { message: err.message })); }
|
|
6643
6587
|
});
|