synabun 2.0.0 → 2.0.7
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 +97 -0
- package/NOTICE +5 -0
- package/README.md +2 -1
- package/THIRD-PARTY-LICENSES.md +59 -0
- package/benchmarks/fixtures/browser/README.md +1 -1
- package/benchmarks/fixtures/desktop/README.md +1 -1
- package/docs/whatsapp.md +153 -18
- 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 +26 -3
- package/hooks/claude-code/stop.mjs +5 -5
- package/lib/child-path.js +303 -0
- package/lib/claude-hooks.js +2 -1
- package/lib/data-home-migration.js +2 -0
- package/lib/desktop-app.js +265 -0
- package/lib/external-tools.js +7 -3
- package/lib/log-redact.js +102 -0
- package/lib/packaged-runtime.js +138 -11
- package/lib/start-launcher.js +88 -1
- package/mcp-server/dist/http.d.ts +3 -1
- package/mcp-server/dist/http.d.ts.map +1 -1
- package/mcp-server/dist/http.js +15 -4
- package/mcp-server/dist/http.js.map +1 -1
- package/mcp-server/dist/index.d.ts.map +1 -1
- package/mcp-server/dist/index.js +9 -6
- package/mcp-server/dist/index.js.map +1 -1
- package/mcp-server/dist/services/budget-judgments.d.ts +108 -0
- package/mcp-server/dist/services/budget-judgments.d.ts.map +1 -0
- package/mcp-server/dist/services/budget-judgments.js +153 -0
- package/mcp-server/dist/services/budget-judgments.js.map +1 -0
- package/mcp-server/dist/services/codex-config-heal.d.ts +5 -1
- package/mcp-server/dist/services/codex-config-heal.d.ts.map +1 -1
- package/mcp-server/dist/services/codex-config-heal.js +41 -4
- package/mcp-server/dist/services/codex-config-heal.js.map +1 -1
- package/mcp-server/dist/services/identity.d.ts +11 -0
- package/mcp-server/dist/services/identity.d.ts.map +1 -1
- package/mcp-server/dist/services/identity.js +24 -0
- package/mcp-server/dist/services/identity.js.map +1 -1
- package/mcp-server/dist/services/neural-interface.d.ts +13 -0
- package/mcp-server/dist/services/neural-interface.d.ts.map +1 -1
- package/mcp-server/dist/services/neural-interface.js +34 -2
- package/mcp-server/dist/services/neural-interface.js.map +1 -1
- package/mcp-server/dist/services/typesafe-config.d.ts +1 -1
- package/mcp-server/dist/services/typesafe-config.d.ts.map +1 -1
- package/mcp-server/dist/services/typesafe-config.js +4 -1
- package/mcp-server/dist/services/typesafe-config.js.map +1 -1
- package/mcp-server/dist/tools/agents.d.ts +10 -7
- package/mcp-server/dist/tools/agents.d.ts.map +1 -1
- package/mcp-server/dist/tools/agents.js +34 -13
- package/mcp-server/dist/tools/agents.js.map +1 -1
- package/mcp-server/dist/tools/whatsapp-send-file.d.ts +17 -0
- package/mcp-server/dist/tools/whatsapp-send-file.d.ts.map +1 -0
- package/mcp-server/dist/tools/whatsapp-send-file.js +44 -0
- package/mcp-server/dist/tools/whatsapp-send-file.js.map +1 -0
- package/mcp-server/dist/tui.js +2 -1
- package/mcp-server/dist/tui.js.map +1 -1
- package/mcp-server/dist/version.d.ts +5 -0
- package/mcp-server/dist/version.d.ts.map +1 -0
- package/mcp-server/dist/version.js +21 -0
- package/mcp-server/dist/version.js.map +1 -0
- package/mcp-server/package-lock.json +2 -2
- package/mcp-server/package.json +1 -3
- package/mcp-server/run.mjs +15 -9
- package/mcp-server/src/http.ts +18 -3
- package/mcp-server/src/index.ts +9 -6
- package/mcp-server/src/services/budget-judgments.ts +183 -0
- package/mcp-server/src/services/codex-config-heal.ts +35 -4
- package/mcp-server/src/services/identity.ts +27 -0
- package/mcp-server/src/services/neural-interface.ts +34 -2
- package/mcp-server/src/services/typesafe-config.ts +4 -1
- package/mcp-server/src/tools/agents.ts +33 -14
- package/mcp-server/src/tools/whatsapp-send-file.ts +51 -0
- package/mcp-server/src/tui.ts +2 -1
- package/mcp-server/src/version.ts +22 -0
- package/neural-interface/i18n/en.json +2126 -209
- package/neural-interface/i18n/pt-BR.json +2123 -207
- package/neural-interface/lib/account-usage.js +163 -0
- package/neural-interface/lib/assistant-api.js +316 -57
- package/neural-interface/lib/assistant-brains/claude.js +19 -2
- package/neural-interface/lib/assistant-brains/codex.js +3 -0
- package/neural-interface/lib/assistant-brains/opencode.js +17 -4
- package/neural-interface/lib/assistant-budget-planner.js +390 -0
- package/neural-interface/lib/assistant-budget-split.js +408 -0
- package/neural-interface/lib/assistant-budget.js +94 -95
- package/neural-interface/lib/assistant-catalog.js +179 -27
- package/neural-interface/lib/assistant-config.js +91 -14
- package/neural-interface/lib/assistant-dispatch.js +494 -138
- package/neural-interface/lib/assistant-hidden-models.js +393 -66
- package/neural-interface/lib/assistant-jev.js +26 -3
- package/neural-interface/lib/assistant-memory.js +24 -10
- package/neural-interface/lib/assistant-persona.js +73 -12
- package/neural-interface/lib/assistant-plan-permissions.js +8 -1
- package/neural-interface/lib/assistant-pricing.js +21 -6
- package/neural-interface/lib/assistant-route-accounts.js +14 -0
- package/neural-interface/lib/assistant-route-gate.js +19 -3
- package/neural-interface/lib/assistant-router.js +373 -42
- package/neural-interface/lib/assistant-runtime.js +488 -59
- 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 +183 -0
- package/neural-interface/lib/augmented-path.js +317 -53
- package/neural-interface/lib/browser-background-tab.js +134 -0
- package/neural-interface/lib/browser-screenshot.js +678 -61
- package/neural-interface/lib/claude-accounts.js +342 -31
- package/neural-interface/lib/claude-agent-bridge.js +123 -9
- package/neural-interface/lib/claude-executable.js +6 -1
- package/neural-interface/lib/claude-model-catalog.js +33 -5
- package/neural-interface/lib/claude-panel-auth.js +221 -0
- package/neural-interface/lib/claude-profile-api-key.js +62 -0
- package/neural-interface/lib/claude-temporary.js +46 -7
- package/neural-interface/lib/cli-install-api.js +137 -0
- package/neural-interface/lib/cli-install.js +797 -0
- package/neural-interface/lib/cli-path.js +130 -0
- package/neural-interface/lib/cli-resolver.js +744 -0
- package/neural-interface/lib/cli-update-planner.js +143 -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/codex-account-auth.js +178 -0
- package/neural-interface/lib/codex-accounts.js +489 -0
- package/neural-interface/lib/codex-capabilities.js +2 -1
- package/neural-interface/lib/codex-compat-rpc.js +8 -2
- package/neural-interface/lib/codex-mcp-approvals.js +5 -1
- package/neural-interface/lib/codex-panel-auth.js +62 -0
- package/neural-interface/lib/codex-session-settings.js +1 -1
- package/neural-interface/lib/codex-settings-schema.js +120 -0
- package/neural-interface/lib/desktop/config.js +3 -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-bash/bzip2.js +339 -0
- package/neural-interface/lib/git-bash/claude-settings.js +73 -0
- package/neural-interface/lib/git-bash/extract-worker.js +34 -0
- package/neural-interface/lib/git-bash/install.js +529 -0
- package/neural-interface/lib/git-bash/licenses/COPYING +360 -0
- package/neural-interface/lib/git-bash/licenses/LGPL-2.1 +511 -0
- package/neural-interface/lib/git-bash/manager.js +268 -0
- package/neural-interface/lib/git-bash/pins.js +206 -0
- package/neural-interface/lib/git-bash/pins.json +34 -0
- package/neural-interface/lib/git-bash/resolver.js +194 -0
- package/neural-interface/lib/git-diff-api.js +103 -0
- package/neural-interface/lib/git-read.js +234 -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/install-details.js +145 -0
- package/neural-interface/lib/machine-names.js +88 -0
- package/neural-interface/lib/mcp-installer.js +14 -2
- package/neural-interface/lib/memory-api.js +31 -7
- package/neural-interface/lib/native-loop-providers.js +26 -8
- package/neural-interface/lib/native-loop-runtime.js +9 -4
- package/neural-interface/lib/os-path.js +319 -0
- package/neural-interface/lib/plugin-installer.js +4 -2
- package/neural-interface/lib/project-storage.js +5 -7
- package/neural-interface/lib/provider-panel-api-key-auth.js +92 -0
- package/neural-interface/lib/provider-panel-terminal-auth.js +95 -0
- package/neural-interface/lib/pty-host/core.js +2 -1
- package/neural-interface/lib/pty-host/manager.js +27 -2
- package/neural-interface/lib/remote-policy.js +20 -11
- package/neural-interface/lib/request-gate.js +589 -0
- package/neural-interface/lib/session-title-generator.js +4 -3
- package/neural-interface/lib/system-about.js +340 -0
- package/neural-interface/lib/system-activity.js +66 -0
- package/neural-interface/lib/terminal-env.js +11 -3
- package/neural-interface/lib/terminal-path.js +55 -0
- package/neural-interface/lib/ui-blob-api.js +108 -0
- package/neural-interface/lib/ui-blob-store.js +68 -0
- package/neural-interface/lib/virtual-socket.js +12 -1
- package/neural-interface/lib/whatsapp/baileys-adapter.js +165 -45
- package/neural-interface/lib/whatsapp/bridge.js +78 -34
- package/neural-interface/lib/whatsapp/commands.js +13 -14
- package/neural-interface/lib/whatsapp/config.js +134 -20
- package/neural-interface/lib/whatsapp/fake-baileys.js +99 -14
- package/neural-interface/lib/whatsapp/host-core.js +168 -35
- package/neural-interface/lib/whatsapp/inbound.js +5 -3
- package/neural-interface/lib/whatsapp/local-file.js +524 -0
- package/neural-interface/lib/whatsapp/manager.js +92 -32
- package/neural-interface/lib/whatsapp/paths.js +11 -1
- package/neural-interface/lib/whatsapp/protocol.js +65 -5
- package/neural-interface/lib/whatsapp/redact.js +63 -0
- package/neural-interface/lib/whatsapp/service.js +343 -91
- package/neural-interface/lib/whatsapp/transcription.js +463 -0
- package/neural-interface/package-lock.json +701 -2
- package/neural-interface/package.json +13 -2
- package/neural-interface/public/index.html +2 -0
- package/neural-interface/public/index2d.html +2 -0
- package/neural-interface/public/install-app.html +9 -6
- package/neural-interface/public/offline.html +19 -3
- package/neural-interface/public/onboarding.html +107 -2
- package/neural-interface/public/shared/account-i18n.js +7 -0
- package/neural-interface/public/shared/account-menu.js +539 -0
- package/neural-interface/public/shared/account-usage.js +19 -0
- package/neural-interface/public/shared/account-view.js +335 -0
- package/neural-interface/public/shared/agent-runtime-options.js +50 -14
- package/neural-interface/public/shared/assistant/asst-accounts.js +182 -184
- package/neural-interface/public/shared/assistant/asst-api.js +32 -2
- package/neural-interface/public/shared/assistant/asst-automation.js +286 -0
- package/neural-interface/public/shared/assistant/asst-brain-picker.js +430 -81
- package/neural-interface/public/shared/assistant/asst-budget.js +259 -303
- package/neural-interface/public/shared/assistant/asst-composer.js +11 -1
- package/neural-interface/public/shared/assistant/asst-mascot.js +140 -61
- package/neural-interface/public/shared/assistant/asst-menu.js +27 -11
- package/neural-interface/public/shared/assistant/asst-panel.js +198 -42
- package/neural-interface/public/shared/assistant/asst-render.js +197 -23
- package/neural-interface/public/shared/assistant/asst-route-models.js +108 -0
- package/neural-interface/public/shared/assistant/asst-route.js +95 -343
- package/neural-interface/public/shared/assistant/asst-routes-editor.js +534 -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-slash.js +1 -1
- package/neural-interface/public/shared/assistant/asst-state.js +186 -2
- package/neural-interface/public/shared/assistant/asst-styles.js +247 -11
- package/neural-interface/public/shared/blob-store.js +111 -22
- package/neural-interface/public/shared/cdx/cdx-commands.js +4 -1
- package/neural-interface/public/shared/cdx/cdx-features.js +22 -14
- package/neural-interface/public/shared/cdx/cdx-icons.js +2 -0
- package/neural-interface/public/shared/cdx/cdx-panel.js +80 -118
- package/neural-interface/public/shared/cdx/cdx-render.js +25 -89
- package/neural-interface/public/shared/cdx/cdx-settings.js +231 -0
- package/neural-interface/public/shared/cdx/cdx-styles.js +164 -107
- package/neural-interface/public/shared/cdx/cdx-tabs.js +348 -668
- package/neural-interface/public/shared/cdx/cdx-ws.js +2 -0
- package/neural-interface/public/shared/claude-account-view.js +3 -0
- package/neural-interface/public/shared/cli-install-view.js +392 -0
- package/neural-interface/public/shared/cli-status.js +282 -22
- package/neural-interface/public/shared/constants.js +6 -1
- package/neural-interface/public/shared/cp/cp-events.js +2 -1
- package/neural-interface/public/shared/cp/cp-rehydrate.js +2 -1
- package/neural-interface/public/shared/cp/cp-styles.js +61 -0
- package/neural-interface/public/shared/desktop-bridge.js +39 -0
- package/neural-interface/public/shared/desktop-titlebar-page.js +59 -0
- package/neural-interface/public/shared/desktop-titlebar-view.js +122 -0
- package/neural-interface/public/shared/desktop-titlebar.js +153 -0
- package/neural-interface/public/shared/desktop-update-view.js +462 -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/git-bash-view.js +145 -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 +161 -165
- package/neural-interface/public/shared/i18n.js +19 -1
- package/neural-interface/public/shared/install-details-view.js +86 -0
- 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 +131 -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 +22 -0
- package/neural-interface/public/shared/ocp-v2/ocp-v2-styles.js +158 -0
- package/neural-interface/public/shared/ocp-v2/ocp-v2-ws.js +3 -2
- package/neural-interface/public/shared/pwa-install-page.js +32 -2
- package/neural-interface/public/shared/pwa-install.css +3 -3
- package/neural-interface/public/shared/pwa-install.js +100 -1
- package/neural-interface/public/shared/settings/settings-about-pane.js +271 -0
- package/neural-interface/public/shared/settings/settings-about.js +254 -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 +33 -4
- package/neural-interface/public/shared/settings/settings-user-context.js +154 -0
- package/neural-interface/public/shared/showcase-privacy.js +489 -0
- package/neural-interface/public/shared/start-bridge.js +238 -19
- package/neural-interface/public/shared/storage.js +30 -3
- package/neural-interface/public/shared/styles.css +1710 -770
- package/neural-interface/public/shared/synabun-mascot.js +68 -58
- package/neural-interface/public/shared/tunnel-view.js +167 -0
- package/neural-interface/public/shared/ui-claude-panel.js +369 -229
- package/neural-interface/public/shared/ui-cli-banner.js +355 -0
- 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-git-bash.js +124 -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 +10 -1
- package/neural-interface/public/shared/ui-keybinds.js +4 -0
- package/neural-interface/public/shared/ui-loading.js +187 -19
- package/neural-interface/public/shared/ui-menubar.js +25 -33
- 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 +48 -22
- package/neural-interface/public/shared/ui-provider-auth.js +104 -0
- package/neural-interface/public/shared/ui-provider-terminal-auth.js +223 -0
- package/neural-interface/public/shared/ui-sessions.js +9 -3
- package/neural-interface/public/shared/ui-settings.js +509 -490
- package/neural-interface/public/shared/ui-sidepanel-runtime.js +2 -1
- package/neural-interface/public/shared/ui-sidepanel-sessions.js +3 -1
- package/neural-interface/public/shared/ui-sync.js +22 -0
- package/neural-interface/public/shared/ui-terminal.js +40 -5
- package/neural-interface/public/shared/ui-tool-updates.js +5 -0
- 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-update.js +594 -5
- package/neural-interface/public/shared/ui-whatsapp.js +21 -6
- 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/whatsapp/wa-view.js +51 -8
- package/neural-interface/public/shared/worker-end.js +253 -0
- package/neural-interface/public/sw.js +2 -2
- package/neural-interface/public/variant/2d/main.js +10 -1
- package/neural-interface/public/variant/3d/main.js +10 -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/vendor-codemirror-entry.js +121 -0
- package/neural-interface/server.js +2085 -1629
- package/neural-interface/templates/rulesets/README.md +1 -1
- package/package.json +5 -8
- package/setup.js +53 -15
- package/benchmarks/browser-live-results.json +0 -79
- package/benchmarks/browser-v2-results.json +0 -258
- package/neural-interface/lib/codex-capabilities.d.ts +0 -41
- package/neural-interface/public/category-logos/claude.svg +0 -1
- package/neural-interface/public/category-logos/gemini-color.svg +0 -1
- package/neural-interface/public/category-logos/openai.svg +0 -1
- package/neural-interface/public/category-logos/synabun.png +0 -0
- package/neural-interface/public/claude.svg +0 -1
- package/neural-interface/public/gallery.svg +0 -6
- package/neural-interface/public/leonardoai.svg +0 -1
- package/neural-interface/public/mcp.svg +0 -4
- package/neural-interface/public/opencode-logo-dark-square.svg +0 -1
- package/neural-interface/public/pin-svgrepo-com.svg +0 -25
- package/neural-interface/public/sessionicon.svg +0 -8
- package/neural-interface/public/toolbar-album.svg +0 -8
- package/neural-interface/public/toolbar-question.svg +0 -6
- package/neural-interface/public/toolbar-sessions.svg +0 -6
- package/neural-interface/scripts/codex-protocol-fixture.mjs +0 -83
- package/skills/synabun/synabunicon.png +0 -0
|
@@ -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
|
+
}
|
|
@@ -29,6 +29,7 @@ export const SETTINGS_GROUPS = [
|
|
|
29
29
|
{ id: 'tools', labelKey: k('group.tools') },
|
|
30
30
|
{ id: 'automations', labelKey: k('group.automations') },
|
|
31
31
|
{ id: 'personalize', labelKey: k('group.personalize') },
|
|
32
|
+
{ id: 'app', labelKey: k('group.app') },
|
|
32
33
|
];
|
|
33
34
|
|
|
34
35
|
/** `key` is the i18n slug under settings.redesign.{nav,page,section}; `icon` names a TAB_ICONS entry. */
|
|
@@ -118,6 +119,14 @@ export const SETTINGS_PAGES = [
|
|
|
118
119
|
{ id: 'stg-sec-tool-permissions', key: 'tool_permissions', state: 'open' },
|
|
119
120
|
],
|
|
120
121
|
},
|
|
122
|
+
{
|
|
123
|
+
id: 'code-editor', key: 'code_editor', group: 'tools', icon: 'editor',
|
|
124
|
+
panes: [{ id: 'editor' }],
|
|
125
|
+
sections: [
|
|
126
|
+
{ id: 'stg-sec-editor-editing', key: 'editing', state: 'open' },
|
|
127
|
+
{ id: 'stg-sec-editor-ai', key: 'ai_autocomplete', state: 'open' },
|
|
128
|
+
],
|
|
129
|
+
},
|
|
121
130
|
{
|
|
122
131
|
id: 'automations', key: 'automations', group: 'automations', icon: 'hooks',
|
|
123
132
|
panes: [{ id: 'hooks' }, { id: 'social' }, { id: 'youtube', titleKey: k('pane.youtube') }],
|
|
@@ -175,6 +184,17 @@ export const SETTINGS_PAGES = [
|
|
|
175
184
|
{ id: 'stg-sec-2d-graph', key: 'd2_graph', state: 'advanced', variant: 'graphics' },
|
|
176
185
|
],
|
|
177
186
|
},
|
|
187
|
+
{
|
|
188
|
+
// The last page: version, links, system and dependencies (settings-about-pane.js, GET /api/system/about).
|
|
189
|
+
// Its header (icon, version, Check for updates, Copy info) is not a section: it is always shown.
|
|
190
|
+
id: 'about', key: 'about', group: 'app', icon: 'about',
|
|
191
|
+
panes: [{ id: 'about' }],
|
|
192
|
+
sections: [
|
|
193
|
+
{ id: 'stg-sec-about-links', key: 'links', state: 'open' },
|
|
194
|
+
{ id: 'stg-sec-about-system', key: 'system', state: 'open' },
|
|
195
|
+
{ id: 'stg-sec-about-dependencies', key: 'dependencies', state: 'open' },
|
|
196
|
+
],
|
|
197
|
+
},
|
|
178
198
|
];
|
|
179
199
|
|
|
180
200
|
export const DEFAULT_SETTINGS_PAGE = 'ai-connections';
|
|
@@ -243,8 +263,10 @@ export const SETTINGS_CONTROLS = {
|
|
|
243
263
|
'stg-sec-ml-server': 'ML012 ML013 ML014',
|
|
244
264
|
'stg-sec-skills': 'SKL004 SKL001',
|
|
245
265
|
'stg-sec-tool-permissions': 'PER012 PER001 PER002 PER003 PER004 PER005 PER006 PER007 PER008 PER009 PER010 PER011',
|
|
246
|
-
'stg-sec-
|
|
247
|
-
'stg-sec-
|
|
266
|
+
'stg-sec-editor-editing': 'EDT014 EDT015 EDT016 EDT017 EDT018 EDT019 EDT020 EDT021 EDT022 EDT023',
|
|
267
|
+
'stg-sec-editor-ai': 'EDT001 EDT002 EDT003 EDT004 EDT005 EDT006 EDT007 EDT008 EDT009 EDT010 EDT011 EDT012 EDT013',
|
|
268
|
+
'stg-sec-greetings': 'AUT058 AUT059 AUT060',
|
|
269
|
+
'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
270
|
'stg-sec-online-access': 'AUT048 AUT042 AUT043 AUT044 AUT045 AUT049 AUT050 AUT051 AUT052',
|
|
249
271
|
'stg-sec-social': 'SOC003 SOC001 SOC002',
|
|
250
272
|
'stg-sec-youtube': 'YT017 YT001 YT002 YT003 YT004 YT005 YT006 YT007 YT008 YT009 YT010 YT011 YT012 YT013 YT014 YT015 YT016 YT018',
|
|
@@ -268,11 +290,13 @@ export const SETTINGS_CONTROLS = {
|
|
|
268
290
|
'stg-sec-visual-tuning': 'UI003 UI004 UI005 UI006 UI007 UI008 UI009 UI010',
|
|
269
291
|
'stg-sec-file-icons': 'ICO001 ICO003 ICO002 ICO004 ICO005',
|
|
270
292
|
'stg-sec-2d-graph': 'GFX001 GFX002 GFX003 GFX004 GFX005 GFX006 GFX007 GFX008',
|
|
293
|
+
'stg-sec-about-dependencies': 'ABT003',
|
|
271
294
|
};
|
|
272
295
|
/** Controls of a page that sit outside its sections (a pane's save bar, the tabs of a pane). */
|
|
273
296
|
export const SETTINGS_PAGE_CONTROLS = {
|
|
274
297
|
browser: 'BRW081 BRW082 BRW085',
|
|
275
298
|
'connected-tools': 'MCP001',
|
|
299
|
+
about: 'ABT001 ABT002',
|
|
276
300
|
};
|
|
277
301
|
/** The window's own controls (dim the background, close, the page list): not settings, so never search results. */
|
|
278
302
|
export const SETTINGS_SHELL_CONTROLS = 'SH002 SH003 SH005';
|
|
@@ -296,6 +320,7 @@ export const homeOfControl = (controlId) => CONTROL_HOME.get(controlId) || null;
|
|
|
296
320
|
// ── Deep-link aliases ──────────────────────────────────────────────────────
|
|
297
321
|
// Old tab id → the page that took it over, and the section a link to that tab
|
|
298
322
|
// should land on. Old callers keep working; new code uses the page ids.
|
|
323
|
+
// A page that never had an old tab needs no entry: `{ tab: 'about' }` names the About page itself.
|
|
299
324
|
|
|
300
325
|
export const SETTINGS_TAB_ALIASES = {
|
|
301
326
|
server: { page: 'memory-backups', section: 'stg-sec-database' },
|
|
@@ -326,9 +351,13 @@ export const SETTINGS_TAB_ALIASES = {
|
|
|
326
351
|
|
|
327
352
|
// Old `expand` / `highlight` / `scrollTo` ids whose element was merged into another
|
|
328
353
|
// section. Ids that still exist in the markup (setup-claude, jv-sec-log, wa-sec-setup,
|
|
329
|
-
// recall-controls,
|
|
330
|
-
//
|
|
354
|
+
// recall-controls, bridge-openclaw, project-storage-manager, cc-cli-paths …) need
|
|
355
|
+
// no entry: they resolve to themselves.
|
|
331
356
|
export const SETTINGS_SECTION_ALIASES = {
|
|
357
|
+
// The three greeting editors became the one Greeting section (lib/greeting/).
|
|
358
|
+
'cc-greeting-config': 'stg-sec-greetings',
|
|
359
|
+
'codex-greeting-config': 'stg-sec-greetings',
|
|
360
|
+
'opencode-greeting-config': 'stg-sec-greetings',
|
|
332
361
|
'notif-sources-section': 'stg-sec-when',
|
|
333
362
|
'notif-triggers-section': 'stg-sec-when',
|
|
334
363
|
'notif-sound-section': 'stg-sec-sounds',
|