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
|
@@ -0,0 +1,328 @@
|
|
|
1
|
+
// ═══════════════════════════════════════════
|
|
2
|
+
// SETTINGS — the code editor's page, in words
|
|
3
|
+
// ═══════════════════════════════════════════
|
|
4
|
+
//
|
|
5
|
+
// What `GET /api/editor/config`, `GET /api/editor/capabilities` and the routes under `/api/editor/complete`
|
|
6
|
+
// (lib/editor-config.js, lib/editor-completion.js) say about Settings → Code editor: for "Editing", which
|
|
7
|
+
// preferences there are and what each is set to; for "AI autocomplete", whether the section can be used at all,
|
|
8
|
+
// what is stored, which models there are, and what a test found. Pure: no DOM and no import, so node runs it
|
|
9
|
+
// (tests/settings-code-editor-view.test.mjs).
|
|
10
|
+
//
|
|
11
|
+
// EDITING_FIELDS the editing preferences, each with its control
|
|
12
|
+
// readEditorEditingResponse({ status, body, error }) what the server answered → a reading
|
|
13
|
+
// describeEditorEditing(reading, { tx }) the "Editing" section as the pane draws it
|
|
14
|
+
// editingOptionLabel(key, value, { tx }) / editingChange(key, raw)
|
|
15
|
+
// readEditorAiResponse({ status, body, error, capabilities }) what the server answered → a reading
|
|
16
|
+
// describeEditorAi(reading, { tx }) the section as settings-code-editor-pane.js draws it
|
|
17
|
+
// describeEditorModels(answer, selected, { tx }) the model list
|
|
18
|
+
// describeEditorTest(result, { tx }) / describeEditorFastest(body, { tx })
|
|
19
|
+
// describeEditorUsage(today, { tx })
|
|
20
|
+
// describeEditorSaveFailure(input, { tx }) the sentence for a change that could not be saved
|
|
21
|
+
// parseExcludeList(text) / excludeListText(list) / isEndpointUrl(text)
|
|
22
|
+
//
|
|
23
|
+
// `tx(key, params)` is the caller's translate function over the settings.redesign.* strings (the kit's `tx`).
|
|
24
|
+
//
|
|
25
|
+
// The section never shows a healthy-looking control for something that is not happening: a server without the
|
|
26
|
+
// feature gets disabled controls and says why, and the key is only ever "set" or "not set".
|
|
27
|
+
|
|
28
|
+
const K = 'settings.redesign.code_editor';
|
|
29
|
+
|
|
30
|
+
const clean = (value, max = 200) => {
|
|
31
|
+
const text = String(value ?? '').replace(/\s+/g, ' ').trim();
|
|
32
|
+
return text.length > max ? `${text.slice(0, max - 1)}…` : text;
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
// ── Editing ──
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* The `editing` preferences (public/shared/editor/editor-prefs.js keeps their defaults and their limits), in the
|
|
39
|
+
* order the section shows them: the text, what is drawn around it, indentation, and what a save does.
|
|
40
|
+
* `id`: the control's inventory id; `kind`: a switch, or one choice among `values`.
|
|
41
|
+
*/
|
|
42
|
+
export const EDITING_FIELDS = Object.freeze([
|
|
43
|
+
{ key: 'fontSize', id: 'EDT014', kind: 'choice', values: Object.freeze([10, 11, 12, 13, 14, 15, 16, 17, 18]) },
|
|
44
|
+
{ key: 'ligatures', id: 'EDT015', kind: 'toggle' },
|
|
45
|
+
{ key: 'wordWrap', id: 'EDT016', kind: 'toggle' },
|
|
46
|
+
{ key: 'minimap', id: 'EDT017', kind: 'toggle' },
|
|
47
|
+
{ key: 'indentGuides', id: 'EDT018', kind: 'toggle' },
|
|
48
|
+
{ key: 'renderWhitespace', id: 'EDT019', kind: 'choice', values: Object.freeze(['none', 'selection', 'all']) },
|
|
49
|
+
{ key: 'indentWith', id: 'EDT020', kind: 'choice', values: Object.freeze(['auto', 'spaces', 'tabs']) },
|
|
50
|
+
{ key: 'tabSize', id: 'EDT021', kind: 'choice', values: Object.freeze(['auto', 2, 4, 8]) },
|
|
51
|
+
{ key: 'trimTrailingWhitespace', id: 'EDT022', kind: 'toggle' },
|
|
52
|
+
{ key: 'insertFinalNewline', id: 'EDT023', kind: 'toggle' },
|
|
53
|
+
]);
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* What GET /api/editor/config answered about the editing preferences.
|
|
57
|
+
*
|
|
58
|
+
* ready the preferences are there
|
|
59
|
+
* old-server 404: the running server was started before the editor kept its preferences
|
|
60
|
+
* unreadable anything else; `reason` says what
|
|
61
|
+
*
|
|
62
|
+
* @param {{ status?: number, body?: object|null, error?: string|null }} response
|
|
63
|
+
* @returns {{ state: 'ready'|'old-server'|'unreadable', editing: object|null, reason: string|null }}
|
|
64
|
+
*/
|
|
65
|
+
export function readEditorEditingResponse({ status = 0, body = null, error = null } = {}) {
|
|
66
|
+
if (status === 404) return { state: 'old-server', editing: null, reason: null };
|
|
67
|
+
if (status === 200 && body?.ok === true && body.config?.editing && typeof body.config.editing === 'object' && !Array.isArray(body.config.editing)) {
|
|
68
|
+
return { state: 'ready', editing: body.config.editing, reason: null };
|
|
69
|
+
}
|
|
70
|
+
return { state: 'unreadable', editing: null, reason: clean(body?.error || error) || (status ? `HTTP ${status}` : null) };
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/** What a choice is called in its list: "12 px", "Detect", "Spaces", "In the selection"… */
|
|
74
|
+
export function editingOptionLabel(key, value, { tx }) {
|
|
75
|
+
if (key === 'fontSize') return tx(`${K}.editing.fontSize`, { size: value });
|
|
76
|
+
if (key === 'renderWhitespace') return tx(`${K}.editing.whitespace.${value}`);
|
|
77
|
+
if (value === 'auto') return tx(`${K}.editing.detect`);
|
|
78
|
+
if (key === 'indentWith') return tx(`${K}.editing.${value}`);
|
|
79
|
+
return String(value);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* The "Editing" section as it is drawn.
|
|
84
|
+
* → { state, ready, note, values: { [key]: value } }
|
|
85
|
+
* `reading`: what readEditorEditingResponse() returned, or null while the server has not answered yet.
|
|
86
|
+
* `values`: for each field what its control shows: a boolean for a switch, one of the field's values (as text)
|
|
87
|
+
* for a choice. Before the preferences are known, a switch is off and a choice shows its first value.
|
|
88
|
+
* `note`: one line above the controls when they cannot be used (and why), else null.
|
|
89
|
+
*/
|
|
90
|
+
export function describeEditorEditing(reading, { tx }) {
|
|
91
|
+
const state = reading ? reading.state || 'unreadable' : 'loading';
|
|
92
|
+
const ready = state === 'ready';
|
|
93
|
+
const stored = ready && reading.editing ? reading.editing : {};
|
|
94
|
+
const values = {};
|
|
95
|
+
for (const field of EDITING_FIELDS) {
|
|
96
|
+
if (field.kind === 'toggle') values[field.key] = stored[field.key] === true;
|
|
97
|
+
else {
|
|
98
|
+
const found = field.values.find((value) => String(value) === String(stored[field.key]));
|
|
99
|
+
values[field.key] = String(found !== undefined ? found : field.values[0]);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
let note = null;
|
|
103
|
+
// Until the preferences arrive the controls show first choices and switches that are off: say that they are
|
|
104
|
+
// being read, so those are not taken for what is stored.
|
|
105
|
+
if (state === 'loading') note = { text: tx('settings.redesign.common.checking'), tone: 'neutral' };
|
|
106
|
+
else if (state === 'old-server') note = { text: tx(`${K}.editing.oldServer`), tone: 'warn' };
|
|
107
|
+
else if (state === 'unreadable') {
|
|
108
|
+
const reason = clean(reading?.reason);
|
|
109
|
+
note = { text: reason ? tx(`${K}.state.unreadable`, { reason }) : tx(`${K}.state.unreadableNoReason`), tone: 'warn' };
|
|
110
|
+
}
|
|
111
|
+
return { state, ready, note, values };
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* What a control's new value changes in the stored preferences.
|
|
116
|
+
* @param {string} key a field's key
|
|
117
|
+
* @param {boolean | string} raw a switch's `checked`, or a list's `value`
|
|
118
|
+
* @returns {{ editing: object } | null} what is sent to PUT /api/editor/config; null for a value the field does not have
|
|
119
|
+
*/
|
|
120
|
+
export function editingChange(key, raw) {
|
|
121
|
+
const field = EDITING_FIELDS.find((entry) => entry.key === key);
|
|
122
|
+
if (!field) return null;
|
|
123
|
+
if (field.kind === 'toggle') return typeof raw === 'boolean' ? { editing: { [key]: raw } } : null;
|
|
124
|
+
const value = field.values.find((entry) => String(entry) === String(raw));
|
|
125
|
+
return value === undefined ? null : { editing: { [key]: value } };
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// ── AI autocomplete ──
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* What the server answered.
|
|
132
|
+
*
|
|
133
|
+
* ready the preferences are there and the server has the `complete` capability
|
|
134
|
+
* old-server 404, or preferences without the capability: the running server was started before AI
|
|
135
|
+
* autocomplete existed (public/ reloads at any time, lib/ only on a restart)
|
|
136
|
+
* unreadable anything else (no answer, a refusal, a body that is not the preferences); `reason` says what
|
|
137
|
+
*
|
|
138
|
+
* @param {{ status?: number, body?: object|null, error?: string|null, capabilities?: string[]|null }} response
|
|
139
|
+
* `status` and `body` of GET /api/editor/config; `capabilities` of GET /api/editor/capabilities (null: not answered)
|
|
140
|
+
* @returns {{ state: 'ready'|'old-server'|'unreadable', config: object|null, hasEndpointKey: boolean, reason: string|null }}
|
|
141
|
+
*/
|
|
142
|
+
export function readEditorAiResponse({ status = 0, body = null, error = null, capabilities = null } = {}) {
|
|
143
|
+
const out = (state, reason = null) => ({ state, config: null, hasEndpointKey: false, reason });
|
|
144
|
+
if (status === 404) return out('old-server');
|
|
145
|
+
if (status === 200 && body?.ok === true && body.config?.ai && typeof body.config.ai === 'object') {
|
|
146
|
+
if (!Array.isArray(capabilities) || !capabilities.includes('complete')) return out('old-server');
|
|
147
|
+
return { state: 'ready', config: body.config, hasEndpointKey: body.hasEndpointKey === true, reason: null };
|
|
148
|
+
}
|
|
149
|
+
return out('unreadable', clean(body?.error || error) || (status ? `HTTP ${status}` : null));
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* The section as it is drawn.
|
|
154
|
+
* → { state, ready, note, pill: { text, state }, enabled, engine, model, endpoint: { kind, url, model, shape },
|
|
155
|
+
* hasKey, keyState, keyPlaceholder, urlPlaceholder, exclude (text), excludeCount }
|
|
156
|
+
* `reading`: what readEditorAiResponse() returned, or null while the server has not answered yet (the controls
|
|
157
|
+
* are then switched off without a word about why: nothing is known to be wrong).
|
|
158
|
+
* `note`: one line above the controls when they cannot be used (and why), else null.
|
|
159
|
+
* `pill.state`: 'ok' on and able to ask · 'warn' on but with nothing to ask yet · 'off'. An empty `pill.text`
|
|
160
|
+
* is not shown.
|
|
161
|
+
*/
|
|
162
|
+
export function describeEditorAi(reading, { tx }) {
|
|
163
|
+
const state = reading ? reading.state || 'unreadable' : 'loading';
|
|
164
|
+
const ready = state === 'ready';
|
|
165
|
+
const view = {
|
|
166
|
+
state, ready, note: null, pill: { text: state === 'loading' ? '' : tx(`${K}.pill.unavailable`), state: 'off' },
|
|
167
|
+
enabled: false, engine: 'opencode', model: null, endpoint: { kind: 'ollama', url: '', model: '', shape: 'fim' },
|
|
168
|
+
hasKey: false, keyState: '', keyPlaceholder: tx(`${K}.key.placeholder`), urlPlaceholder: tx(`${K}.url.placeholderOllama`),
|
|
169
|
+
exclude: '', excludeCount: '',
|
|
170
|
+
};
|
|
171
|
+
if (state === 'loading') {
|
|
172
|
+
view.note = { text: tx('settings.redesign.common.checking'), tone: 'neutral' };
|
|
173
|
+
return view;
|
|
174
|
+
}
|
|
175
|
+
if (!ready) {
|
|
176
|
+
const reason = clean(reading?.reason);
|
|
177
|
+
if (state === 'old-server') view.note = { text: tx(`${K}.state.oldServer`), tone: 'warn' };
|
|
178
|
+
else view.note = { text: reason ? tx(`${K}.state.unreadable`, { reason }) : tx(`${K}.state.unreadableNoReason`), tone: 'warn' };
|
|
179
|
+
return view;
|
|
180
|
+
}
|
|
181
|
+
const ai = reading.config.ai;
|
|
182
|
+
const endpoint = ai.endpoint && typeof ai.endpoint === 'object' ? ai.endpoint : {};
|
|
183
|
+
view.enabled = ai.enabled === true;
|
|
184
|
+
view.engine = ai.engine === 'endpoint' ? 'endpoint' : 'opencode';
|
|
185
|
+
view.model = typeof ai.model === 'string' && ai.model ? ai.model : null;
|
|
186
|
+
view.endpoint = {
|
|
187
|
+
kind: endpoint.kind === 'openai' ? 'openai' : 'ollama', url: String(endpoint.url || ''), model: String(endpoint.model || ''),
|
|
188
|
+
shape: endpoint.shape === 'chat' ? 'chat' : 'fim',
|
|
189
|
+
};
|
|
190
|
+
view.hasKey = reading.hasEndpointKey === true;
|
|
191
|
+
view.keyState = tx(view.hasKey ? `${K}.key.set` : `${K}.key.notSet`);
|
|
192
|
+
view.keyPlaceholder = tx(view.hasKey ? `${K}.key.placeholderReplace` : `${K}.key.placeholder`);
|
|
193
|
+
view.urlPlaceholder = tx(view.endpoint.kind === 'openai' ? `${K}.url.placeholderOpenai` : `${K}.url.placeholderOllama`);
|
|
194
|
+
const list = Array.isArray(ai.exclude) ? ai.exclude.filter((glob) => typeof glob === 'string') : [];
|
|
195
|
+
view.exclude = excludeListText(list);
|
|
196
|
+
view.excludeCount = list.length ? tx(`${K}.exclude.count`, { n: list.length }) : tx(`${K}.exclude.empty`);
|
|
197
|
+
const able = view.engine === 'endpoint' ? !!(view.endpoint.url && view.endpoint.model) : !!view.model;
|
|
198
|
+
if (!view.enabled) view.pill = { text: tx(`${K}.pill.off`), state: 'off' };
|
|
199
|
+
else if (!able) view.pill = { text: tx(`${K}.pill.setup`), state: 'warn' };
|
|
200
|
+
else view.pill = { text: tx(`${K}.pill.on`), state: 'ok' };
|
|
201
|
+
return view;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/** A price per million tokens the way a model row writes it: 0.15, 0.60, 2.00, 0.003. */
|
|
205
|
+
export function formatPrice(value) {
|
|
206
|
+
const n = Number(value);
|
|
207
|
+
if (!Number.isFinite(n) || n < 0) return '';
|
|
208
|
+
return n > 0 && n < 0.01 ? n.toFixed(3) : n.toFixed(2);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/** A duration: 640 ms, 1.2 s. */
|
|
212
|
+
export function formatDuration(value) {
|
|
213
|
+
const ms = Math.max(0, Math.round(Number(value) || 0));
|
|
214
|
+
return ms < 1000 ? `${ms} ms` : `${(ms / 1000).toFixed(1)} s`;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/** A count: 950, 1.2k, 3.4M. */
|
|
218
|
+
export function formatAmount(value) {
|
|
219
|
+
const n = Math.max(0, Math.round(Number(value) || 0));
|
|
220
|
+
if (n < 1000) return String(n);
|
|
221
|
+
if (n < 1_000_000) return `${(n / 1000).toFixed(n < 10_000 ? 1 : 0).replace(/\.0$/, '')}k`;
|
|
222
|
+
return `${(n / 1_000_000).toFixed(1).replace(/\.0$/, '')}M`;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* The model list.
|
|
227
|
+
* @param {{ status?: number, body?: object|null } | null} answer what GET /api/editor/complete/models answered;
|
|
228
|
+
* null while it is being asked
|
|
229
|
+
* @param {string|null} selected `ai.model`
|
|
230
|
+
* @param {{ tx: Function, providersPage?: string }} deps `providersPage`: the name of the Settings page where a
|
|
231
|
+
* provider is signed in to
|
|
232
|
+
* @returns {{ state: 'loading'|'list'|'empty'|'unavailable', message: string, canStart: boolean,
|
|
233
|
+
* rows: { id: string, label: string, provider: string, meta: string, fast: boolean, selected: boolean, listed: boolean }[] }}
|
|
234
|
+
* `canStart`: the list could not be read because OpenCode is not running, and starting it would give one.
|
|
235
|
+
*/
|
|
236
|
+
export function describeEditorModels(answer, selected, { tx, providersPage = '' }) {
|
|
237
|
+
const view = { state: 'loading', message: tx(`${K}.models.loading`), canStart: false, rows: [] };
|
|
238
|
+
const missing = () => (selected ? [{ id: selected, label: selected, provider: selected.split('/')[0], meta: tx(`${K}.models.notListed`), fast: false, selected: true, listed: false }] : []);
|
|
239
|
+
if (!answer) return view;
|
|
240
|
+
const body = answer.body;
|
|
241
|
+
if (answer.status !== 200 || body?.ok !== true || !Array.isArray(body.models)) {
|
|
242
|
+
return { state: 'unavailable', message: clean(body?.message || body?.error) || tx(`${K}.models.unavailable`), canStart: false, rows: missing() };
|
|
243
|
+
}
|
|
244
|
+
if (body.available === false) {
|
|
245
|
+
return { state: 'unavailable', message: clean(body.message) || tx(`${K}.models.unavailable`), canStart: true, rows: missing() };
|
|
246
|
+
}
|
|
247
|
+
const rows = body.models.filter((model) => model && typeof model.id === 'string').map((model) => {
|
|
248
|
+
const parts = [];
|
|
249
|
+
if (model.fast) parts.push(tx(`${K}.models.fast`));
|
|
250
|
+
if (model.free) parts.push(tx(`${K}.models.free`));
|
|
251
|
+
else if (model.price) parts.push(tx(`${K}.models.price`, { input: formatPrice(model.price.input), output: formatPrice(model.price.output) }));
|
|
252
|
+
else parts.push(tx(`${K}.models.noPrice`));
|
|
253
|
+
return { id: model.id, label: String(model.label || model.id), provider: String(model.provider || model.id.split('/')[0]), meta: parts.join(' · '), fast: model.fast === true, selected: model.id === selected, listed: true };
|
|
254
|
+
});
|
|
255
|
+
if (selected && !rows.some((row) => row.selected)) rows.unshift(...missing());
|
|
256
|
+
if (!rows.length) return { state: 'empty', message: tx(`${K}.models.none`, { page: providersPage }), canStart: false, rows };
|
|
257
|
+
return { state: 'list', message: '', canStart: false, rows };
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
/**
|
|
261
|
+
* One test's result as a sentence.
|
|
262
|
+
* @param {{ ok?: boolean, ms?: number, tokens?: { input: number, output: number }|null, sample?: string, error?: string }|null} result
|
|
263
|
+
* a row of POST /api/editor/complete/test; null when the request itself got no answer
|
|
264
|
+
* @returns {{ tone: 'ok'|'warn'|'err', text: string }}
|
|
265
|
+
*/
|
|
266
|
+
export function describeEditorTest(result, { tx }) {
|
|
267
|
+
if (!result) return { tone: 'err', text: tx(`${K}.test.failed`, { reason: tx(`${K}.test.noAnswer`) }) };
|
|
268
|
+
if (!result.ok) return { tone: 'err', text: tx(`${K}.test.failed`, { reason: clean(result.error, 160) || '?' }) };
|
|
269
|
+
const time = formatDuration(result.ms);
|
|
270
|
+
const sample = clean(result.sample, 80);
|
|
271
|
+
if (!sample) return { tone: 'warn', text: tx(`${K}.test.empty`, { time }) };
|
|
272
|
+
if (result.tokens) return { tone: 'ok', text: tx(`${K}.test.ok`, { time, input: formatAmount(result.tokens.input), output: formatAmount(result.tokens.output), sample }) };
|
|
273
|
+
return { tone: 'ok', text: tx(`${K}.test.okNoTokens`, { time, sample }) };
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
/**
|
|
277
|
+
* What "Find fastest" found.
|
|
278
|
+
* @param {object|null} body the answer of POST /api/editor/complete/test { fastest: true }; null: no answer
|
|
279
|
+
* @returns {{ tone: 'ok'|'warn'|'err', text: string, model: string|null }} `model`: the one to select, or null
|
|
280
|
+
*/
|
|
281
|
+
export function describeEditorFastest(body, { tx }) {
|
|
282
|
+
if (!body || body.ok !== true || !Array.isArray(body.results)) {
|
|
283
|
+
return { tone: 'err', text: tx(`${K}.test.failed`, { reason: clean(body?.error || body?.message, 160) || tx(`${K}.test.noAnswer`) }), model: null };
|
|
284
|
+
}
|
|
285
|
+
const best = body.fastest ? body.results.find((row) => row.model === body.fastest && row.ok) : null;
|
|
286
|
+
if (best) return { tone: 'ok', text: tx(`${K}.fastest.found`, { model: best.model, time: formatDuration(best.ms) }), model: best.model };
|
|
287
|
+
if (!body.results.length) return { tone: 'warn', text: clean(body.message, 160) || tx(`${K}.fastest.noCandidates`), model: null };
|
|
288
|
+
return { tone: 'warn', text: tx(`${K}.fastest.none`), model: null };
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
/** Today's use as one line. `today`: the `today` of GET /api/editor/complete/usage, or null. */
|
|
292
|
+
export function describeEditorUsage(today, { tx }) {
|
|
293
|
+
const n = (value) => Math.max(0, Math.round(Number(value) || 0));
|
|
294
|
+
if (!today || n(today.requests) === 0) return tx(`${K}.usage.none`);
|
|
295
|
+
return tx(`${K}.usage.today`, { requests: formatAmount(today.requests), input: formatAmount(today.tokensIn), output: formatAmount(today.tokensOut), accepted: n(today.accepted), shown: n(today.shown) });
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
/**
|
|
299
|
+
* The patterns a text holds: one per line (or between commas), no empty ones, none twice, no folders.
|
|
300
|
+
* The server normalises the same way (editor-prefs.js); this is what the page sends.
|
|
301
|
+
*/
|
|
302
|
+
export function parseExcludeList(text) {
|
|
303
|
+
const out = [];
|
|
304
|
+
for (const piece of String(text ?? '').split(/[\n,]/)) {
|
|
305
|
+
const glob = piece.trim().slice(0, 200);
|
|
306
|
+
if (glob && !glob.includes('/') && !glob.includes('\\') && !out.includes(glob)) out.push(glob);
|
|
307
|
+
if (out.length >= 200) break;
|
|
308
|
+
}
|
|
309
|
+
return out;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
/** A list of patterns as the text field shows it. */
|
|
313
|
+
export const excludeListText = (list) => (Array.isArray(list) ? list.join('\n') : '');
|
|
314
|
+
|
|
315
|
+
/** Whether a text is an address a request can be sent to. Empty is not an address, and not an error either. */
|
|
316
|
+
export const isEndpointUrl = (text) => /^https?:\/\/\S+$/i.test(String(text ?? '').trim());
|
|
317
|
+
|
|
318
|
+
/**
|
|
319
|
+
* A change that could not be saved: the control goes back to where it was, and this is the sentence that says so.
|
|
320
|
+
* @param {{ status?: number, body?: object|null, error?: string|null }} input
|
|
321
|
+
*/
|
|
322
|
+
export function describeEditorSaveFailure({ status = 0, body = null, error = null } = {}, { tx }) {
|
|
323
|
+
let reason = clean(body?.error || body?.message, 160);
|
|
324
|
+
if (!reason && status === 404) reason = tx(`${K}.save.oldServer`);
|
|
325
|
+
if (!reason && status) reason = `HTTP ${status}`;
|
|
326
|
+
if (!reason) reason = clean(error, 160) || tx(`${K}.save.noAnswer`);
|
|
327
|
+
return tx(`${K}.save.failed`, { reason });
|
|
328
|
+
}
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
// ═══════════════════════════════════════════
|
|
2
|
+
// SETTINGS — the greeting, in words
|
|
3
|
+
// ═══════════════════════════════════════════
|
|
4
|
+
//
|
|
5
|
+
// What `GET /api/greeting` and `POST /api/greeting/preview` (lib/greeting/api.js, the engine in
|
|
6
|
+
// lib/greeting/engine.js) say about Settings → Automations → Greeting: whether the one switch can be
|
|
7
|
+
// used at all, what it is set to, what stands in the way, and what a new session would be greeted
|
|
8
|
+
// with right now. Pure: no DOM (it imports only the greeting's own DOM-free view), so node runs it
|
|
9
|
+
// (tests/settings-greeting-view.test.mjs).
|
|
10
|
+
//
|
|
11
|
+
// readGreetingStatus({ status, body, error }) what the server answered → a reading
|
|
12
|
+
// describeGreeting(reading, preview, { tx, t }) the section as ui-settings.js draws it
|
|
13
|
+
// describeGreetingSaveFailure(input, { tx }) the sentence for a switch that could not be saved
|
|
14
|
+
//
|
|
15
|
+
// `tx(key, params)` is the caller's translate function over the settings.redesign.* strings (the
|
|
16
|
+
// kit's `tx`); `t` is the app's translator, for the elapsed time the greeting card also formats.
|
|
17
|
+
//
|
|
18
|
+
// The section never shows a healthy-looking control for something that is not happening: a server
|
|
19
|
+
// without the engine gets a disabled switch and says why, and the preview says in so many words
|
|
20
|
+
// whether a session opened now would actually be greeted.
|
|
21
|
+
|
|
22
|
+
import { formatAway } from '../greeting/greeting-view.js';
|
|
23
|
+
|
|
24
|
+
const K = 'settings.redesign.hooks.greet';
|
|
25
|
+
|
|
26
|
+
const clean = (value, max = 200) => {
|
|
27
|
+
const text = String(value ?? '').replace(/\s+/g, ' ').trim();
|
|
28
|
+
return text.length > max ? `${text.slice(0, max - 1)}…` : text;
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* What the server answered.
|
|
33
|
+
*
|
|
34
|
+
* ready the engine is there: `payload` is { settings, enabled, killed, writer, spend, gapMs, last }
|
|
35
|
+
* old-server 404: the running server was started before the engine existed (public/ reloads at any
|
|
36
|
+
* time, lib/ only on a restart)
|
|
37
|
+
* failed 503 GREETING_UNAVAILABLE: the engine did not start; `reason` is the error
|
|
38
|
+
* unreadable anything else (no answer, a refusal, a body that is not the payload); `reason` says what
|
|
39
|
+
*
|
|
40
|
+
* @param {{ status?: number, body?: object|null, error?: string|null }} response
|
|
41
|
+
* @returns {{ state: 'ready'|'old-server'|'failed'|'unreadable', payload: object|null, reason: string|null }}
|
|
42
|
+
*/
|
|
43
|
+
export function readGreetingStatus({ status = 0, body = null, error = null } = {}) {
|
|
44
|
+
if (status === 200 && body?.ok === true && body.settings && typeof body.settings === 'object') return { state: 'ready', payload: body, reason: null };
|
|
45
|
+
if (status === 404) return { state: 'old-server', payload: null, reason: null };
|
|
46
|
+
if (status === 503 && body?.code === 'GREETING_UNAVAILABLE') return { state: 'failed', payload: null, reason: clean(body.reason) || null };
|
|
47
|
+
return { state: 'unreadable', payload: null, reason: clean(body?.error || error) || (status ? `HTTP ${status}` : null) };
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/** "$0.0006" · "<$0.0001" · '' */
|
|
51
|
+
function usd(value) {
|
|
52
|
+
const amount = Number(value);
|
|
53
|
+
if (!Number.isFinite(amount) || amount <= 0) return '';
|
|
54
|
+
if (amount < 0.0001) return '<$0.0001';
|
|
55
|
+
return `$${amount < 0.01 ? amount.toFixed(4) : amount.toFixed(2)}`;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* The section as it is drawn.
|
|
60
|
+
* `preview`: null (not asked yet) | { loading: true } | { failed } (the request itself failed) | the engine's
|
|
61
|
+
* preview payload (whose own `error` only says why no model wrote the sentence: 'not-prepared', 'no-writer' …).
|
|
62
|
+
* → { state, ready, on, disabled, note, notes[], preview: { shown, state, project, lead, greeting, empty, spend, canWrite } }
|
|
63
|
+
* preview.state: 'hidden' | 'loading' | 'greeting' | 'empty' | 'error'
|
|
64
|
+
* preview.greeting: what the shared card draws (public/shared/greeting/greeting-card.js), or null
|
|
65
|
+
*/
|
|
66
|
+
export function describeGreeting(reading, preview, { tx, t = null, now = Date.now() } = {}) {
|
|
67
|
+
const state = reading?.state || 'unreadable';
|
|
68
|
+
const ready = state === 'ready';
|
|
69
|
+
const payload = ready ? reading.payload : null;
|
|
70
|
+
const on = ready && payload.settings.enabled !== false;
|
|
71
|
+
const killed = ready && payload.killed === true;
|
|
72
|
+
let note = null;
|
|
73
|
+
if (state === 'old-server') note = { tone: 'info', text: tx(`${K}.state.oldServer`) };
|
|
74
|
+
else if (state === 'failed') note = { tone: 'warn', text: reading.reason ? tx(`${K}.state.failed`, { reason: reading.reason }) : tx(`${K}.state.failedUnknown`) };
|
|
75
|
+
else if (state === 'unreadable') note = { tone: 'warn', text: reading?.reason ? tx(`${K}.state.unreadable`, { reason: reading.reason }) : tx(`${K}.state.unreadableUnknown`) };
|
|
76
|
+
else if (killed) note = { tone: 'warn', text: tx(`${K}.state.killed`) };
|
|
77
|
+
|
|
78
|
+
const notes = [];
|
|
79
|
+
if (ready && on && !killed && payload.writer === false) notes.push({ tone: 'info', text: tx(`${K}.note.noWriter`) });
|
|
80
|
+
|
|
81
|
+
const view = { shown: ready, state: 'hidden', project: '', lead: '', greeting: null, empty: '', spend: '', canWrite: false };
|
|
82
|
+
if (ready) {
|
|
83
|
+
if (!preview || preview.loading) { view.state = 'loading'; view.lead = tx(`${K}.preview.loading`); }
|
|
84
|
+
else if (preview.failed || preview.reason === 'error' || preview.reason === 'unavailable') {
|
|
85
|
+
view.state = 'error';
|
|
86
|
+
view.project = clean(preview.label || preview.project || '', 60);
|
|
87
|
+
view.empty = tx(`${K}.preview.error`, { reason: clean(preview.failed || preview.reason || '', 120) || '?' });
|
|
88
|
+
} else {
|
|
89
|
+
const project = clean(preview.label || preview.project || '', 60);
|
|
90
|
+
view.project = project;
|
|
91
|
+
const text = typeof preview.text === 'string' ? preview.text.trim() : '';
|
|
92
|
+
const since = Date.parse(preview.lastHereAt ?? '');
|
|
93
|
+
const away = Number.isFinite(since) ? formatAway(Math.max(0, now - since), t) : '';
|
|
94
|
+
if (!text) {
|
|
95
|
+
view.state = 'empty';
|
|
96
|
+
view.empty = tx(preview.reason === 'first-session' || !Number.isFinite(since) ? `${K}.preview.firstSession` : `${K}.preview.nothingToSay`, { project });
|
|
97
|
+
} else {
|
|
98
|
+
view.state = 'greeting';
|
|
99
|
+
const reason = !on || killed ? 'off' : preview.reason;
|
|
100
|
+
if (reason === 'off') view.lead = tx(`${K}.preview.off`, { project });
|
|
101
|
+
else if (reason === 'recent') view.lead = tx(`${K}.preview.recent`, { project, time: away || tx(`${K}.preview.moment`), gap: formatAway(Number(preview.gapMs) || 3_600_000, t) });
|
|
102
|
+
else if (reason === 'nothing-new') view.lead = tx(`${K}.preview.nothingNew`, { project });
|
|
103
|
+
else view.lead = tx(`${K}.preview.now`, { project });
|
|
104
|
+
view.greeting = {
|
|
105
|
+
id: null, text, source: preview.source === 'writer' ? 'writer' : 'fallback', error: preview.error || null, model: preview.model || null,
|
|
106
|
+
tokens: preview.tokens || null, costUsd: preview.costUsd ?? null, preparedAt: preview.preparedAt || null,
|
|
107
|
+
basis: Array.isArray(preview.basis) ? preview.basis : [], awaySince: null,
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
const spend = preview.spend || payload.spend || null;
|
|
111
|
+
if (spend) {
|
|
112
|
+
const calls = Math.max(0, Math.round(Number(spend.calls) || 0));
|
|
113
|
+
view.spend = calls > 0
|
|
114
|
+
? tx(`${K}.preview.spend`, { calls, cap: Math.round(Number(spend.cap) || 0), cost: usd(spend.costUsd) || '$0' })
|
|
115
|
+
: tx(`${K}.preview.spendNone`);
|
|
116
|
+
}
|
|
117
|
+
view.canWrite = payload.writer !== false && view.state !== 'empty';
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
return { state, ready, on, disabled: !ready || killed, note, notes, preview: view };
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Why a switch could not be saved, as one sentence. `status` / `body`: what the server answered;
|
|
125
|
+
* `error`: the fetch failure when there was no answer.
|
|
126
|
+
*/
|
|
127
|
+
export function describeGreetingSaveFailure({ status = 0, body = null, error = null } = {}, { tx }) {
|
|
128
|
+
if (status === 404) return tx(`${K}.save.oldServer`);
|
|
129
|
+
if (status === 503) return tx(`${K}.save.failed`, { reason: clean(body?.reason || body?.error) || 'HTTP 503' });
|
|
130
|
+
if (status === 403) return tx(`${K}.save.forbidden`);
|
|
131
|
+
const reason = clean(body?.error || error) || (status ? `HTTP ${status}` : '');
|
|
132
|
+
return reason ? tx(`${K}.save.failed`, { reason }) : tx(`${K}.save.noAnswer`);
|
|
133
|
+
}
|
|
@@ -118,6 +118,14 @@ export const SETTINGS_PAGES = [
|
|
|
118
118
|
{ id: 'stg-sec-tool-permissions', key: 'tool_permissions', state: 'open' },
|
|
119
119
|
],
|
|
120
120
|
},
|
|
121
|
+
{
|
|
122
|
+
id: 'code-editor', key: 'code_editor', group: 'tools', icon: 'editor',
|
|
123
|
+
panes: [{ id: 'editor' }],
|
|
124
|
+
sections: [
|
|
125
|
+
{ id: 'stg-sec-editor-editing', key: 'editing', state: 'open' },
|
|
126
|
+
{ id: 'stg-sec-editor-ai', key: 'ai_autocomplete', state: 'open' },
|
|
127
|
+
],
|
|
128
|
+
},
|
|
121
129
|
{
|
|
122
130
|
id: 'automations', key: 'automations', group: 'automations', icon: 'hooks',
|
|
123
131
|
panes: [{ id: 'hooks' }, { id: 'social' }, { id: 'youtube', titleKey: k('pane.youtube') }],
|
|
@@ -243,8 +251,10 @@ export const SETTINGS_CONTROLS = {
|
|
|
243
251
|
'stg-sec-ml-server': 'ML012 ML013 ML014',
|
|
244
252
|
'stg-sec-skills': 'SKL004 SKL001',
|
|
245
253
|
'stg-sec-tool-permissions': 'PER012 PER001 PER002 PER003 PER004 PER005 PER006 PER007 PER008 PER009 PER010 PER011',
|
|
246
|
-
'stg-sec-
|
|
247
|
-
'stg-sec-
|
|
254
|
+
'stg-sec-editor-editing': 'EDT014 EDT015 EDT016 EDT017 EDT018 EDT019 EDT020 EDT021 EDT022 EDT023',
|
|
255
|
+
'stg-sec-editor-ai': 'EDT001 EDT002 EDT003 EDT004 EDT005 EDT006 EDT007 EDT008 EDT009 EDT010 EDT011 EDT012 EDT013',
|
|
256
|
+
'stg-sec-greetings': 'AUT058 AUT059 AUT060',
|
|
257
|
+
'stg-sec-automation-behavior': 'AUT053 AUT054 AUT055 AUT056 AUT057 AUT046 AUT036 AUT037 AUT038 AUT039 AUT040 AUT041 AUT028 AUT029 AUT030 AUT031 AUT032 AUT033 AUT034 AUT035',
|
|
248
258
|
'stg-sec-online-access': 'AUT048 AUT042 AUT043 AUT044 AUT045 AUT049 AUT050 AUT051 AUT052',
|
|
249
259
|
'stg-sec-social': 'SOC003 SOC001 SOC002',
|
|
250
260
|
'stg-sec-youtube': 'YT017 YT001 YT002 YT003 YT004 YT005 YT006 YT007 YT008 YT009 YT010 YT011 YT012 YT013 YT014 YT015 YT016 YT018',
|
|
@@ -326,9 +336,13 @@ export const SETTINGS_TAB_ALIASES = {
|
|
|
326
336
|
|
|
327
337
|
// Old `expand` / `highlight` / `scrollTo` ids whose element was merged into another
|
|
328
338
|
// section. Ids that still exist in the markup (setup-claude, jv-sec-log, wa-sec-setup,
|
|
329
|
-
// recall-controls,
|
|
330
|
-
//
|
|
339
|
+
// recall-controls, bridge-openclaw, project-storage-manager, cc-cli-paths …) need
|
|
340
|
+
// no entry: they resolve to themselves.
|
|
331
341
|
export const SETTINGS_SECTION_ALIASES = {
|
|
342
|
+
// The three greeting editors became the one Greeting section (lib/greeting/).
|
|
343
|
+
'cc-greeting-config': 'stg-sec-greetings',
|
|
344
|
+
'codex-greeting-config': 'stg-sec-greetings',
|
|
345
|
+
'opencode-greeting-config': 'stg-sec-greetings',
|
|
332
346
|
'notif-sources-section': 'stg-sec-when',
|
|
333
347
|
'notif-triggers-section': 'stg-sec-when',
|
|
334
348
|
'notif-sound-section': 'stg-sec-sounds',
|