synabun 2.0.9 → 2.1.0
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 +115 -0
- package/README.md +25 -2
- package/THIRD-PARTY-LICENSES.md +68 -4
- package/bin/synabun.js +71 -24
- package/docs/whatsapp.md +2 -2
- package/hooks/claude-code/pre-websearch.mjs +12 -1
- package/hooks/claude-code/shared.mjs +5 -0
- package/hooks/claude-code/stop.mjs +1 -1
- package/launcher.mjs +9 -3
- package/lib/claude-hooks.js +172 -18
- package/lib/client-config-repair.js +17 -8
- package/lib/desktop-app.js +7 -1
- package/lib/external-tools.js +1 -1
- package/lib/first-launch-protection.js +57 -12
- package/lib/mcp-dist.js +176 -0
- package/lib/release-npm-ci.mjs +138 -0
- package/lib/release-publish.mjs +91 -0
- package/lib/release-scan.mjs +50 -0
- package/lib/server-crash-record.js +123 -0
- package/lib/server-exit.js +318 -0
- package/lib/server-supervisor.js +508 -0
- package/lib/start-launcher.js +43 -5
- package/lib/tools-cli.js +45 -0
- package/lib/update-snapshot.js +228 -0
- package/lib/virustotal.mjs +192 -0
- package/lib/windows-crash-event.js +269 -0
- package/mcp-server/dist/http.d.ts +20 -1
- package/mcp-server/dist/http.d.ts.map +1 -1
- package/mcp-server/dist/http.js +59 -12
- package/mcp-server/dist/http.js.map +1 -1
- package/mcp-server/dist/index.d.ts +33 -27
- package/mcp-server/dist/index.d.ts.map +1 -1
- package/mcp-server/dist/index.js +128 -143
- package/mcp-server/dist/index.js.map +1 -1
- package/mcp-server/dist/preload.js +1 -1
- package/mcp-server/dist/preload.js.map +1 -1
- package/mcp-server/dist/services/codex-config-heal.d.ts +6 -2
- package/mcp-server/dist/services/codex-config-heal.d.ts.map +1 -1
- package/mcp-server/dist/services/codex-config-heal.js +81 -28
- package/mcp-server/dist/services/codex-config-heal.js.map +1 -1
- package/mcp-server/dist/services/memory-judge-backfill.d.ts +6 -0
- package/mcp-server/dist/services/memory-judge-backfill.d.ts.map +1 -1
- package/mcp-server/dist/services/memory-judge-backfill.js +7 -1
- package/mcp-server/dist/services/memory-judge-backfill.js.map +1 -1
- package/mcp-server/dist/services/neural-interface.d.ts +16 -10
- package/mcp-server/dist/services/neural-interface.d.ts.map +1 -1
- package/mcp-server/dist/services/neural-interface.js +67 -11
- package/mcp-server/dist/services/neural-interface.js.map +1 -1
- package/mcp-server/dist/services/server-instructions.d.ts +7 -2
- package/mcp-server/dist/services/server-instructions.d.ts.map +1 -1
- package/mcp-server/dist/services/server-instructions.js +20 -62
- package/mcp-server/dist/services/server-instructions.js.map +1 -1
- package/mcp-server/dist/services/tool-catalog.d.ts +120 -0
- package/mcp-server/dist/services/tool-catalog.d.ts.map +1 -0
- package/mcp-server/dist/services/tool-catalog.js +339 -0
- package/mcp-server/dist/services/tool-catalog.js.map +1 -0
- package/mcp-server/dist/services/tool-list-refresh-barrier.d.ts +12 -0
- package/mcp-server/dist/services/tool-list-refresh-barrier.d.ts.map +1 -0
- package/mcp-server/dist/services/tool-list-refresh-barrier.js +40 -0
- package/mcp-server/dist/services/tool-list-refresh-barrier.js.map +1 -0
- package/mcp-server/dist/services/tool-list.d.ts +108 -0
- package/mcp-server/dist/services/tool-list.d.ts.map +1 -0
- package/mcp-server/dist/services/tool-list.js +334 -0
- package/mcp-server/dist/services/tool-list.js.map +1 -0
- package/mcp-server/dist/services/tool-runtime.d.ts +161 -0
- package/mcp-server/dist/services/tool-runtime.d.ts.map +1 -0
- package/mcp-server/dist/services/tool-runtime.js +400 -0
- package/mcp-server/dist/services/tool-runtime.js.map +1 -0
- package/mcp-server/dist/services/whiteboard-geometry.d.ts +26 -2
- package/mcp-server/dist/services/whiteboard-geometry.d.ts.map +1 -1
- package/mcp-server/dist/services/whiteboard-geometry.js +91 -8
- package/mcp-server/dist/services/whiteboard-geometry.js.map +1 -1
- package/mcp-server/dist/tools/agents.d.ts +10 -10
- package/mcp-server/dist/tools/agents.d.ts.map +1 -1
- package/mcp-server/dist/tools/agents.js +22 -13
- package/mcp-server/dist/tools/agents.js.map +1 -1
- package/mcp-server/dist/tools/browser-batch.d.ts +3 -3
- package/mcp-server/dist/tools/image-tools.d.ts +1 -1
- package/mcp-server/dist/tools/image-tools.js +1 -1
- package/mcp-server/dist/tools/skill-tools.d.ts +505 -0
- package/mcp-server/dist/tools/skill-tools.d.ts.map +1 -0
- package/mcp-server/dist/tools/skill-tools.js +639 -0
- package/mcp-server/dist/tools/skill-tools.js.map +1 -0
- package/mcp-server/dist/tools/skill.d.ts +7 -0
- package/mcp-server/dist/tools/skill.d.ts.map +1 -0
- package/mcp-server/dist/tools/skill.js +11 -0
- package/mcp-server/dist/tools/skill.js.map +1 -0
- package/mcp-server/dist/tools/tool-groups.d.ts +68 -0
- package/mcp-server/dist/tools/tool-groups.d.ts.map +1 -0
- package/mcp-server/dist/tools/tool-groups.js +147 -0
- package/mcp-server/dist/tools/tool-groups.js.map +1 -0
- package/mcp-server/dist/tools/whiteboard-format.d.ts.map +1 -1
- package/mcp-server/dist/tools/whiteboard-format.js +7 -4
- package/mcp-server/dist/tools/whiteboard-format.js.map +1 -1
- package/mcp-server/dist/tools/whiteboard-tools.d.ts +64 -13
- package/mcp-server/dist/tools/whiteboard-tools.d.ts.map +1 -1
- package/mcp-server/dist/tools/whiteboard-tools.js +30 -5
- package/mcp-server/dist/tools/whiteboard-tools.js.map +1 -1
- package/mcp-server/package-lock.json +2 -2
- package/mcp-server/package.json +1 -1
- package/mcp-server/src/http.ts +75 -12
- package/mcp-server/src/index.ts +147 -154
- package/mcp-server/src/preload.ts +12 -12
- package/mcp-server/src/services/codex-config-heal.ts +80 -29
- package/mcp-server/src/services/memory-judge-backfill.ts +7 -1
- package/mcp-server/src/services/neural-interface.ts +79 -20
- package/mcp-server/src/services/server-instructions.ts +27 -63
- package/mcp-server/src/services/tool-catalog.ts +403 -0
- package/mcp-server/src/services/tool-list-refresh-barrier.ts +33 -0
- package/mcp-server/src/services/tool-list.ts +410 -0
- package/mcp-server/src/services/tool-runtime.ts +457 -0
- package/mcp-server/src/services/whiteboard-geometry.ts +94 -9
- package/mcp-server/src/tools/agents.ts +22 -14
- package/mcp-server/src/tools/image-tools.ts +1 -1
- package/mcp-server/src/tools/skill-tools.ts +589 -0
- package/mcp-server/src/tools/skill.ts +13 -0
- package/mcp-server/src/tools/tool-groups.ts +164 -0
- package/mcp-server/src/tools/whiteboard-format.ts +9 -3
- package/mcp-server/src/tools/whiteboard-tools.ts +39 -5
- package/neural-interface/i18n/en.json +3680 -206
- package/neural-interface/i18n/pt-BR.json +3628 -157
- package/neural-interface/lib/account-usage.js +5 -3
- package/neural-interface/lib/app-controls-api.js +161 -0
- package/neural-interface/lib/assistant-api.js +148 -35
- package/neural-interface/lib/assistant-brains/claude.js +4 -3
- package/neural-interface/lib/assistant-brains/codex-browser-policy-hook.mjs +12 -2
- package/neural-interface/lib/assistant-brains/codex-route-gate-hook.mjs +32 -9
- package/neural-interface/lib/assistant-brains/codex.js +9 -5
- package/neural-interface/lib/assistant-brains/opencode.js +122 -44
- package/neural-interface/lib/assistant-brains/opencode2-browser-policy/index.js +59 -0
- package/neural-interface/lib/assistant-brains/opencode2-route-gate/index.js +97 -0
- package/neural-interface/lib/assistant-catalog.js +23 -9
- package/neural-interface/lib/assistant-dispatch.js +874 -156
- package/neural-interface/lib/assistant-envelope.js +22 -2
- package/neural-interface/lib/assistant-persona.js +80 -11
- package/neural-interface/lib/assistant-plan-permissions.js +10 -9
- package/neural-interface/lib/assistant-route-gate-codex.js +3 -0
- package/neural-interface/lib/assistant-route-gate.js +6 -4
- package/neural-interface/lib/assistant-router.js +13 -8
- package/neural-interface/lib/assistant-runtime.js +183 -26
- package/neural-interface/lib/assistant-task-prompt.js +78 -14
- package/neural-interface/lib/assistant-tools.js +51 -0
- package/neural-interface/lib/backup-service.js +620 -3
- package/neural-interface/lib/backup-zip-reader.js +10 -5
- package/neural-interface/lib/backup-zip-writer.js +257 -0
- package/neural-interface/lib/browser-execution.js +10 -2
- package/neural-interface/lib/browser-guard-cdp.js +83 -0
- package/neural-interface/lib/browser-network-guard.js +284 -0
- package/neural-interface/lib/browser-policy-roots.js +64 -0
- package/neural-interface/lib/browser-tool-policy.js +186 -31
- package/neural-interface/lib/claude-agent-bridge.js +4 -2
- package/neural-interface/lib/claude-hooks-status.js +103 -0
- package/neural-interface/lib/claude-model-catalog.js +64 -14
- package/neural-interface/lib/claude-temporary.js +5 -11
- package/neural-interface/lib/cli-install.js +18 -138
- package/neural-interface/lib/cli-resolver.js +4 -1
- package/neural-interface/lib/cli-update-planner.js +32 -31
- package/neural-interface/lib/cloudflared/tunnel.js +42 -2
- package/neural-interface/lib/codex-mcp-approvals.js +11 -0
- package/neural-interface/lib/codex-settings-schema.js +109 -47
- package/neural-interface/lib/codex-worker-app-server.js +172 -0
- package/neural-interface/lib/confined-fs.js +30 -3
- package/neural-interface/lib/crash-notice-api.js +98 -0
- package/neural-interface/lib/editor-completion-opencode.js +164 -67
- package/neural-interface/lib/exec-loop-command.js +4 -1
- package/neural-interface/lib/http-guards.js +8 -2
- package/neural-interface/lib/install-details.js +4 -1
- package/neural-interface/lib/launch-cache.js +293 -0
- package/neural-interface/lib/login-start.js +524 -0
- package/neural-interface/lib/mcp-agent-servers.js +67 -0
- package/neural-interface/lib/mcp-client-config.js +0 -29
- package/neural-interface/lib/mcp-external-servers.js +323 -0
- package/neural-interface/lib/{mcp-profile-refresh-coordinator.js → mcp-tools-refresh-coordinator.js} +4 -4
- package/neural-interface/lib/morelogin.js +30 -0
- package/neural-interface/lib/native-loop-providers.js +565 -50
- package/neural-interface/lib/native-loop-runtime.js +287 -41
- package/neural-interface/lib/opencode-config.js +557 -0
- package/neural-interface/lib/opencode-db-schema.js +58 -0
- package/neural-interface/lib/opencode-history.js +49 -204
- package/neural-interface/lib/opencode-mcp-ready.js +108 -0
- package/neural-interface/lib/opencode-native-boundary.js +44 -0
- package/neural-interface/lib/opencode-protocol.js +310 -0
- package/neural-interface/lib/opencode-provider-view.js +81 -0
- package/neural-interface/lib/opencode-runtime-config.js +32 -15
- package/neural-interface/lib/opencode-serve-records.js +624 -0
- package/neural-interface/lib/opencode-v2-client.js +53 -131
- package/neural-interface/lib/opencode-v2-ws-requests.js +35 -195
- package/neural-interface/lib/opencode-worker-activity.js +229 -0
- package/neural-interface/lib/opencode-worker-data.js +27 -0
- package/neural-interface/lib/opencode2-client.js +796 -0
- package/neural-interface/lib/opencode2-proxy.js +76 -0
- package/neural-interface/lib/opencode2-translate.js +1057 -0
- package/neural-interface/lib/owner-authority.js +249 -0
- package/neural-interface/lib/page-session.js +275 -0
- package/neural-interface/lib/peer-identity.js +763 -0
- package/neural-interface/lib/process-identity.js +361 -0
- package/neural-interface/lib/request-gate.js +17 -2
- package/neural-interface/lib/rulesets/api.js +1 -1
- package/neural-interface/lib/rulesets/fs-safe.js +1 -1
- package/neural-interface/lib/rulesets/installer.js +92 -27
- package/neural-interface/lib/rulesets/json-edit.js +1 -1
- package/neural-interface/lib/session-cache-builder.js +18 -10
- package/neural-interface/lib/session-title-generator.js +58 -18
- package/neural-interface/lib/skills/api-v2.js +495 -0
- package/neural-interface/lib/skills/bundle.js +418 -0
- package/neural-interface/lib/skills/catalog.js +294 -0
- package/neural-interface/lib/skills/creation-api.js +191 -0
- package/neural-interface/lib/skills/creation-brief.js +247 -0
- package/neural-interface/lib/skills/creation-sources.js +267 -0
- package/neural-interface/lib/skills/creation.js +843 -0
- package/neural-interface/lib/skills/deploy.js +564 -0
- package/neural-interface/lib/skills/digest.js +119 -0
- package/neural-interface/lib/skills/discover.js +111 -0
- package/neural-interface/lib/skills/draft-confinement.js +214 -0
- package/neural-interface/lib/skills/eval-confinement.js +397 -0
- package/neural-interface/lib/skills/eval-sandbox.js +280 -0
- package/neural-interface/lib/skills/eval-trace.js +119 -0
- package/neural-interface/lib/skills/evals-api.js +212 -0
- package/neural-interface/lib/skills/evals.js +1573 -0
- package/neural-interface/lib/skills/export.js +200 -0
- package/neural-interface/lib/skills/frontmatter.js +64 -0
- package/neural-interface/lib/skills/health.js +75 -0
- package/neural-interface/lib/skills/json-edit.js +165 -0
- package/neural-interface/lib/skills/known-bridge-digests.json +387 -0
- package/neural-interface/lib/skills/known-bridges.js +96 -0
- package/neural-interface/lib/skills/library.js +823 -0
- package/neural-interface/lib/skills/local-store.js +21 -0
- package/neural-interface/lib/skills/migration.js +433 -0
- package/neural-interface/lib/skills/operations.js +45 -0
- package/neural-interface/lib/skills/ownership.js +427 -0
- package/neural-interface/lib/skills/packages.js +88 -0
- package/neural-interface/lib/skills/reviews.js +212 -0
- package/neural-interface/lib/skills/revision.js +80 -0
- package/neural-interface/lib/skills/roots.js +262 -0
- package/neural-interface/lib/skills/runtime-checks.js +355 -0
- package/neural-interface/lib/skills/scan-assets.js +252 -0
- package/neural-interface/lib/skills/scan-context.js +26 -0
- package/neural-interface/lib/skills/scan-shell.js +324 -0
- package/neural-interface/lib/skills/scan.js +364 -0
- package/neural-interface/lib/skills/scanners.js +54 -0
- package/neural-interface/lib/skills/service.js +522 -0
- package/neural-interface/lib/skills/telemetry.js +113 -0
- package/neural-interface/lib/sqlite-integrity-worker.js +22 -0
- package/neural-interface/lib/style-guide/api.js +4 -3
- package/neural-interface/lib/tool-lists.js +124 -0
- package/neural-interface/lib/untrusted-file-headers.js +27 -0
- package/neural-interface/lib/whatsapp/bridge.js +6 -3
- package/neural-interface/lib/worker-confinement.js +1192 -0
- package/neural-interface/lib/worker-guard-setting.js +56 -0
- package/neural-interface/lib/worker-mcp-channel.js +175 -0
- package/neural-interface/lib/worker-relay-ports.js +227 -0
- package/neural-interface/lib/worker-resume.js +229 -0
- package/neural-interface/lib/worker-sandbox.js +299 -0
- package/neural-interface/package-lock.json +1354 -50
- package/neural-interface/package.json +6 -2
- package/neural-interface/public/index.html +5 -3
- package/neural-interface/public/index2d.html +19 -11
- package/neural-interface/public/onboarding.html +2 -2
- package/neural-interface/public/shared/account-menu.js +8 -5
- package/neural-interface/public/shared/agent-runtime-options.js +76 -16
- package/neural-interface/public/shared/api.js +1527 -1336
- package/neural-interface/public/shared/app-frame.js +192 -0
- package/neural-interface/public/shared/assistant/asst-agents-dock.js +205 -19
- package/neural-interface/public/shared/assistant/asst-api.js +11 -3
- package/neural-interface/public/shared/assistant/asst-ask-styles.js +270 -0
- package/neural-interface/public/shared/assistant/asst-ask-tail.js +118 -0
- package/neural-interface/public/shared/assistant/asst-ask.js +397 -0
- package/neural-interface/public/shared/assistant/asst-brain-picker.js +241 -112
- package/neural-interface/public/shared/assistant/asst-composer.js +1 -1
- package/neural-interface/public/shared/assistant/asst-control.js +157 -26
- package/neural-interface/public/shared/assistant/asst-followups-card.js +383 -0
- package/neural-interface/public/shared/assistant/asst-followups.js +417 -0
- package/neural-interface/public/shared/assistant/asst-hosts.js +49 -2
- package/neural-interface/public/shared/assistant/asst-limit-dock.js +40 -0
- package/neural-interface/public/shared/assistant/asst-limit-wait.js +294 -0
- package/neural-interface/public/shared/assistant/asst-limit.js +168 -0
- package/neural-interface/public/shared/assistant/asst-mascot.js +49 -12
- package/neural-interface/public/shared/assistant/asst-menu.js +84 -12
- package/neural-interface/public/shared/assistant/asst-panel.js +303 -44
- package/neural-interface/public/shared/assistant/asst-rack-styles.js +93 -58
- package/neural-interface/public/shared/assistant/asst-redact.js +186 -0
- package/neural-interface/public/shared/assistant/asst-render.js +770 -100
- package/neural-interface/public/shared/assistant/asst-route.js +83 -12
- package/neural-interface/public/shared/assistant/asst-run-stage.js +231 -0
- package/neural-interface/public/shared/assistant/asst-sanitize.js +193 -3
- package/neural-interface/public/shared/assistant/asst-scene-styles.js +273 -0
- package/neural-interface/public/shared/assistant/asst-scenes.js +530 -0
- package/neural-interface/public/shared/assistant/asst-sidepanel-styles.js +3 -15
- package/neural-interface/public/shared/assistant/asst-sidepanel.js +67 -4
- package/neural-interface/public/shared/assistant/asst-slash.js +9 -7
- package/neural-interface/public/shared/assistant/asst-state.js +25 -15
- package/neural-interface/public/shared/assistant/asst-stop.js +127 -0
- package/neural-interface/public/shared/assistant/asst-styles.js +102 -21
- package/neural-interface/public/shared/assistant/asst-tool-kinds.js +165 -13
- package/neural-interface/public/shared/automation-context.js +5 -2
- package/neural-interface/public/shared/cc-dropdown.js +550 -0
- package/neural-interface/public/shared/cdx/cdx-context-menu.js +10 -9
- package/neural-interface/public/shared/cdx/cdx-copy.js +16 -0
- package/neural-interface/public/shared/cdx/cdx-output-model.js +22 -1
- package/neural-interface/public/shared/cdx/cdx-output.js +8 -7
- package/neural-interface/public/shared/cdx/cdx-panel.js +58 -150
- package/neural-interface/public/shared/cdx/cdx-protocol.js +28 -1
- package/neural-interface/public/shared/cdx/cdx-render.js +62 -32
- package/neural-interface/public/shared/cdx/cdx-requests.js +3 -3
- package/neural-interface/public/shared/cdx/cdx-skill-hints.js +54 -0
- package/neural-interface/public/shared/cdx/cdx-styles.js +15 -3
- package/neural-interface/public/shared/cdx/cdx-tabs.js +201 -74
- package/neural-interface/public/shared/cdx/cdx-ws.js +0 -1
- package/neural-interface/public/shared/cli-install-view.js +4 -8
- package/neural-interface/public/shared/cli-status.js +2 -2
- package/neural-interface/public/shared/constants.js +2 -2
- package/neural-interface/public/shared/context-menu-areas.js +34 -0
- package/neural-interface/public/shared/context-menu-logic.js +243 -0
- package/neural-interface/public/shared/context-menu-model.js +366 -0
- package/neural-interface/public/shared/context-menu.js +372 -0
- package/neural-interface/public/shared/cp/cp-context-menu.js +10 -9
- package/neural-interface/public/shared/cp/cp-markdown.js +4 -57
- package/neural-interface/public/shared/crash-notice-view.js +168 -0
- package/neural-interface/public/shared/data-cache.js +223 -0
- package/neural-interface/public/shared/desktop-bridge.js +11 -0
- package/neural-interface/public/shared/desktop-titlebar-view.js +78 -1
- package/neural-interface/public/shared/desktop-titlebar.js +214 -28
- package/neural-interface/public/shared/desktop-update-view.js +35 -5
- package/neural-interface/public/shared/editor/editor-panel-logic.js +2 -2
- package/neural-interface/public/shared/file-links.js +73 -8
- package/neural-interface/public/shared/html-shell.js +78 -86
- package/neural-interface/public/shared/i18n.js +2 -0
- package/neural-interface/public/shared/launch-prefetch.js +59 -0
- package/neural-interface/public/shared/ocp-v2/ocp-v2-approvals.js +7 -7
- package/neural-interface/public/shared/ocp-v2/ocp-v2-binding.js +4 -4
- package/neural-interface/public/shared/ocp-v2/ocp-v2-caps.js +3 -2
- package/neural-interface/public/shared/ocp-v2/ocp-v2-changes-logic.js +3 -101
- package/neural-interface/public/shared/ocp-v2/ocp-v2-childpanel.js +9 -2
- package/neural-interface/public/shared/ocp-v2/ocp-v2-compact-button.js +1 -3
- package/neural-interface/public/shared/ocp-v2/ocp-v2-composer-logic.js +2 -6
- package/neural-interface/public/shared/ocp-v2/ocp-v2-context-menu.js +11 -11
- package/neural-interface/public/shared/ocp-v2/ocp-v2-context-model.js +0 -3
- package/neural-interface/public/shared/ocp-v2/ocp-v2-events.js +8 -4
- package/neural-interface/public/shared/ocp-v2/ocp-v2-manager.js +7 -7
- package/neural-interface/public/shared/ocp-v2/ocp-v2-modelpicker.js +54 -17
- package/neural-interface/public/shared/ocp-v2/ocp-v2-outcome-notifications.js +16 -0
- package/neural-interface/public/shared/ocp-v2/ocp-v2-panel.js +140 -254
- package/neural-interface/public/shared/ocp-v2/ocp-v2-plan.js +1 -2
- package/neural-interface/public/shared/ocp-v2/ocp-v2-projectbar.js +97 -203
- package/neural-interface/public/shared/ocp-v2/ocp-v2-providers.js +11 -9
- package/neural-interface/public/shared/ocp-v2/ocp-v2-rehydrate.js +15 -4
- package/neural-interface/public/shared/ocp-v2/ocp-v2-render.js +7 -22
- package/neural-interface/public/shared/ocp-v2/ocp-v2-send-logic.js +401 -0
- package/neural-interface/public/shared/ocp-v2/ocp-v2-send.js +20 -13
- package/neural-interface/public/shared/ocp-v2/ocp-v2-session-actions.js +8 -61
- package/neural-interface/public/shared/ocp-v2/ocp-v2-sessions-logic.js +53 -82
- package/neural-interface/public/shared/ocp-v2/ocp-v2-slash-hints.js +16 -15
- package/neural-interface/public/shared/ocp-v2/ocp-v2-state.js +8 -21
- package/neural-interface/public/shared/ocp-v2/ocp-v2-status-logic.js +1 -99
- package/neural-interface/public/shared/ocp-v2/ocp-v2-status.js +5 -35
- package/neural-interface/public/shared/ocp-v2/ocp-v2-styles.js +48 -40
- package/neural-interface/public/shared/ocp-v2/{ocp-v2-profile-inheritance.js → ocp-v2-tools-inheritance.js} +16 -16
- package/neural-interface/public/shared/ocp-v2/ocp-v2-ws.js +205 -62
- package/neural-interface/public/shared/opencode-providers.js +39 -0
- package/neural-interface/public/shared/page-session.js +96 -0
- package/neural-interface/public/shared/registry.js +5 -3
- package/neural-interface/public/shared/retired-2d.js +3 -0
- package/neural-interface/public/shared/schedules-view.js +452 -0
- package/neural-interface/public/shared/settings/settings-app-pane.js +335 -0
- package/neural-interface/public/shared/settings/settings-app.js +243 -0
- package/neural-interface/public/shared/settings/settings-claude-hooks.js +135 -0
- package/neural-interface/public/shared/settings/settings-data.js +90 -0
- package/neural-interface/public/shared/settings/settings-ia.js +23 -6
- package/neural-interface/public/shared/settings/settings-opencode.js +89 -0
- package/neural-interface/public/shared/settings/settings-skill-folders-pane.js +265 -0
- package/neural-interface/public/shared/settings/settings-skill-folders.js +130 -0
- package/neural-interface/public/shared/settings/settings-worker-guard.js +72 -0
- package/neural-interface/public/shared/sidepanel-session-status.js +49 -0
- package/neural-interface/public/shared/skills/ss-args.js +96 -0
- package/neural-interface/public/shared/skills/ss-creation.js +219 -0
- package/neural-interface/public/shared/skills/ss-dialog.js +130 -0
- package/neural-interface/public/shared/skills/ss-doc.js +173 -0
- package/neural-interface/public/shared/skills/ss-eval-adapters.js +269 -0
- package/neural-interface/public/shared/skills/ss-eval-grade.js +359 -0
- package/neural-interface/public/shared/skills/ss-eval-optimise.js +153 -0
- package/neural-interface/public/shared/skills/ss-eval-plan.js +315 -0
- package/neural-interface/public/shared/skills/ss-evals.js +417 -0
- package/neural-interface/public/shared/skills/ss-form.js +159 -0
- package/neural-interface/public/shared/skills/ss-frontmatter.js +376 -0
- package/neural-interface/public/shared/skills/ss-ids.js +32 -0
- package/neural-interface/public/shared/skills/ss-lint.js +141 -0
- package/neural-interface/public/shared/skills/ss-presets.js +117 -0
- package/neural-interface/public/shared/skills/ss-profiles.js +475 -0
- package/neural-interface/public/shared/skills/ss-resources.js +165 -0
- package/neural-interface/public/shared/skills/ss-review-view.js +190 -0
- package/neural-interface/public/shared/skills/ss-tabs.js +79 -0
- package/neural-interface/public/shared/skills/ss-unicode.js +135 -0
- package/neural-interface/public/shared/skills/studio/sx-actions.js +222 -0
- package/neural-interface/public/shared/skills/studio/sx-cm.js +101 -0
- package/neural-interface/public/shared/skills/studio/sx-create-ai-view.js +749 -0
- package/neural-interface/public/shared/skills/studio/sx-create-ai.js +236 -0
- package/neural-interface/public/shared/skills/studio/sx-create-view.js +254 -0
- package/neural-interface/public/shared/skills/studio/sx-create.js +126 -0
- package/neural-interface/public/shared/skills/studio/sx-diff-view.js +43 -0
- package/neural-interface/public/shared/skills/studio/sx-diff.js +133 -0
- package/neural-interface/public/shared/skills/studio/sx-docs.js +480 -0
- package/neural-interface/public/shared/skills/studio/sx-dom.js +229 -0
- package/neural-interface/public/shared/skills/studio/sx-edit-view.js +848 -0
- package/neural-interface/public/shared/skills/studio/sx-editor-model.js +193 -0
- package/neural-interface/public/shared/skills/studio/sx-errors.js +22 -0
- package/neural-interface/public/shared/skills/studio/sx-geometry.js +82 -0
- package/neural-interface/public/shared/skills/studio/sx-import.js +262 -0
- package/neural-interface/public/shared/skills/studio/sx-inspector.js +308 -0
- package/neural-interface/public/shared/skills/studio/sx-install-flow.js +279 -0
- package/neural-interface/public/shared/skills/studio/sx-install.js +296 -0
- package/neural-interface/public/shared/skills/studio/sx-library-view.js +639 -0
- package/neural-interface/public/shared/skills/studio/sx-library.js +475 -0
- package/neural-interface/public/shared/skills/studio/sx-main.js +681 -0
- package/neural-interface/public/shared/skills/studio/sx-operations-view.js +98 -0
- package/neural-interface/public/shared/skills/studio/sx-operations.js +11 -0
- package/neural-interface/public/shared/skills/studio/sx-plan.js +158 -0
- package/neural-interface/public/shared/skills/studio/sx-publish-view.js +621 -0
- package/neural-interface/public/shared/skills/studio/sx-review-view.js +369 -0
- package/neural-interface/public/shared/skills/studio/sx-review.js +148 -0
- package/neural-interface/public/shared/skills/studio/sx-scan.js +35 -0
- package/neural-interface/public/shared/skills/studio/sx-snippets.js +96 -0
- package/neural-interface/public/shared/skills/studio/sx-subject.js +248 -0
- package/neural-interface/public/shared/skills/studio/sx-test-view.js +994 -0
- package/neural-interface/public/shared/skills/studio/sx-test.js +328 -0
- package/neural-interface/public/shared/skills/studio/sx-versions-view.js +241 -0
- package/neural-interface/public/shared/skills/studio/sx-workspace-view.js +425 -0
- package/neural-interface/public/shared/styleguide/sg-panel.js +4 -2
- package/neural-interface/public/shared/styles.css +2065 -2150
- package/neural-interface/public/shared/synabun-mascot.js +1115 -9
- package/neural-interface/public/shared/tool-list-picker.js +55 -0
- package/neural-interface/public/shared/tool-list-storage.js +24 -0
- package/neural-interface/public/shared/tool-list-view.js +112 -0
- package/neural-interface/public/shared/tour/tour-actor.js +268 -0
- package/neural-interface/public/shared/tour/tour-beats.js +336 -0
- package/neural-interface/public/shared/tour/tour-engine.js +884 -0
- package/neural-interface/public/shared/tour/tour-ink.js +390 -0
- package/neural-interface/public/shared/tour/tour-stage.js +440 -0
- package/neural-interface/public/shared/tour/tour-state.js +40 -0
- package/neural-interface/public/shared/tour/tour-tips.js +99 -0
- package/neural-interface/public/shared/tour/tour.css +144 -0
- package/neural-interface/public/shared/ui-assistant.js +27 -5
- package/neural-interface/public/shared/ui-automation-studio.js +155 -152
- package/neural-interface/public/shared/ui-bookmarks.js +2 -1
- package/neural-interface/public/shared/ui-claude-panel.js +153 -13
- package/neural-interface/public/shared/ui-codex-panel.js +1 -0
- package/neural-interface/public/shared/ui-command-runner.js +6 -3
- package/neural-interface/public/shared/ui-context-menu.js +295 -0
- package/neural-interface/public/shared/ui-cost-widget.js +57 -59
- package/neural-interface/public/shared/ui-crash-notice.js +187 -0
- package/neural-interface/public/shared/ui-detail.js +21 -9
- package/neural-interface/public/shared/ui-file-links.js +1 -0
- package/neural-interface/public/shared/ui-image-gallery.js +56 -52
- package/neural-interface/public/shared/ui-invite.js +18 -2
- package/neural-interface/public/shared/ui-keybinds.js +26 -1
- package/neural-interface/public/shared/ui-link.js +9 -9
- package/neural-interface/public/shared/ui-menubar.js +229 -40
- package/neural-interface/public/shared/ui-native-loop-router-state.js +5 -0
- package/neural-interface/public/shared/ui-native-loop-router.js +9 -1
- package/neural-interface/public/shared/ui-navbar.js +4 -24
- package/neural-interface/public/shared/ui-opencode-panel-v2.js +1 -0
- package/neural-interface/public/shared/ui-panels.js +96 -65
- package/neural-interface/public/shared/ui-schedules-studio.js +2267 -953
- package/neural-interface/public/shared/ui-sessions.js +20 -7
- package/neural-interface/public/shared/ui-settings.js +679 -496
- package/neural-interface/public/shared/ui-sidebar.js +4 -4
- package/neural-interface/public/shared/ui-sidepanel-session-entry.js +13 -0
- package/neural-interface/public/shared/ui-sidepanel-sessions.js +56 -5
- package/neural-interface/public/shared/ui-sidepanel-state.js +23 -12
- package/neural-interface/public/shared/ui-sidepanel-tray.js +84 -8
- package/neural-interface/public/shared/ui-sidepanel-windows.js +16 -11
- package/neural-interface/public/shared/ui-skills.js +42 -2781
- package/neural-interface/public/shared/ui-styleguide.js +5 -2
- package/neural-interface/public/shared/ui-sync.js +22 -5
- package/neural-interface/public/shared/ui-terminal.js +159 -199
- package/neural-interface/public/shared/ui-tool-updates.js +3 -1
- package/neural-interface/public/shared/ui-tooltip.js +10 -5
- package/neural-interface/public/shared/ui-trash.js +14 -5
- package/neural-interface/public/shared/ui-tutorial-layout.js +3 -117
- package/neural-interface/public/shared/ui-tutorial-preview.js +103 -0
- package/neural-interface/public/shared/ui-tutorial-steps.js +3 -61
- package/neural-interface/public/shared/ui-tutorial.css +47 -100
- package/neural-interface/public/shared/ui-tutorial.js +285 -499
- package/neural-interface/public/shared/ui-update.js +121 -16
- package/neural-interface/public/shared/ui-whiteboard.js +313 -59
- package/neural-interface/public/shared/ui-worker-end.js +4 -0
- package/neural-interface/public/shared/ui-workspaces.js +3 -2
- package/neural-interface/public/shared/view-variant.js +66 -0
- package/neural-interface/public/shared/whatsapp/wa-view.js +1 -1
- package/neural-interface/public/shared/whiteboard-list.js +172 -0
- package/neural-interface/public/shared/worker-end.js +10 -3
- package/neural-interface/public/shared/worker-restart.js +12 -0
- package/neural-interface/public/sidepanel-session.html +1 -1
- package/neural-interface/public/variant/2d/background.js +2 -1
- package/neural-interface/public/variant/2d/context-menu.js +8 -2
- package/neural-interface/public/variant/2d/graph.js +42 -20
- package/neural-interface/public/variant/2d/hulls.js +14 -9
- package/neural-interface/public/variant/2d/lasso.js +11 -7
- package/neural-interface/public/variant/2d/main.js +36 -2
- package/neural-interface/public/variant/2d/minimap.js +11 -13
- package/neural-interface/public/variant/3d/main.js +72 -6
- package/neural-interface/public/variant/3d/map-controls.js +24 -9
- package/neural-interface/public/vendor/caveat/OFL.txt +93 -0
- package/neural-interface/public/vendor/caveat/caveat-latin-400-normal.woff2 +0 -0
- package/neural-interface/public/vendor/caveat/caveat-latin-700-normal.woff2 +0 -0
- package/neural-interface/public/vendor/caveat/caveat.css +17 -0
- package/neural-interface/public/vendor/caveat/manifest.json +12 -0
- package/neural-interface/public/vendor/highlight/LICENSE +29 -0
- package/neural-interface/public/vendor/highlight/github-dark-dimmed-11.12.0.min.css +9 -0
- package/neural-interface/public/vendor/highlight/highlight-11.12.0.min.js +1262 -0
- package/neural-interface/public/vendor/highlight/manifest.json +5 -0
- package/neural-interface/public/vendor/html2canvas/LICENSE +22 -0
- package/neural-interface/public/vendor/html2canvas/html2canvas-1.4.1.min.js +20 -0
- package/neural-interface/public/vendor/html2canvas/manifest.json +4 -0
- package/neural-interface/public/vendor/marked/LICENSE.md +44 -0
- package/neural-interface/public/vendor/marked/manifest.json +4 -0
- package/neural-interface/public/vendor/marked/marked-14.1.4.esm.js +2502 -0
- package/neural-interface/public/vendor/mermaid/LICENSE +21 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/abnfDiagram-PAR5IXJI.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/architecture-7GRP2DOG-M7G3SWCO.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/architectureDiagram-2PNWDENW.mjs +36 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/blockDiagram-PUWGUBOA.mjs +129 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/c4Diagram-3NLNHZP5.mjs +38 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-2NMSUPJH.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-32MCUW2P.mjs +206 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-3NPNU5GP.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-3SMGFRGC.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-4CAJECRU.mjs +14 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-4OADUZ5Q.mjs +10 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-5IMINLNL.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-5IQCK5RY.mjs +3 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-5MVFYWFU.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-AQ6EADP3.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-AZZRMDJM.mjs +15 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-CLS4B6BI.mjs +168 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-DZP67EKU.mjs +70 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-EICP7K5X.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-GDY2UAB4.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-H7VHZCWX.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-IJBDOHL6.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-JFFVMJIA.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-JQRUD6KW.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-JZGDI5JK.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-KD6SPIPN.mjs +2 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-NF366YI6.mjs +231 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-NUDPKBEF.mjs +32 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-OJINMHTS.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-Q6NGPIAH.mjs +2 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-QJSWEUOL.mjs +132 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-R7JDMUKV.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-RCXAM2GK.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-RT4MAWR3.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-SFMINX4W.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-SLDQ463B.mjs +88 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-STOV2HOB.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-T5OCTHI4.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-TCEHX7R3.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-VQC2EELJ.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-VRZKL3EH.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-VTHDQ7BV.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-VY2OSTJR.mjs +2 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-WUBWJARI.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-YBMQ5JGJ.mjs +2 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-YDMLL4PJ.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-Z6V44ARX.mjs +62 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-ZJJ7VQJP.mjs +339 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-ZNLK7HRR.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-ZQN7UHIL.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-ZS6VBONO.mjs +174 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-ZUNWM646.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/classDiagram-FGAMPOII.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/classDiagram-v2-AI5RJEK4.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/cose-bilkent-Q72C2OJ3.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/cynefin-OW5HDTMX-6OCTWOGG.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/cynefinDiagram-2OOWUY4M.mjs +62 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/dagre-MPVFI544.mjs +4 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/diagram-AE4NWXFW.mjs +24 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/diagram-BUJID5UG.mjs +41 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/diagram-C44CZ5NI.mjs +24 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/diagram-EYOLEXXS.mjs +30 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/diagram-P7B6PWOJ.mjs +3 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/ebnfDiagram-FTPZ3PV6.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/erDiagram-H3ERR26V.mjs +99 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/eventmodeling-NTZA5JFV-WKSWQHFS.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/flowDiagram-YHGXBVSY.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/ganttDiagram-RWFTB7ED.mjs +292 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/gitGraph-4MIJSDKK-JH2HKYUO.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/gitGraphDiagram-MAR6GKOP.mjs +106 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/info-A6RAGUB7-3TX3OFWW.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/infoDiagram-LV547AFH.mjs +2 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/ishikawaDiagram-PUJQGNDS.mjs +70 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/journeyDiagram-EW5QJT4W.mjs +139 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/kanban-definition-JSQERAZB.mjs +89 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/katex-CZ4GXH2S.mjs +257 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/mindmap-definition-VBJCLQLM.mjs +96 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/packet-AYTQ26CC-MKII6P3Y.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/pegDiagram-7WILOQTJ.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/pie-WAS4IAKB-6B4OPMJ5.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/pieDiagram-EFA7CBSB.mjs +39 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/quadrantDiagram-WARVYA7P.mjs +7 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/radar-RG4KPBEZ-UPTR3XUL.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/railroad-74A4TZTK-MEFUPFMS.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/railroad-abnf-HS5TGJTU-R5YG2S5D.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/railroad-ebnf-LZEXJU2U-G5QEX2L6.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/railroad-peg-WCYAUIDC-H5I2ZRY2.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/railroadDiagram-5NIQVNLQ.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/requirementDiagram-SXREGOKZ.mjs +84 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/sankeyDiagram-VV74M2VP.mjs +40 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/sequenceDiagram-52ZFIFDL.mjs +162 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/sizeCapture-FGA5AGYP.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/stateDiagram-3QAKPIVI.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/stateDiagram-v2-HN2P4RDZ.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/swimlanes-QLVP42QD.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/swimlanesDiagram-JKPDBFCN.mjs +8 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/timeline-definition-GS45XAXW.mjs +120 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/treeView-Q6P3EWNA-ZRJGANNQ.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/treemap-WGGIJYW6-K64VN5AS.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/vennDiagram-QIQU2ZRJ.mjs +34 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/wardley-WFR3VGLG-D67OWCCH.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/wardleyDiagram-U6YNM66N.mjs +78 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/xychartDiagram-XJAZEDNM.mjs +7 -0
- package/neural-interface/public/vendor/mermaid/manifest.json +4 -0
- package/neural-interface/public/vendor/mermaid/mermaid-11.17.2.esm.min.mjs +14 -0
- package/neural-interface/public/vendor/yaml/LICENSE +13 -0
- package/neural-interface/public/vendor/yaml/manifest.json +4 -0
- package/neural-interface/public/vendor/yaml/yaml-2.9.1.min.js +137 -0
- package/neural-interface/scripts/build-vendor.mjs +116 -3
- package/neural-interface/server.js +4834 -2541
- package/neural-interface/templates/rulesets/core.md +1 -1
- package/neural-interface/templates/rulesets/legacy-hashes.json +18 -0
- package/neural-interface/templates/rulesets/manifest.json +8 -8
- package/npm-shrinkwrap.json +18 -12
- package/package.json +16 -3
- package/postinstall.js +9 -4
- package/preuninstall.js +1 -1
- package/setup.js +97 -322
- package/skills/skill-forge/SKILL.md +177 -0
- package/skills/skill-forge/evals/evals.json +54 -0
- package/skills/skill-forge/references/package.md +105 -0
- package/skills/skill-forge/references/review-and-recovery.md +63 -0
- package/skills/skill-forge/references/runtime-profiles.md +20 -0
- package/skills/skill-forge/references/sources-and-capabilities.md +51 -0
- package/skills/synabun/SKILL.md +10 -7
- package/skills/synabun/modules/audit.md +1 -5
- package/skills/synabun/modules/changelog.md +1 -5
- package/skills/synabun/modules/idea.md +1 -1
- package/skills/synabun/modules/memorize.md +6 -9
- package/skills/synabun/modules/schedule.md +5 -5
- package/mcp-server/dist/services/profiles.d.ts +0 -120
- package/mcp-server/dist/services/profiles.d.ts.map +0 -1
- package/mcp-server/dist/services/profiles.js +0 -387
- package/mcp-server/dist/services/profiles.js.map +0 -1
- package/mcp-server/dist/tools/leonardo-browser-tools.d.ts +0 -97
- package/mcp-server/dist/tools/leonardo-browser-tools.d.ts.map +0 -1
- package/mcp-server/dist/tools/leonardo-browser-tools.js +0 -228
- package/mcp-server/dist/tools/leonardo-browser-tools.js.map +0 -1
- package/mcp-server/dist/tools/leonardo.d.ts +0 -13
- package/mcp-server/dist/tools/leonardo.d.ts.map +0 -1
- package/mcp-server/dist/tools/leonardo.js +0 -21
- package/mcp-server/dist/tools/leonardo.js.map +0 -1
- package/mcp-server/dist/tools/profile.d.ts +0 -17
- package/mcp-server/dist/tools/profile.d.ts.map +0 -1
- package/mcp-server/dist/tools/profile.js +0 -114
- package/mcp-server/dist/tools/profile.js.map +0 -1
- package/mcp-server/src/services/profiles.ts +0 -428
- package/mcp-server/src/tools/leonardo-browser-tools.ts +0 -291
- package/mcp-server/src/tools/leonardo.ts +0 -30
- package/mcp-server/src/tools/profile.ts +0 -122
- package/neural-interface/lib/assistant-brains/opencode-browser-policy.js +0 -47
- package/neural-interface/lib/assistant-brains/opencode-route-gate.js +0 -62
- package/neural-interface/lib/mcp-profile-servers.js +0 -120
- package/neural-interface/public/shared/ui-tutorial-draw.js +0 -311
- package/skills/leonardo/SKILL.md +0 -168
- package/skills/leonardo/icon.svg +0 -1
- package/skills/leonardo/modules/browser-guide.md +0 -378
- package/skills/leonardo/modules/image-prompter.md +0 -283
- package/skills/leonardo/modules/model-advisor.md +0 -100
- package/skills/leonardo/modules/prompt-library.md +0 -110
- package/skills/leonardo/modules/style-guide.md +0 -186
- package/skills/leonardo/modules/video-prompter.md +0 -317
|
@@ -1,18 +1,26 @@
|
|
|
1
|
+
import { fetchToolCatalog } from './agent-runtime-options.js';
|
|
2
|
+
import { configureToolPickerTranslation, toolPickerHTML, readToolPicker, toolWord } from './tool-list-picker.js';
|
|
1
3
|
import { mountMemoryMaintenance } from './ui-memory-maintenance.js';
|
|
2
4
|
import { buildJudgmentsTab, wireJudgmentsTab } from './ui-judgments.js';
|
|
3
5
|
import { buildWhatsAppTab, wireWhatsAppTab } from './ui-whatsapp.js';
|
|
4
6
|
import { buildCodeEditorTab, wireCodeEditorTab } from './settings/settings-code-editor-pane.js';
|
|
5
7
|
import { buildAboutTab, wireAboutTab } from './settings/settings-about-pane.js';
|
|
8
|
+
import { buildAppTab, wireAppTab } from './settings/settings-app-pane.js';
|
|
6
9
|
import { SETTINGS_GROUPS, SETTINGS_PAGES, DEFAULT_SETTINGS_PAGE, pageKeys, getSettingsPage, resolveSettingsTarget, homeOfControl } from './settings/settings-ia.js';
|
|
7
10
|
import * as kit from './settings/settings-kit.js';
|
|
11
|
+
import { skillFoldersHTML, wireSkillFolders, refreshSkillChips, chipState, installedToast } from './settings/settings-skill-folders-pane.js';
|
|
8
12
|
import { RULES_HOST_LABELS, describeRulesHost, describeRulesInstall, describeRulesInstallAll } from './settings/settings-rules.js';
|
|
9
13
|
import { readUserContextResponse, describeUserContext, describeUserContextSaveFailure } from './settings/settings-user-context.js';
|
|
14
|
+
import { readWorkerGuardResponse, describeWorkerGuard, describeWorkerGuardSaveFailure } from './settings/settings-worker-guard.js';
|
|
15
|
+
import { CLAUDE_HOOK_ROWS, readClaudeHooksResponse, describeClaudeHooks, describeClaudeHooksSaveFailure } from './settings/settings-claude-hooks.js';
|
|
10
16
|
import { readGreetingStatus, describeGreeting, describeGreetingSaveFailure } from './settings/settings-greeting.js';
|
|
17
|
+
import { describeOpencodeInstall } from './settings/settings-opencode.js';
|
|
11
18
|
import { renderGreeting, clearGreeting } from './greeting/greeting-card.js';
|
|
12
19
|
import { createTunnelCard, drawInto, mcpKeyNeededText, readTunnel } from './ui-tunnel.js';
|
|
13
20
|
import { remoteMcpAddress } from './tunnel-view.js';
|
|
14
21
|
import { recheckCliStatus, getCliStatus, addCliToPath } from './cli-status.js';
|
|
15
22
|
import { cliInstallText, setCliInstallTranslator } from './cli-install-view.js';
|
|
23
|
+
import { dropdownHTML, wireDropdowns } from './cc-dropdown.js';
|
|
16
24
|
setCliInstallTranslator((key, params) => kit.tx(`cliInstall.${key}`, params));
|
|
17
25
|
import { setGitBashTranslator } from './git-bash-view.js';
|
|
18
26
|
import { gitBashRowHtml, wireGitBashRow } from './ui-git-bash.js';
|
|
@@ -20,12 +28,14 @@ setGitBashTranslator((key, params) => kit.tx(`gitBash.${key}`, params));
|
|
|
20
28
|
import { initI18n, isReady as i18nReady, SUPPORTED_LOCALES, LOCALE_NAMES, SYSTEM_LOCALE, getLocale, getLocaleChoice, setLocaleChoice, getSystemLocale, englishTranslator, loadEnglishTranslator } from './i18n.js';
|
|
21
29
|
import { wireSettingsShell, wireSettingsSearch, collectSearchRows, revealSettingsTarget, followSettingsTarget, onSettingsTakeover, applyStoredSections, storeSectionState } from './settings/settings-shell.js';
|
|
22
30
|
import { buildSettingsSearchIndex } from './settings/settings-search.js';
|
|
31
|
+
import { prefetchSettingsData, readSettingsData, refreshSettingsData } from './settings/settings-data.js';
|
|
32
|
+
import { registerLaunchPrefetch } from './launch-prefetch.js';
|
|
23
33
|
// ═══════════════════════════════════════════
|
|
24
34
|
// UI-SETTINGS — Settings modal with shared tabs + variant tab injection
|
|
25
35
|
// ═══════════════════════════════════════════
|
|
26
36
|
//
|
|
27
37
|
// The most complex shared UI module. Builds a floating settings panel with
|
|
28
|
-
//
|
|
38
|
+
// fourteen task pages in six groups (./settings/settings-ia.js). A page is a
|
|
29
39
|
// title, a one-line purpose and section cards (./settings/settings-kit.js);
|
|
30
40
|
// each builder below owns one pane of a page. Variant-registered tabs (the 2D
|
|
31
41
|
// Graphics tab) join the Appearance page. Old tab ids still open the right page.
|
|
@@ -34,6 +44,7 @@ import { state, emit, on } from './state.js';
|
|
|
34
44
|
import { getSettingsTabs } from './registry.js';
|
|
35
45
|
import { escapeHtml } from './utils.js';
|
|
36
46
|
import { storage } from './storage.js';
|
|
47
|
+
import { normalizeViewConfig } from './view-variant.js';
|
|
37
48
|
import { KEYS } from './constants.js';
|
|
38
49
|
import { registerAction } from './ui-keybinds.js';
|
|
39
50
|
import { CLI_PROFILES, ensureModelsForProfile, getModelsForProfile } from './agent-runtime-options.js';
|
|
@@ -44,6 +55,7 @@ import { getNotifSettings, playTestSound, sendTestBanner, SOUND_PRESETS } from '
|
|
|
44
55
|
import {
|
|
45
56
|
fetchRulesStatus, fetchRulesTextWithFallback, installRules, removeRules, installAllRules, setRulesAutoUpdate, removeLegacyRules,
|
|
46
57
|
} from './api.js';
|
|
58
|
+
import { centeredInFrame } from './app-frame.js';
|
|
47
59
|
|
|
48
60
|
// ── SVG icon constants ──
|
|
49
61
|
|
|
@@ -76,6 +88,8 @@ const TAB_ICONS = {
|
|
|
76
88
|
whatsapp: '<svg viewBox="0 0 24 24"><path d="M3.5 20.5l1.3-3.9A8.5 8.5 0 1 1 8 19.6z"/><path d="M9.5 8.5c.4 2.6 2.9 5.2 5.5 5.6l1-1.2 1.6.8c-.3 1-1.1 1.6-2.1 1.5-2.9-.3-5.8-3.2-6.1-6.1-.1-1 .5-1.8 1.5-2.1l.8 1.6z"/></svg>',
|
|
77
89
|
morelogin: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round"><path d="M12 11.5a1 1 0 0 0-1 1c0 1.8-.3 3.6-1.2 5.2"/><path d="M7.8 14.5a8 8 0 0 0-.6 4.3"/><path d="M12 7.5a4.5 4.5 0 0 1 4.5 4.5c0 .6 0 1.2-.1 1.8"/><path d="M5 9.2a7 7 0 0 1 11-1.9"/><path d="M15 13a3 3 0 0 1-3 3"/></svg>',
|
|
78
90
|
editor: '<svg viewBox="0 0 24 24"><polyline points="16 18 22 12 16 6"/><polyline points="8 6 2 12 8 18"/><line x1="13.5" y1="5" x2="10.5" y2="19"/></svg>',
|
|
91
|
+
// Settings > App: the software's own controls (a window with its title bar)
|
|
92
|
+
app: '<svg viewBox="0 0 24 24"><rect x="3" y="4" width="18" height="16" rx="2"/><line x1="3" y1="9" x2="21" y2="9"/><line x1="6.5" y1="6.5" x2="6.51" y2="6.5"/><line x1="9.5" y1="6.5" x2="9.51" y2="6.5"/></svg>',
|
|
79
93
|
about: '<svg viewBox="0 0 24 24"><circle cx="12" cy="12" r="10"/><line x1="12" y1="16" x2="12" y2="11"/><line x1="12" y1="7.5" x2="12.01" y2="7.5"/></svg>',
|
|
80
94
|
};
|
|
81
95
|
|
|
@@ -108,6 +122,7 @@ function showCCToast(msg, duration = 3500) {
|
|
|
108
122
|
// INTERFACE CUSTOMIZATION — config, presets, apply
|
|
109
123
|
// ═══════════════════════════════════════════
|
|
110
124
|
|
|
125
|
+
// Optional legacy view preferences must never reintroduce a 2D selection.
|
|
111
126
|
const IFACE_DEFAULTS = {
|
|
112
127
|
visualizationEnabled: true,
|
|
113
128
|
scale: 1,
|
|
@@ -156,7 +171,7 @@ const IFACE_SLIDERS = {
|
|
|
156
171
|
export function loadIfaceConfig() {
|
|
157
172
|
try {
|
|
158
173
|
const raw = storage.getItem('neural-interface-config');
|
|
159
|
-
if (raw) return { ...IFACE_DEFAULTS, ...JSON.parse(raw) };
|
|
174
|
+
if (raw) return { ...IFACE_DEFAULTS, ...normalizeViewConfig(JSON.parse(raw)) };
|
|
160
175
|
// Migrate legacy ui-scale if present
|
|
161
176
|
const legacyScale = storage.getItem('neural-ui-scale');
|
|
162
177
|
if (legacyScale) {
|
|
@@ -169,7 +184,7 @@ export function loadIfaceConfig() {
|
|
|
169
184
|
}
|
|
170
185
|
|
|
171
186
|
export function saveIfaceConfig(cfg) {
|
|
172
|
-
storage.setItem('neural-interface-config', JSON.stringify(cfg));
|
|
187
|
+
storage.setItem('neural-interface-config', JSON.stringify(normalizeViewConfig(cfg)));
|
|
173
188
|
// Keep legacy key in sync for backwards compat
|
|
174
189
|
storage.setItem('neural-ui-scale', cfg.scale);
|
|
175
190
|
}
|
|
@@ -546,7 +561,7 @@ function buildSearchHTML() {
|
|
|
546
561
|
}
|
|
547
562
|
|
|
548
563
|
// What search reads from the panel beside its index (settings-search.js): the labelled rows of the panes that draw
|
|
549
|
-
// themselves, and the names of what the panes load (projects, providers, tools, tool
|
|
564
|
+
// themselves, and the names of what the panes load (projects, providers, tools, tool lists and servers, skills,
|
|
550
565
|
// browser profiles, themes, the linked WhatsApp account). Labels and names only: a source names label elements and
|
|
551
566
|
// the shell reads their text, never a value; `help` is given only where it is the app's own copy. `named` rows may
|
|
552
567
|
// sit inside an inventory control (a list it tags).
|
|
@@ -952,7 +967,7 @@ function buildSetupTab(setupStatus) {
|
|
|
952
967
|
{ id: 'claude', name: 'Claude', icon: ANTHROPIC_ICON, file: '~/.claude.json', ids: { toggle: 'SET004', cli: 'SET005', primary: 'SET006', remove: 'SET007', view: 'SET008', copy: 'SET009' } },
|
|
953
968
|
{ id: 'gemini', name: 'Gemini', icon: GEMINI_ICON, file: '~/.gemini/settings.json', ids: { toggle: 'SET010', config: 'SET011', primary: 'SET012', remove: 'SET013', view: 'SET014', copy: 'SET015' } },
|
|
954
969
|
{ id: 'codex', name: 'Codex', icon: OPENAI_ICON, file: '~/.codex/config.toml', ids: { toggle: 'SET016', cli: 'SET017', config: 'SET018', primary: 'SET019', remove: 'SET020', view: 'SET021', copy: 'SET022' } },
|
|
955
|
-
{ id: 'opencode', name: 'OpenCode', icon: OPENCODE_ICON, file: '~/.config/opencode/
|
|
970
|
+
{ id: 'opencode', name: 'OpenCode', icon: OPENCODE_ICON, file: '~/.config/opencode/opencode.json', ids: { toggle: 'SET023', config: 'SET024', primary: 'SET025', remove: 'SET026', view: 'SET027', copy: 'SET028' } },
|
|
956
971
|
].map((p) => ({ ...p, on: setupStatus[p.id]?.connected || false }));
|
|
957
972
|
|
|
958
973
|
const copyLabel = (invId) => `${COPY_ICON} ${kit.L(invId)}`;
|
|
@@ -1170,23 +1185,23 @@ function buildMcpTab() {
|
|
|
1170
1185
|
return `
|
|
1171
1186
|
<div class="stg-pane" data-stg-pane="mcp">
|
|
1172
1187
|
<div class="stg-mcp-page-tabs" role="tablist" aria-label="${kit.L('MCP001')}" ${kit.inv('MCP001')}>
|
|
1173
|
-
${tab('stg-mcp-page-tab', 'data-pane', '
|
|
1188
|
+
${tab('stg-mcp-page-tab', 'data-pane', 'tools', m('tab.tools'), true)}
|
|
1174
1189
|
${tab('stg-mcp-page-tab', 'data-pane', 'installed', m('tab.installed'), false)}
|
|
1175
1190
|
</div>
|
|
1176
1191
|
|
|
1177
|
-
<div class="stg-mcp-pane" data-pane="
|
|
1192
|
+
<div class="stg-mcp-pane" data-pane="tools" data-stg-reveal='.stg-mcp-page-tab[data-pane="tools"]'>
|
|
1178
1193
|
${kit.card({
|
|
1179
|
-
id: 'stg-sec-
|
|
1194
|
+
id: 'stg-sec-default-tools', section: 'connected_tools.default_tools',
|
|
1180
1195
|
body: `
|
|
1181
1196
|
<div class="stg-mcp-intro">
|
|
1182
|
-
<div class="stg-help" style="margin-top:0">${m('
|
|
1197
|
+
<div class="stg-help" style="margin-top:0">${m('defaultToolsHelp')}</div>
|
|
1183
1198
|
<div class="stg-mcp-note">
|
|
1184
1199
|
<strong>${m('noteTitle')}</strong>
|
|
1185
1200
|
<span>${m('noteBody')}</span>
|
|
1186
1201
|
</div>
|
|
1187
1202
|
</div>
|
|
1188
|
-
<div id="stg-mcp-
|
|
1189
|
-
<div class="stg-mcp-
|
|
1203
|
+
<div id="stg-mcp-default-tools" class="stg-mcp-default-tools stg-mcp-block" ${kit.inv('MCP022')} role="group" aria-label="${kit.L('MCP022')}">
|
|
1204
|
+
<div class="stg-mcp-empty-note">${m('loading')}</div>
|
|
1190
1205
|
</div>
|
|
1191
1206
|
<div id="stg-mcp-always-on" class="stg-mcp-subsection" ${kit.inv('MCP024')}></div>
|
|
1192
1207
|
<div class="stg-mcp-subsection stg-mcp-servers-section">
|
|
@@ -1298,6 +1313,7 @@ function buildOpencodeTab() {
|
|
|
1298
1313
|
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M21 2v6h-6"/><path d="M3 12a9 9 0 0 1 15-6.7L21 8"/><path d="M3 22v-6h6"/><path d="M21 12a9 9 0 0 1-15 6.7L3 16"/></svg>
|
|
1299
1314
|
</button>
|
|
1300
1315
|
</span>`, { forId: 'stg-ocp-port', className: 'ocp-server-bar' })}
|
|
1316
|
+
<div class="stg-help ocp-install-note" id="stg-ocp-install" role="status" aria-live="polite" hidden></div>
|
|
1301
1317
|
</div>
|
|
1302
1318
|
|
|
1303
1319
|
<div class="ocp-group-head">
|
|
@@ -1323,9 +1339,10 @@ function buildOpencodeTab() {
|
|
|
1323
1339
|
<div class="ocp-group">
|
|
1324
1340
|
${kit.advanced(`
|
|
1325
1341
|
<div class="ocp-config-footer" ${kit.inv('OCP019')}>
|
|
1326
|
-
<span class="ocp-config-path">${kit.th('settings.redesign.opencode.configPath', { path: '<code class="stg-code">~/.config/opencode/
|
|
1342
|
+
<span class="ocp-config-path">${kit.th('settings.redesign.opencode.configPath', { path: '<code class="stg-code" id="stg-ocp-config-path">~/.config/opencode/opencode.json</code>' })}</span>
|
|
1327
1343
|
<button type="button" class="ocp-config-link" id="stg-ocp-open-config" aria-describedby="stg-ocp-open-config-help">${kit.L('OCP019')}</button>
|
|
1328
1344
|
</div>
|
|
1345
|
+
<p class="stg-help" id="stg-ocp-config-jsonc" hidden></p>
|
|
1329
1346
|
${kit.help('OCP019', { forId: 'stg-ocp-open-config' })}`)}
|
|
1330
1347
|
</div>`,
|
|
1331
1348
|
})}
|
|
@@ -1336,8 +1353,7 @@ function buildOpencodeTab() {
|
|
|
1336
1353
|
body: `
|
|
1337
1354
|
<div class="ocp-compact-group ocp-rows">
|
|
1338
1355
|
${check('OCP013', 'stg-ocp-compact-auto')}
|
|
1339
|
-
${
|
|
1340
|
-
${kit.field('OCP015', `<input type="number" class="ocp-reserved-input" id="stg-ocp-compact-reserved" value="10000" min="1000" max="50000" step="1000" aria-describedby="stg-ocp-compact-reserved-help">`, { forId: 'stg-ocp-compact-reserved' })}
|
|
1356
|
+
${kit.field('OCP015', `<input type="number" class="ocp-reserved-input" id="stg-ocp-compact-reserved" value="" placeholder="${kit.te('settings.redesign.opencode.bufferDefault')}" min="0" max="1000000" step="1000" aria-describedby="stg-ocp-compact-reserved-help">`, { forId: 'stg-ocp-compact-reserved' })}
|
|
1341
1357
|
${kit.toggleField('OCP016', `<button type="button" class="cc-toggle on" id="stg-ocp-snapshot-toggle" aria-labelledby="stg-ocp-snapshot-toggle-label" aria-describedby="stg-ocp-snapshot-toggle-help"></button>`, { forId: 'stg-ocp-snapshot-toggle', className: 'ocp-snapshot-row' })}
|
|
1342
1358
|
</div>
|
|
1343
1359
|
<p class="ocp-footnote">${kit.te('settings.redesign.opencode.snapshotDetail')}</p>`,
|
|
@@ -1663,19 +1679,24 @@ function buildSkillsTab(ccSkills) {
|
|
|
1663
1679
|
const row = (skill) => {
|
|
1664
1680
|
const installed = skill.installedTargets || { claude: !!skill.installed, codex: false, opencode: false };
|
|
1665
1681
|
const anyOn = targets.some(t => installed[t.id]);
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1682
|
+
// What each runtime has of it: a native skill folder, an old bridge not moved yet, or a folder of that name
|
|
1683
|
+
// SynaBun did not install (settings/settings-skill-folders.js chipState).
|
|
1684
|
+
const chips = targets.map(t => {
|
|
1685
|
+
const chip = chipState(skill.targetStates?.[t.id], installed[t.id]);
|
|
1686
|
+
return `
|
|
1687
|
+
<button type="button" class="cc-skill-target${chip.on ? ' on' : ''}"
|
|
1688
|
+
data-cc-skill="${kit.esc(skill.dirName)}"
|
|
1669
1689
|
data-cc-target="${t.id}"
|
|
1670
|
-
data-state="${
|
|
1671
|
-
${kit.inv('SKL001')} data-stg-pressed="on" aria-pressed="${
|
|
1672
|
-
title="${kit.te(
|
|
1673
|
-
|
|
1690
|
+
data-state="${chip.on ? 'on' : 'off'}" data-install-state="${chip.state}"
|
|
1691
|
+
${kit.inv('SKL001')} data-stg-pressed="on" aria-pressed="${chip.on ? 'true' : 'false'}"
|
|
1692
|
+
title="${kit.te(chip.key, { skill: skill.name, assistant: t.label })}">${t.label}</button>
|
|
1693
|
+
`;
|
|
1694
|
+
}).join('');
|
|
1674
1695
|
return `
|
|
1675
|
-
<div class="cc-skill-row${anyOn ? ' installed' : ''}" data-skill-name="${skill.dirName}">
|
|
1696
|
+
<div class="cc-skill-row${anyOn ? ' installed' : ''}" data-skill-name="${kit.esc(skill.dirName)}">
|
|
1676
1697
|
<div class="cc-skill-info">
|
|
1677
|
-
<span class="cc-skill-name">/${skill.name}</span>
|
|
1678
|
-
<span class="cc-skill-desc">${skill.description || ''}</span>
|
|
1698
|
+
<span class="cc-skill-name">/${kit.esc(skill.name)}</span>
|
|
1699
|
+
<span class="cc-skill-desc">${kit.esc(skill.description || '')}</span>
|
|
1679
1700
|
</div>
|
|
1680
1701
|
<div class="cc-skill-targets" role="group" aria-label="${kit.te('settings.redesign.skills.targets', { skill: skill.name })}">${chips}</div>
|
|
1681
1702
|
</div>`;
|
|
@@ -1685,10 +1706,14 @@ function buildSkillsTab(ccSkills) {
|
|
|
1685
1706
|
<div class="stg-pane" data-stg-pane="skills">
|
|
1686
1707
|
${kit.card({
|
|
1687
1708
|
id: 'stg-sec-skills', section: 'tool_access.skills',
|
|
1688
|
-
body: skills.length
|
|
1709
|
+
body: (skills.length
|
|
1689
1710
|
? `<div ${kit.inv('SKL004')} role="group" aria-label="${kit.L('SKL004')}">${skills.map(row).join('')}</div>
|
|
1690
1711
|
${kit.help('SKL001')}`
|
|
1691
|
-
: `<div class="stg-help" style="margin-top:0">${kit.te('settings.redesign.skills.none')}</div
|
|
1712
|
+
: `<div class="stg-help" style="margin-top:0">${kit.te('settings.redesign.skills.none')}</div>`)
|
|
1713
|
+
// Skills Studio is where a skill is edited, versioned and published; these chips stay for what SynaBun ships.
|
|
1714
|
+
+ `<div class="cc-skill-studio"><button type="button" class="stg-action-btn compact" data-cc-open-studio="1">${kit.te('settings.redesign.skills.openStudio')}</button>
|
|
1715
|
+
<span class="stg-help">${kit.te('settings.redesign.skills.openStudioHelp')}</span></div>`
|
|
1716
|
+
+ skillFoldersHTML(),
|
|
1692
1717
|
})}
|
|
1693
1718
|
</div>`;
|
|
1694
1719
|
}
|
|
@@ -1699,7 +1724,7 @@ function buildPermissionsTab(toolCategories, toolPermissions) {
|
|
|
1699
1724
|
// The inventory ids of the "all tools of this group" switches, by group.
|
|
1700
1725
|
const ALL_SWITCH = {
|
|
1701
1726
|
memory: 'PER001', browser: 'PER003', whiteboard: 'PER004', cards: 'PER005', automation: 'PER006',
|
|
1702
|
-
|
|
1727
|
+
images: 'PER008', gsc: 'PER009', youtube: 'PER010', thirdparty: 'PER011',
|
|
1703
1728
|
};
|
|
1704
1729
|
|
|
1705
1730
|
let totalOn = 0, totalAll = 0;
|
|
@@ -1819,6 +1844,52 @@ const userContextDigestHTML = (digest) => (digest.rows.length
|
|
|
1819
1844
|
? digest.rows.map(row => `<button type="button" class="uc-digest-row" role="listitem" data-uc-memory="${kit.esc(row.id)}" title="${kit.te('settings.redesign.hooks.uc.openMemory')}"><span class="uc-digest-statement">${kit.esc(row.statement)}</span><span class="uc-digest-facet">${kit.esc(row.facet)}${row.seed ? ` · ${kit.te('settings.redesign.hooks.uc.seed')}` : ''}</span></button>`).join('')
|
|
1820
1845
|
: `<div class="stg-help uc-digest-message" role="listitem" style="margin:0">${kit.esc(digest.message)}</div>`);
|
|
1821
1846
|
|
|
1847
|
+
/**
|
|
1848
|
+
* Put a view (describeClaudeHooks) on the section: the line above, the switch, its notes and Repair, and the
|
|
1849
|
+
* per-moment rows and their count in the Hooks card, which show nothing on or off that was not read.
|
|
1850
|
+
*/
|
|
1851
|
+
function drawClaudeHooks(overlay, view) {
|
|
1852
|
+
const section = overlay.querySelector('#stg-sec-automation-behavior');
|
|
1853
|
+
if (!section) return;
|
|
1854
|
+
const note = section.querySelector('#ch-status-note');
|
|
1855
|
+
if (note) { note.hidden = !view.note; note.textContent = view.note?.text || ''; note.dataset.chState = view.state; }
|
|
1856
|
+
const rowBox = section.querySelector('#ch-row');
|
|
1857
|
+
if (rowBox) { rowBox.dataset.chReady = view.ready ? '1' : '0'; rowBox.dataset.chState = view.state; }
|
|
1858
|
+
const toggle = section.querySelector('.cc-toggle[data-ch-master]');
|
|
1859
|
+
if (toggle) {
|
|
1860
|
+
toggle.classList.toggle('on', view.on);
|
|
1861
|
+
toggle.setAttribute('aria-pressed', view.on ? 'true' : 'false');
|
|
1862
|
+
toggle.disabled = view.disabled;
|
|
1863
|
+
toggle.setAttribute('aria-describedby', `ch-master-help ch-notes${view.note ? ' ch-status-note' : ''}`);
|
|
1864
|
+
const item = toggle.closest('.cc-integration-item');
|
|
1865
|
+
item?.classList.toggle('enabled', view.on);
|
|
1866
|
+
item?.classList.toggle('uc-disabled', view.disabled);
|
|
1867
|
+
}
|
|
1868
|
+
const notes = section.querySelector('#ch-notes');
|
|
1869
|
+
if (notes) notes.innerHTML = userContextNotesHTML(view.notes);
|
|
1870
|
+
const repair = section.querySelector('#ch-repair');
|
|
1871
|
+
if (repair) repair.hidden = !(view.repair && !view.disabled);
|
|
1872
|
+
const applies = section.querySelector('#ch-applies');
|
|
1873
|
+
if (applies) applies.textContent = view.applies;
|
|
1874
|
+
const locked = !view.ready || view.disabled;
|
|
1875
|
+
for (const row of CLAUDE_HOOK_ROWS) {
|
|
1876
|
+
const rowToggle = section.querySelector(`.cc-toggle[data-cc-hook="${row.key}"]`);
|
|
1877
|
+
if (!rowToggle) continue;
|
|
1878
|
+
const on = !!view.rows?.[row.key];
|
|
1879
|
+
rowToggle.classList.toggle('on', on);
|
|
1880
|
+
rowToggle.setAttribute('aria-pressed', on ? 'true' : 'false');
|
|
1881
|
+
rowToggle.disabled = locked;
|
|
1882
|
+
const item = rowToggle.closest('.cc-integration-item');
|
|
1883
|
+
item?.classList.toggle('enabled', on);
|
|
1884
|
+
item?.classList.toggle('uc-disabled', locked);
|
|
1885
|
+
}
|
|
1886
|
+
const badge = section.querySelector('#cc-hooks-badge');
|
|
1887
|
+
if (badge) {
|
|
1888
|
+
badge.textContent = view.ready ? `${view.onRows}/${view.totalRows}` : '—';
|
|
1889
|
+
badge.classList.toggle('all-on', view.ready && view.onRows === view.totalRows);
|
|
1890
|
+
}
|
|
1891
|
+
}
|
|
1892
|
+
|
|
1822
1893
|
/** Put a view (describeUserContext) on the section: the note, each switch and its notes, the digest. */
|
|
1823
1894
|
function drawUserContext(overlay, view) {
|
|
1824
1895
|
const section = overlay.querySelector('#stg-sec-automation-behavior');
|
|
@@ -1895,16 +1966,19 @@ function tunnelExtraHtml(view, mcpKeyInfo) {
|
|
|
1895
1966
|
].filter(Boolean).join('');
|
|
1896
1967
|
}
|
|
1897
1968
|
|
|
1898
|
-
function buildConnectionsTab(ccIntegrations, ccSkills, tunnelStatus, mcpKeyInfo, openclawBridge, greeting, toolPermissions, toolCategories, userContext = null) {
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
const
|
|
1902
|
-
const
|
|
1903
|
-
const
|
|
1904
|
-
const
|
|
1905
|
-
const
|
|
1906
|
-
const
|
|
1907
|
-
const
|
|
1969
|
+
function buildConnectionsTab(ccIntegrations, ccSkills, tunnelStatus, mcpKeyInfo, openclawBridge, greeting, toolPermissions, toolCategories, userContext = null, workerGuard = null, claudeHooks = null) {
|
|
1970
|
+
// SynaBun's Claude Code hooks (settings-claude-hooks.js): the switch and the rows below show what is really in the
|
|
1971
|
+
// settings files, and nothing (disabled, with the reason) when that could not be read: never "off" by default.
|
|
1972
|
+
const ch = claudeHooks || describeClaudeHooks(null, { tx: kit.tx });
|
|
1973
|
+
const rowOn = (key) => !!ch.rows?.[key];
|
|
1974
|
+
const ssOn = rowOn('SessionStart');
|
|
1975
|
+
const psOn = rowOn('UserPromptSubmit');
|
|
1976
|
+
const pcOn = rowOn('PreCompact');
|
|
1977
|
+
const stOn = rowOn('Stop');
|
|
1978
|
+
const prOn = rowOn('PreToolUse');
|
|
1979
|
+
const ptOn = rowOn('PostToolUse');
|
|
1980
|
+
const subStartOn = rowOn('SubagentStart');
|
|
1981
|
+
const subStopOn = rowOn('SubagentStop');
|
|
1908
1982
|
const hf = ccIntegrations.hookFeatures || {};
|
|
1909
1983
|
const cmOn = hf.conversationMemory !== false;
|
|
1910
1984
|
const ulOn = hf.userLearning !== false;
|
|
@@ -1921,7 +1995,8 @@ function buildConnectionsTab(ccIntegrations, ccSkills, tunnelStatus, mcpKeyInfo,
|
|
|
1921
1995
|
{ key: 'SubagentStart', on: subStartOn, inv: 'AUT034' }, { key: 'SubagentStop', on: subStopOn, inv: 'AUT035' },
|
|
1922
1996
|
];
|
|
1923
1997
|
const onCount = hookRows.filter(r => r.on).length;
|
|
1924
|
-
const allOn = onCount === hookRows.length;
|
|
1998
|
+
const allOn = ch.ready && onCount === hookRows.length;
|
|
1999
|
+
const hookRowsLocked = !ch.ready || ch.disabled;
|
|
1925
2000
|
|
|
1926
2001
|
const copyIcon = COPY_ICON;
|
|
1927
2002
|
|
|
@@ -2046,6 +2121,44 @@ function buildConnectionsTab(ccIntegrations, ccSkills, tunnelStatus, mcpKeyInfo,
|
|
|
2046
2121
|
<button type="button" class="conn-add-btn" id="uc-digest-all" ${kit.inv('AUT057')} title="${kit.H('AUT057')}" style="margin:0;width:auto;padding:4px 10px;border-style:solid">${kit.L('AUT057')}</button>
|
|
2047
2122
|
</div>`;
|
|
2048
2123
|
|
|
2124
|
+
// Protect workers (lib/worker-guard-setting.js): what capability read-only / workspace means for every worker the
|
|
2125
|
+
// Assistant dispatches. `workerGuard` is the view of what the server answered (describeWorkerGuard): a server
|
|
2126
|
+
// started before the setting shows the default, disabled, under a line that says a restart is missing.
|
|
2127
|
+
const wg = workerGuard || describeWorkerGuard(null, { tx: kit.tx });
|
|
2128
|
+
const workerGuardBlock = `
|
|
2129
|
+
<div class="cc-hook-toggles" id="wg-row" data-wg-ready="${wg.ready ? '1' : '0'}">
|
|
2130
|
+
<div class="cc-integration-item enabled${wg.disabled ? ' uc-disabled' : ''}" data-wg-row>
|
|
2131
|
+
<div class="cc-integration-info">
|
|
2132
|
+
<div class="cc-integration-label" id="wg-mode-label">${kit.L('AUT061')}</div>
|
|
2133
|
+
<div class="cc-integration-path" id="wg-mode-help">${kit.H('AUT061')}</div>
|
|
2134
|
+
<div class="uc-layer-notes" id="wg-notes" data-stg-data>${userContextNotesHTML(wg.note ? [wg.note] : [])}</div>
|
|
2135
|
+
<div class="stg-help" id="wg-mode-detail" data-stg-data style="margin:4px 0 0">${kit.esc(wg.options.find(o => o.value === wg.mode)?.help || '')}</div>
|
|
2136
|
+
</div>
|
|
2137
|
+
${kit.dropdown('wg-mode', wg.options.map(o => ({ value: o.value, label: kit.esc(o.label) })), { value: wg.mode, classes: 'stg-dropdown stg-dropdown-inline', label: kit.L('AUT061'), attrs: `${kit.inv('AUT061')} aria-describedby="wg-mode-help wg-notes"`, disabled: wg.disabled })}
|
|
2138
|
+
</div>
|
|
2139
|
+
</div>
|
|
2140
|
+
<div class="stg-help uc-save-status" id="wg-save-status" role="alert" data-stg-data hidden></div>
|
|
2141
|
+
<div class="stg-help" id="wg-applies">${kit.esc(wg.applies)}</div>`;
|
|
2142
|
+
|
|
2143
|
+
// SynaBun's hooks in Claude Code: one switch for all of them (POST / DELETE /api/claude-code/integrations, target
|
|
2144
|
+
// global), what is really installed under it, and Repair when the files are out of step. Outside the collapsed
|
|
2145
|
+
// card below, so it can be found; the per-moment rows stay in that card.
|
|
2146
|
+
const claudeHooksBlock = `
|
|
2147
|
+
<div class="stg-help uc-status-note" id="ch-status-note" role="status" data-ch-state="${ch.state}" data-stg-data${ch.note ? '' : ' hidden'}>${ch.note ? kit.esc(ch.note.text) : ''}</div>
|
|
2148
|
+
<div class="cc-hook-toggles" id="ch-row" data-ch-ready="${ch.ready ? '1' : '0'}" data-ch-state="${ch.state}">
|
|
2149
|
+
<div class="cc-integration-item${ch.on ? ' enabled' : ''}${ch.disabled ? ' uc-disabled' : ''}" data-ch-master-row>
|
|
2150
|
+
<div class="cc-integration-info">
|
|
2151
|
+
<div class="cc-integration-label" id="ch-master-label">${kit.L('AUT062')}</div>
|
|
2152
|
+
<div class="cc-integration-path" id="ch-master-help">${kit.H('AUT062')}</div>
|
|
2153
|
+
<div class="uc-layer-notes" id="ch-notes" data-stg-data>${userContextNotesHTML(ch.notes)}</div>
|
|
2154
|
+
<button type="button" class="cc-copy-btn stg-text-btn ch-repair" id="ch-repair" title="${kit.te('settings.redesign.hooks.claudeHooks.repair.title')}"${ch.repair && !ch.disabled ? '' : ' hidden'}>${kit.te('settings.redesign.hooks.claudeHooks.repair.action')}</button>
|
|
2155
|
+
</div>
|
|
2156
|
+
<button type="button" class="cc-toggle${ch.on ? ' on' : ''}" data-ch-master ${kit.inv('AUT062')} aria-pressed="${ch.on ? 'true' : 'false'}" aria-labelledby="ch-master-label" aria-describedby="ch-master-help ch-notes${ch.note ? ' ch-status-note' : ''}"${ch.disabled ? ' disabled' : ''}></button>
|
|
2157
|
+
</div>
|
|
2158
|
+
</div>
|
|
2159
|
+
<div class="stg-help uc-save-status" id="ch-save-status" role="alert" data-stg-data hidden></div>
|
|
2160
|
+
<div class="stg-help uc-applies" id="ch-applies">${kit.esc(ch.applies)}</div>`;
|
|
2161
|
+
|
|
2049
2162
|
return `
|
|
2050
2163
|
<div class="stg-pane" data-stg-pane="hooks">
|
|
2051
2164
|
|
|
@@ -2060,11 +2173,16 @@ function buildConnectionsTab(ccIntegrations, ccSkills, tunnelStatus, mcpKeyInfo,
|
|
|
2060
2173
|
body: `
|
|
2061
2174
|
${userContextBlock}
|
|
2062
2175
|
|
|
2176
|
+
${workerGuardBlock}
|
|
2177
|
+
|
|
2178
|
+
${claudeHooksBlock}
|
|
2179
|
+
|
|
2063
2180
|
${kit.card({
|
|
2064
2181
|
id: 'stg-sec-hooks', level: 'h4', collapsible: true, collapsed: true, advanced: true, attrs: 'data-cc-target="global"',
|
|
2065
2182
|
title: h('hooksTitle'), purpose: h('hooksPurpose'),
|
|
2066
2183
|
// These hooks are Claude Code's own (~/.claude/settings.json, hooks/claude-code): no other assistant runs them.
|
|
2067
|
-
|
|
2184
|
+
// A count only from a state that was read: "—" otherwise, never "0/8".
|
|
2185
|
+
status: `<span class="cc-hooks-badge${allOn ? ' all-on' : ''}" id="cc-hooks-badge">${ch.ready ? `${onCount}/${hookRows.length}` : '—'}</span>${providerBadge({ cli: true, vscode: true, web: false, cowork: false, codex: false })}`,
|
|
2068
2186
|
body: `
|
|
2069
2187
|
<div class="stg-help" style="margin:0 0 10px">${h('claudeOnly')}</div>
|
|
2070
2188
|
<div class="cc-hook-toggles">
|
|
@@ -2083,12 +2201,12 @@ function buildConnectionsTab(ccIntegrations, ccSkills, tunnelStatus, mcpKeyInfo,
|
|
|
2083
2201
|
</div>
|
|
2084
2202
|
<div class="cc-hook-toggles" style="margin-top:10px">
|
|
2085
2203
|
${hookRows.map(r => `
|
|
2086
|
-
<div class="cc-integration-item${r.on ? ' enabled' : ''}" data-hook="${r.key}">
|
|
2204
|
+
<div class="cc-integration-item${r.on ? ' enabled' : ''}${hookRowsLocked ? ' uc-disabled' : ''}" data-hook="${r.key}">
|
|
2087
2205
|
<div class="cc-integration-info">
|
|
2088
2206
|
<div class="cc-integration-label" id="cc-hook-${r.key}-label">${kit.L(r.inv)}</div>
|
|
2089
2207
|
<div class="cc-integration-path" id="cc-hook-${r.key}-help">${kit.H(r.inv)}</div>
|
|
2090
2208
|
</div>
|
|
2091
|
-
<button type="button" class="cc-toggle${r.on ? ' on' : ''}" data-cc-hook="${r.key}" data-cc-scope="global" ${kit.inv(r.inv)} aria-labelledby="cc-hook-${r.key}-label" aria-describedby="cc-hook-${r.key}-help"></button>
|
|
2209
|
+
<button type="button" class="cc-toggle${r.on ? ' on' : ''}" data-cc-hook="${r.key}" data-cc-scope="global" ${kit.inv(r.inv)} aria-pressed="${r.on ? 'true' : 'false'}" aria-labelledby="cc-hook-${r.key}-label" aria-describedby="cc-hook-${r.key}-help"${hookRowsLocked ? ' disabled' : ''}></button>
|
|
2092
2210
|
</div>`).join('')}
|
|
2093
2211
|
</div>`,
|
|
2094
2212
|
})}`,
|
|
@@ -3168,10 +3286,45 @@ function landOnSearchEntry(overlay, entry, { focus = false, watch = true } = {})
|
|
|
3168
3286
|
}
|
|
3169
3287
|
|
|
3170
3288
|
// ═══════════════════════════════════════════
|
|
3289
|
+
// The search index of a locale (and of the variant tabs the page registered): what the strings are, not what
|
|
3290
|
+
// the panes hold, so it is made once (at launch idle, or by the first open) and every open reuses it.
|
|
3291
|
+
let _searchIndexMemo = null;
|
|
3292
|
+
function memoSearchIndex(en, variantTabs, build) {
|
|
3293
|
+
const key = JSON.stringify([getLocale(), typeof en === 'function', (variantTabs || []).map(tab => [tab.id, tab.label || ''])]);
|
|
3294
|
+
if (_searchIndexMemo?.key !== key) _searchIndexMemo = { key, index: build() };
|
|
3295
|
+
return _searchIndexMemo.index;
|
|
3296
|
+
}
|
|
3297
|
+
|
|
3298
|
+
/** Make the index now (launch idle), with the English names when the locale is another (loaded once, here). */
|
|
3299
|
+
function prebuildSettingsSearchIndex() {
|
|
3300
|
+
const variantTabs = getSettingsTabs();
|
|
3301
|
+
const make = (en) => memoSearchIndex(en, variantTabs, () => buildSettingsSearchIndex({ t: kit.tx, en, variants: variantTabs.map(tab => ({ id: tab.id, label: tab.label })) }));
|
|
3302
|
+
if (getLocale() === 'en') { make(null); return undefined; }
|
|
3303
|
+
const english = englishTranslator();
|
|
3304
|
+
if (english) { make(english); return undefined; }
|
|
3305
|
+
make(null);
|
|
3306
|
+
return loadEnglishTranslator().then((en) => { if (en) make(en); }).catch(() => {});
|
|
3307
|
+
}
|
|
3308
|
+
|
|
3171
3309
|
// MAIN ENTRY — openSettingsModal
|
|
3172
3310
|
// ═══════════════════════════════════════════
|
|
3173
3311
|
|
|
3174
|
-
|
|
3312
|
+
// An open that comes while another is still reading its data joins it: one panel, the later options applied.
|
|
3313
|
+
let _settingsOpening = null;
|
|
3314
|
+
export function openSettingsModal(options = {}) {
|
|
3315
|
+
if (_settingsOpening) {
|
|
3316
|
+
return _settingsOpening.then(() => {
|
|
3317
|
+
const panel = document.getElementById('settings-panel');
|
|
3318
|
+
if (panel) { panel.style.zIndex = '300001'; applySettingsOpenOptions(panel, options); }
|
|
3319
|
+
});
|
|
3320
|
+
}
|
|
3321
|
+
const opening = openSettingsModalNow(options);
|
|
3322
|
+
const guarded = opening.catch(() => {}).finally(() => { if (_settingsOpening === guarded) _settingsOpening = null; });
|
|
3323
|
+
_settingsOpening = guarded;
|
|
3324
|
+
return opening;
|
|
3325
|
+
}
|
|
3326
|
+
|
|
3327
|
+
async function openSettingsModalNow(options = {}) {
|
|
3175
3328
|
// If already open, just bring it to front
|
|
3176
3329
|
const existing = document.getElementById('settings-panel');
|
|
3177
3330
|
if (existing) {
|
|
@@ -3205,33 +3358,32 @@ export async function openSettingsModal(options = {}) {
|
|
|
3205
3358
|
// The Assistant's user context (lib/assistant-user-context.js): what its endpoint answered, as a reading.
|
|
3206
3359
|
// No answer is a reading too: the section says what is true instead of showing switches that do nothing.
|
|
3207
3360
|
let userContextReading = readUserContextResponse({ status: 0 });
|
|
3361
|
+
// Protect workers (lib/worker-guard-setting.js): what its endpoint answered, as a reading.
|
|
3362
|
+
let workerGuardReading = readWorkerGuardResponse({ status: 0 });
|
|
3363
|
+
// SynaBun's Claude Code hooks (lib/claude-hooks-status.js): the `claudeHooks` of the integrations read, as a reading.
|
|
3364
|
+
let claudeHooksReading = readClaudeHooksResponse({ status: 0 });
|
|
3208
3365
|
|
|
3209
3366
|
try {
|
|
3210
|
-
|
|
3211
|
-
|
|
3212
|
-
|
|
3213
|
-
|
|
3214
|
-
|
|
3215
|
-
|
|
3216
|
-
|
|
3217
|
-
|
|
3218
|
-
|
|
3219
|
-
|
|
3220
|
-
|
|
3221
|
-
|
|
3222
|
-
fetch('/api/claude-code/tool-categories').then(r => r.json()),
|
|
3223
|
-
fetch('/api/discord/config').then(r => r.json()),
|
|
3224
|
-
fetch('/api/skins').then(r => r.json()),
|
|
3225
|
-
fetch('/api/file-icons').then(r => r.json()),
|
|
3226
|
-
fetch('/api/youtube/config').then(r => r.json()),
|
|
3227
|
-
fetch('/api/typesafe/config').then(r => r.json()),
|
|
3228
|
-
loadUserContextReading(),
|
|
3229
|
-
]);
|
|
3367
|
+
// Read through the page's data cache (settings/settings-data.js): kept answers at once (each open its own
|
|
3368
|
+
// copy; read again in the background when old), so Settings waits only the first time.
|
|
3369
|
+
const reads = await readSettingsData();
|
|
3370
|
+
// The greeting and the user context are readings of what their endpoints answered (no answer is a reading too).
|
|
3371
|
+
const readingOf = (entry, make) => ({ status: 'fulfilled', value: entry?.status === 'fulfilled' ? make(entry.value) : make({ status: 0, error: entry?.reason?.message }) });
|
|
3372
|
+
const greetRes = readingOf(reads.greeting, readGreetingStatus);
|
|
3373
|
+
const userContextRes = readingOf(reads.userContext, readUserContextResponse);
|
|
3374
|
+
const workerGuardRes = readingOf(reads.workerGuard, readWorkerGuardResponse);
|
|
3375
|
+
const settingsRes = reads.settings, connRes = reads.connections, ccRes = reads.integrations, skillsRes = reads.skills;
|
|
3376
|
+
const tunnelRes = reads.tunnel, keyRes = reads.mcpKey, bridgeRes = reads.openclaw, setupRes = reads.setup, cliRes = reads.cli;
|
|
3377
|
+
const toolPermsRes = reads.toolPermissions, toolCatsRes = reads.toolCategories, discordRes = reads.discord, skinsRes = reads.skins;
|
|
3378
|
+
const iconsRes = reads.icons, youtubeRes = reads.youtube, typesafeRes = reads.typesafe;
|
|
3230
3379
|
if (typesafeRes.status === 'fulfilled' && typesafeRes.value.ok) typesafeConfig = typesafeRes.value;
|
|
3231
3380
|
if (userContextRes.status === 'fulfilled') userContextReading = userContextRes.value;
|
|
3381
|
+
if (workerGuardRes.status === 'fulfilled') workerGuardReading = workerGuardRes.value;
|
|
3232
3382
|
if (settingsRes.status === 'fulfilled') settings = settingsRes.value;
|
|
3233
3383
|
if (connRes.status === 'fulfilled' && connRes.value.connections) connections = connRes.value.connections;
|
|
3234
|
-
|
|
3384
|
+
// Read as it came (status and body): a refusal or an old server is said as what it is, never as hooks that are off.
|
|
3385
|
+
if (ccRes.status === 'fulfilled' && ccRes.value?.status >= 200 && ccRes.value.status < 300 && ccRes.value.body?.ok) ccIntegrations = ccRes.value.body;
|
|
3386
|
+
claudeHooksReading = readingOf(ccRes, readClaudeHooksResponse).value;
|
|
3235
3387
|
if (skillsRes.status === 'fulfilled' && skillsRes.value.ok) ccSkills = skillsRes.value.skills || [];
|
|
3236
3388
|
if (tunnelRes.status === 'fulfilled' && tunnelRes.value.ok) tunnelStatus = tunnelRes.value;
|
|
3237
3389
|
if (keyRes.status === 'fulfilled' && keyRes.value.ok) mcpKeyInfo = keyRes.value;
|
|
@@ -3256,15 +3408,18 @@ export async function openSettingsModal(options = {}) {
|
|
|
3256
3408
|
|| getSettingsPage(storage.getItem(KEYS.SETTINGS_LAST_PAGE))?.id
|
|
3257
3409
|
|| DEFAULT_SETTINGS_PAGE;
|
|
3258
3410
|
const openerEl = document.activeElement;
|
|
3411
|
+
// Listeners on document and window that belong to this panel end with it (close aborts it).
|
|
3412
|
+
const panelLife = new AbortController();
|
|
3259
3413
|
|
|
3260
3414
|
// ── Panel ──
|
|
3261
3415
|
const overlay = document.createElement('div');
|
|
3262
3416
|
overlay.className = 'settings-panel glass resizable';
|
|
3263
3417
|
overlay.id = 'settings-panel';
|
|
3264
3418
|
|
|
3265
|
-
// Always open centered at default size
|
|
3266
|
-
|
|
3267
|
-
overlay.style.
|
|
3419
|
+
// Always open centered at default size, in the app frame
|
|
3420
|
+
const at = centeredInFrame(900, 650);
|
|
3421
|
+
overlay.style.left = at.left + 'px';
|
|
3422
|
+
overlay.style.top = at.top + 'px';
|
|
3268
3423
|
|
|
3269
3424
|
// ── Assemble HTML ──
|
|
3270
3425
|
overlay.innerHTML = `
|
|
@@ -3297,7 +3452,7 @@ export async function openSettingsModal(options = {}) {
|
|
|
3297
3452
|
<div class="settings-content">
|
|
3298
3453
|
${buildPagesHTML({
|
|
3299
3454
|
server: buildServerTab(settings, connections),
|
|
3300
|
-
hooks: buildConnectionsTab(ccIntegrations, ccSkills, tunnelStatus, mcpKeyInfo, openclawBridge, describeGreeting(greetingReading, null, { tx: kit.tx, t: kit.tx }), toolPermissions, toolCategories, describeUserContext(userContextReading, { tx: kit.tx })),
|
|
3455
|
+
hooks: buildConnectionsTab(ccIntegrations, ccSkills, tunnelStatus, mcpKeyInfo, openclawBridge, describeGreeting(greetingReading, null, { tx: kit.tx, t: kit.tx }), toolPermissions, toolCategories, describeUserContext(userContextReading, { tx: kit.tx }), describeWorkerGuard(workerGuardReading, { tx: kit.tx }), describeClaudeHooks(claudeHooksReading, { tx: kit.tx })),
|
|
3301
3456
|
terminal: buildTerminalTab(cliConfig),
|
|
3302
3457
|
opencode: buildOpencodeTab(),
|
|
3303
3458
|
notifications: buildNotificationsTab(),
|
|
@@ -3320,6 +3475,7 @@ export async function openSettingsModal(options = {}) {
|
|
|
3320
3475
|
skins: buildSkinsTab(skinsData.skins, skinsData.active),
|
|
3321
3476
|
interface: buildInterfaceTab(),
|
|
3322
3477
|
icons: buildIconsTab(customIconsData),
|
|
3478
|
+
app: buildAppTab(),
|
|
3323
3479
|
about: buildAboutTab(),
|
|
3324
3480
|
}, variantTabs, initialPage)}
|
|
3325
3481
|
</div>
|
|
@@ -3361,10 +3517,14 @@ export async function openSettingsModal(options = {}) {
|
|
|
3361
3517
|
// ── Close helper ──
|
|
3362
3518
|
const close = () => {
|
|
3363
3519
|
document.removeEventListener('keydown', onSettingsEsc);
|
|
3520
|
+
const first = !panelLife.signal.aborted;
|
|
3521
|
+
panelLife.abort();
|
|
3364
3522
|
// Tabs that poll (Judgments) clear their timers on this event.
|
|
3365
3523
|
overlay.dispatchEvent(new CustomEvent('settings-close'));
|
|
3366
3524
|
backdrop.remove();
|
|
3367
3525
|
overlay.remove();
|
|
3526
|
+
// What was changed here is what the next open shows: its reads are asked for again now, in the background.
|
|
3527
|
+
if (first) refreshSettingsData();
|
|
3368
3528
|
// Give the keyboard back to whatever opened Settings.
|
|
3369
3529
|
if (openerEl && openerEl !== document.body && document.body.contains(openerEl)) { try { openerEl.focus({ preventScroll: true }); } catch {} }
|
|
3370
3530
|
};
|
|
@@ -3525,12 +3685,12 @@ export async function openSettingsModal(options = {}) {
|
|
|
3525
3685
|
+ '</div>';
|
|
3526
3686
|
html += '<div style="padding:4px 0">';
|
|
3527
3687
|
if (data.parent) {
|
|
3528
|
-
html += `<div class="cc-browse-item" data-path="${
|
|
3688
|
+
html += `<div class="cc-browse-item" data-path="${kit.esc(data.parent)}" style="padding:4px 10px;cursor:pointer;font-size:12px;display:flex;align-items:center;gap:6px;color:var(--t-muted)">`
|
|
3529
3689
|
+ '<svg viewBox="0 0 24 24" style="width:13px;height:13px;fill:none;stroke:currentColor;stroke-width:2"><polyline points="15 18 9 12 15 6"/></svg>'
|
|
3530
3690
|
+ '' + kit.te('settings.redesign.server.parent') + '</div>';
|
|
3531
3691
|
}
|
|
3532
3692
|
for (const d of data.directories) {
|
|
3533
|
-
html += `<div class="cc-browse-item" data-path="${
|
|
3693
|
+
html += `<div class="cc-browse-item" data-path="${kit.esc(data.current + '/' + d)}" style="padding:4px 10px;cursor:pointer;font-size:12px;display:flex;align-items:center;gap:6px">`
|
|
3534
3694
|
+ '<svg viewBox="0 0 24 24" style="width:13px;height:13px;fill:none;stroke:currentColor;stroke-width:2"><path d="M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z"/></svg>'
|
|
3535
3695
|
+ escapeHtml(d) + '</div>';
|
|
3536
3696
|
}
|
|
@@ -3914,7 +4074,7 @@ export async function openSettingsModal(options = {}) {
|
|
|
3914
4074
|
container.querySelectorAll('.stg-dropdown.open').forEach(dd => {
|
|
3915
4075
|
if (!dd.contains(e.target)) dd.classList.remove('open');
|
|
3916
4076
|
});
|
|
3917
|
-
});
|
|
4077
|
+
}, { signal: panelLife.signal });
|
|
3918
4078
|
}
|
|
3919
4079
|
|
|
3920
4080
|
function syncStgDropdown(container, hiddenId) {
|
|
@@ -4410,6 +4570,9 @@ export async function openSettingsModal(options = {}) {
|
|
|
4410
4570
|
// ── Code editor: AI autocomplete (loads and saves itself) ──
|
|
4411
4571
|
wireCodeEditorTab(overlay);
|
|
4412
4572
|
|
|
4573
|
+
// ── App: updates, start at login, the window, maintenance (loads itself when its page is shown) ──
|
|
4574
|
+
wireAppTab(overlay, { toast: showCCToast });
|
|
4575
|
+
|
|
4413
4576
|
// ── About: version, links, system and dependencies (loads itself the first time its page is shown) ──
|
|
4414
4577
|
wireAboutTab(overlay, { toast: showCCToast });
|
|
4415
4578
|
|
|
@@ -4598,9 +4761,14 @@ export async function openSettingsModal(options = {}) {
|
|
|
4598
4761
|
});
|
|
4599
4762
|
const data = await res.json();
|
|
4600
4763
|
showPathAction(profileId, data);
|
|
4764
|
+
const refusal = profileId === 'opencode' && data.support ? describeOpencodeInstall(data.support, { th: kit.th, tx: kit.tx }) : null;
|
|
4601
4765
|
if (data.ok && data.found && data.path && data.state === 'broken') {
|
|
4602
4766
|
// Found, but it does not start: not put in the field, so a broken path is never saved by accident.
|
|
4603
4767
|
showCCToast(kit.tx('settings.redesign.terminal.detectionFailed', { message: data.error ? `${data.path}: ${data.error}` : data.path }));
|
|
4768
|
+
} else if (data.ok && data.found && data.path && refusal?.status === 'notSupported') {
|
|
4769
|
+
// An OpenCode SynaBun does not drive (OpenCode 1, say): put in the field, with the sentence that says why it will not start.
|
|
4770
|
+
input.value = data.path;
|
|
4771
|
+
showCCToast(refusal.text, 12000);
|
|
4604
4772
|
} else if (data.ok && data.found && data.path) {
|
|
4605
4773
|
input.value = data.path;
|
|
4606
4774
|
showCCToast(kit.tx('settings.redesign.terminal.found', { path: data.version ? `${data.path} (${data.version})` : data.path }));
|
|
@@ -4788,7 +4956,7 @@ export async function openSettingsModal(options = {}) {
|
|
|
4788
4956
|
|
|
4789
4957
|
// Re-poll when tab regains visibility (covers Permissions API gaps on Safari).
|
|
4790
4958
|
const visHandler = () => { if (!document.hidden) refreshNotifPermRow(); };
|
|
4791
|
-
document.addEventListener('visibilitychange', visHandler);
|
|
4959
|
+
document.addEventListener('visibilitychange', visHandler, { signal: panelLife.signal });
|
|
4792
4960
|
|
|
4793
4961
|
// Request permission
|
|
4794
4962
|
if (requestPermBtn) {
|
|
@@ -4858,9 +5026,15 @@ export async function openSettingsModal(options = {}) {
|
|
|
4858
5026
|
if (data.ok) {
|
|
4859
5027
|
const nowOn = !isOn;
|
|
4860
5028
|
toggleBtn.classList.toggle('on');
|
|
5029
|
+
toggleBtn.setAttribute('aria-pressed', nowOn ? 'true' : 'false');
|
|
4861
5030
|
const row = toggleBtn.closest('.cc-integration-item');
|
|
4862
5031
|
if (row) row.classList.toggle('enabled', nowOn);
|
|
4863
|
-
if (target === 'global') {
|
|
5032
|
+
if (target === 'global') {
|
|
5033
|
+
updateGlobalHookBadge();
|
|
5034
|
+
// The answer's state of every hook (a server from before it sends none): the switch above follows it.
|
|
5035
|
+
if (data.claudeHooks) { claudeHooksReading = readClaudeHooksResponse({ status: res.status, body: { ok: true, claudeHooks: data.claudeHooks } }); redrawClaudeHooks(); }
|
|
5036
|
+
refreshSettingsData(['integrations']);
|
|
5037
|
+
}
|
|
4864
5038
|
else { toggleBtn.textContent = nowOn ? kit.tx('settings.redesign.hooks.enabled') : kit.tx('settings.redesign.hooks.enable'); if (panel) panel.classList.toggle('enabled'); const badge = panel?.querySelector('.cc-panel-status'); if (badge) { badge.textContent = nowOn ? kit.tx('settings.redesign.hooks.active') : kit.tx('settings.redesign.hooks.off'); badge.className = 'cc-panel-status ' + (nowOn ? 'active' : 'inactive'); } updateProjectCount(); }
|
|
4865
5039
|
} else { alert(data.error || kit.tx('settings.redesign.hooks.failedToToggleHook')); }
|
|
4866
5040
|
} catch (err) { alert(kit.tx('settings.redesign.hooks.failed2', { message: err.message })); }
|
|
@@ -4872,6 +5046,41 @@ export async function openSettingsModal(options = {}) {
|
|
|
4872
5046
|
toggle.addEventListener('click', () => { ccToggleHook('global', null, toggle, overlay.querySelector('.iface-section[data-cc-target="global"]'), toggle.dataset.ccHook); });
|
|
4873
5047
|
});
|
|
4874
5048
|
|
|
5049
|
+
// ── SynaBun hooks in Claude Code: one switch for all of them, and Repair ──
|
|
5050
|
+
// Each change is the server's: the row is drawn again from the state its answer carries (what the files hold now),
|
|
5051
|
+
// and a change that failed leaves the row as it was, with the reason under it. One change at a time.
|
|
5052
|
+
function redrawClaudeHooks() { drawClaudeHooks(overlay, describeClaudeHooks(claudeHooksReading, { tx: kit.tx })); }
|
|
5053
|
+
const setClaudeHooksSaveStatus = (text = '') => {
|
|
5054
|
+
const status = overlay.querySelector('#ch-save-status');
|
|
5055
|
+
if (status) { status.hidden = !text; status.textContent = text; }
|
|
5056
|
+
};
|
|
5057
|
+
let claudeHooksBusy = false;
|
|
5058
|
+
async function changeClaudeHooks(action) {
|
|
5059
|
+
if (claudeHooksBusy || claudeHooksReading.state !== 'ready') return;
|
|
5060
|
+
claudeHooksBusy = true;
|
|
5061
|
+
setClaudeHooksSaveStatus('');
|
|
5062
|
+
const row = overlay.querySelector('[data-ch-master-row]');
|
|
5063
|
+
if (row) row.style.opacity = '0.6';
|
|
5064
|
+
let failure = null;
|
|
5065
|
+
try {
|
|
5066
|
+
// On and Repair ask for the hooks to run (unblock: Claude Code's own disableAllHooks in the same file goes too).
|
|
5067
|
+
const payload = action === 'off' ? { target: 'global' } : { target: 'global', unblock: true };
|
|
5068
|
+
const res = await fetch('/api/claude-code/integrations', { method: action === 'off' ? 'DELETE' : 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(payload) });
|
|
5069
|
+
const body = await res.json().catch(() => null);
|
|
5070
|
+
if (res.ok && body?.ok) {
|
|
5071
|
+
claudeHooksReading = readClaudeHooksResponse({ status: res.status, body: { ok: true, claudeHooks: body.claudeHooks } });
|
|
5072
|
+
refreshSettingsData(['integrations']);
|
|
5073
|
+
} else failure = { status: res.status, body };
|
|
5074
|
+
} catch (err) { failure = { status: 0, error: err?.message }; }
|
|
5075
|
+
finally { claudeHooksBusy = false; if (row) row.style.opacity = ''; }
|
|
5076
|
+
redrawClaudeHooks();
|
|
5077
|
+
if (failure) setClaudeHooksSaveStatus(describeClaudeHooksSaveFailure({ action, ...failure }, { tx: kit.tx }));
|
|
5078
|
+
}
|
|
5079
|
+
overlay.querySelector('.cc-toggle[data-ch-master]')?.addEventListener('click', (e) => {
|
|
5080
|
+
changeClaudeHooks(e.currentTarget.classList.contains('on') ? 'off' : 'on');
|
|
5081
|
+
});
|
|
5082
|
+
overlay.querySelector('#ch-repair')?.addEventListener('click', () => changeClaudeHooks('repair'));
|
|
5083
|
+
|
|
4875
5084
|
// ── The Assistant's user context: layers and the digest preview ──
|
|
4876
5085
|
const ucRowsEl = overlay.querySelector('#uc-digest-rows');
|
|
4877
5086
|
const redrawUserContext = (reading = userContextReading) => drawUserContext(overlay, describeUserContext(reading, { tx: kit.tx }));
|
|
@@ -4898,6 +5107,47 @@ export async function openSettingsModal(options = {}) {
|
|
|
4898
5107
|
if (!link) return;
|
|
4899
5108
|
try { applySettingsOpenOptions(overlay, JSON.parse(link.dataset.ucGoto)); } catch { /* a malformed target goes nowhere */ }
|
|
4900
5109
|
});
|
|
5110
|
+
// Protect workers: the choice is saved at once and follows the server's answer; a save that failed puts the
|
|
5111
|
+
// dropdown back where it was, with the reason under it. Saves go one after the other, in the order of the picks.
|
|
5112
|
+
{
|
|
5113
|
+
const input = overlay.querySelector('#wg-mode');
|
|
5114
|
+
const dd = overlay.querySelector('.cc-dropdown[data-for="wg-mode"]');
|
|
5115
|
+
const status = overlay.querySelector('#wg-save-status');
|
|
5116
|
+
const detail = overlay.querySelector('#wg-mode-detail');
|
|
5117
|
+
const setStatus = (text) => { if (status) { status.textContent = text; status.hidden = !text; } };
|
|
5118
|
+
const showMode = (mode) => {
|
|
5119
|
+
if (input) input.value = mode;
|
|
5120
|
+
const view = describeWorkerGuard(workerGuardReading, { tx: kit.tx });
|
|
5121
|
+
const option = view.options.find(o => o.value === mode);
|
|
5122
|
+
dd?.querySelector('.cc-dropdown-value') && (dd.querySelector('.cc-dropdown-value').textContent = option?.label || mode);
|
|
5123
|
+
dd?.querySelectorAll('.cc-dropdown-item').forEach(item => item.classList.toggle('active', item.dataset.value === mode));
|
|
5124
|
+
if (detail) detail.textContent = option?.help || '';
|
|
5125
|
+
};
|
|
5126
|
+
let saves = Promise.resolve();
|
|
5127
|
+
input?.addEventListener('change', () => {
|
|
5128
|
+
if (workerGuardReading.state !== 'ready') return;
|
|
5129
|
+
const mode = String(input.value || '');
|
|
5130
|
+
if (!mode || mode === workerGuardReading.mode) { showMode(workerGuardReading.mode); return; }
|
|
5131
|
+
setStatus('');
|
|
5132
|
+
showMode(mode);
|
|
5133
|
+
if (dd) dd.style.opacity = '0.4';
|
|
5134
|
+
saves = saves.then(async () => {
|
|
5135
|
+
let failure = null;
|
|
5136
|
+
try {
|
|
5137
|
+
const res = await fetch('/api/assistant/worker-guard', { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ mode }) });
|
|
5138
|
+
const body = await res.json().catch(() => null);
|
|
5139
|
+
const reading = readWorkerGuardResponse({ status: res.status, body });
|
|
5140
|
+
if (res.ok && reading.state === 'ready') workerGuardReading = reading;
|
|
5141
|
+
else failure = { status: res.status, body };
|
|
5142
|
+
} catch (err) {
|
|
5143
|
+
failure = { status: 0, error: err?.message };
|
|
5144
|
+
}
|
|
5145
|
+
if (dd) dd.style.opacity = '';
|
|
5146
|
+
showMode(workerGuardReading.mode);
|
|
5147
|
+
if (failure) setStatus(describeWorkerGuardSaveFailure({ ...failure, mode }, { tx: kit.tx }));
|
|
5148
|
+
});
|
|
5149
|
+
});
|
|
5150
|
+
}
|
|
4901
5151
|
// The switch moves at once and follows the server's answer: the stored settings when the save went through,
|
|
4902
5152
|
// back where it was (with the reason next to the switches) when it did not. Saves go one after the other, in
|
|
4903
5153
|
// the order of the clicks, so a second click made before the first was answered is kept, not dropped.
|
|
@@ -5151,6 +5401,12 @@ export async function openSettingsModal(options = {}) {
|
|
|
5151
5401
|
btn.addEventListener('click', () => { const idx = btn.dataset.ccProjectToggle; const panel = overlay.querySelector(`.cc-panel[data-cc-idx="${idx}"]`); const projectPath = panel?.dataset.ccPath; if (projectPath) ccToggleHook('project', projectPath, btn, panel); });
|
|
5152
5402
|
});
|
|
5153
5403
|
|
|
5404
|
+
// Skills Studio, from Settings → Skills: Settings closes, the Studio opens on its Library.
|
|
5405
|
+
overlay.querySelector('[data-cc-open-studio]')?.addEventListener('click', () => {
|
|
5406
|
+
close();
|
|
5407
|
+
emit('skills:open', { place: 'library' });
|
|
5408
|
+
});
|
|
5409
|
+
|
|
5154
5410
|
// Skill target chips — one skill can be installed independently into Claude / Codex / OpenCode
|
|
5155
5411
|
const runtimeLabels = { claude: 'Claude Code', codex: 'Codex CLI', opencode: 'OpenCode CLI' };
|
|
5156
5412
|
overlay.querySelectorAll('.cc-skill-target[data-cc-skill]').forEach(chip => {
|
|
@@ -5160,28 +5416,48 @@ export async function openSettingsModal(options = {}) {
|
|
|
5160
5416
|
const isOn = chip.classList.contains('on');
|
|
5161
5417
|
try {
|
|
5162
5418
|
chip.style.opacity = '0.4'; chip.style.pointerEvents = 'none';
|
|
5163
|
-
const
|
|
5419
|
+
const send = (extra = {}) => fetch('/api/skills/install', {
|
|
5164
5420
|
method: isOn ? 'DELETE' : 'POST',
|
|
5165
5421
|
headers: { 'Content-Type': 'application/json' },
|
|
5166
|
-
body: JSON.stringify({ name: skillName, target }),
|
|
5167
|
-
});
|
|
5168
|
-
|
|
5422
|
+
body: JSON.stringify({ name: skillName, target, ...extra }),
|
|
5423
|
+
}).then((res) => res.json());
|
|
5424
|
+
let data = await send();
|
|
5425
|
+
// A skill of that name that SynaBun did not install, or that was changed since, is neither replaced nor
|
|
5426
|
+
// removed unasked: the server says what it is, and only a yes sends its digest back (a backup is kept).
|
|
5427
|
+
if (!data.ok && data.currentDigest && (data.code === 'skill_unowned' || data.code === 'skill_modified')) {
|
|
5428
|
+
if (!confirm(data.question || data.error)) return;
|
|
5429
|
+
data = await send({ expectedDestinationDigest: data.currentDigest });
|
|
5430
|
+
}
|
|
5169
5431
|
if (data.ok) {
|
|
5170
5432
|
chip.classList.toggle('on');
|
|
5171
5433
|
chip.dataset.state = isOn ? 'off' : 'on';
|
|
5434
|
+
// (a server from before the skill folders answers without `destination`: the chip then only says it is installed)
|
|
5435
|
+
const now = chipState(isOn ? null : (data.destination ? (data.shared ? { native: 'absent', shared: 'native' } : { native: 'native', bridge: !!data.bridge }) : undefined), !isOn);
|
|
5436
|
+
chip.dataset.installState = now.state;
|
|
5437
|
+
chip.setAttribute('aria-pressed', now.on ? 'true' : 'false');
|
|
5438
|
+
chip.title = kit.tx(now.key, { skill: skillName, assistant: chip.textContent.trim() });
|
|
5172
5439
|
const row = chip.closest('.cc-skill-row');
|
|
5173
5440
|
if (row) {
|
|
5174
5441
|
const anyOn = !!row.querySelector('.cc-skill-target.on');
|
|
5175
5442
|
row.classList.toggle('installed', anyOn);
|
|
5176
5443
|
}
|
|
5177
5444
|
const runtime = runtimeLabels[target] || target;
|
|
5178
|
-
|
|
5445
|
+
// The shared ~/.agents/skills is read by Codex and OpenCode both: the other chips of the skill follow.
|
|
5446
|
+
if (data.shared) refreshSkillChips(overlay);
|
|
5447
|
+
// An install is a native skill folder now; the server says when an old bridge of it is still there.
|
|
5448
|
+
if (isOn) showCCToast(kit.tx('settings.redesign.hooks.skillUninstalledRestartToApply', { runtime: runtime }));
|
|
5449
|
+
else if (data.destination) showCCToast(kit.tx(installedToast(data), { skill: skillName, runtime: runtime }), 6000);
|
|
5450
|
+
else showCCToast(kit.tx('settings.redesign.hooks.skillInstalledRestartToApply', { runtime: runtime }));
|
|
5179
5451
|
} else { alert(data.error || kit.tx('settings.redesign.hooks.failedToToggleSkill')); }
|
|
5180
5452
|
} catch (err) { alert(kit.tx('settings.redesign.hooks.failed2', { message: err.message })); }
|
|
5181
5453
|
finally { chip.style.opacity = ''; chip.style.pointerEvents = ''; }
|
|
5182
5454
|
});
|
|
5183
5455
|
});
|
|
5184
5456
|
|
|
5457
|
+
// Skill folders: the move of the old bridges to native skill folders, and the shared ~/.agents/skills switch.
|
|
5458
|
+
// After a move, an undo, or a change made anywhere else, the chips are read again in place.
|
|
5459
|
+
wireSkillFolders(overlay, { toast: (message, ms) => showCCToast(message, ms), onChanged: () => refreshSkillChips(overlay), signal: panelLife.signal });
|
|
5460
|
+
|
|
5185
5461
|
// ── Setup tab: per-provider MCP toggles, CLI copy, config copy, SynaBun rules ──
|
|
5186
5462
|
{
|
|
5187
5463
|
const checkIcon = '<svg viewBox="0 0 24 24" style="width:12px;height:12px"><polyline points="20 6 9 17 4 12"/></svg>';
|
|
@@ -5337,7 +5613,7 @@ export async function openSettingsModal(options = {}) {
|
|
|
5337
5613
|
// An edited copy is removed only on an explicit yes (force). A file without SynaBun's markers is the
|
|
5338
5614
|
// user's own and stays whatever is sent, so there is nothing to confirm for it.
|
|
5339
5615
|
const edited = info?.state === 'modified' && info.replaceable !== false;
|
|
5340
|
-
// OpenCode: a line in
|
|
5616
|
+
// OpenCode: a line in opencode.json that SynaBun did not add keeps the file on a plain remove (the answer
|
|
5341
5617
|
// says so, and the host is then no longer managed). Asked again, the confirm sends force, which takes that line too.
|
|
5342
5618
|
const listed = info?.entry === 'user';
|
|
5343
5619
|
const force = edited || (listed && info.managed === false);
|
|
@@ -5480,8 +5756,8 @@ export async function openSettingsModal(options = {}) {
|
|
|
5480
5756
|
wireRulesControls('codex', 'codex');
|
|
5481
5757
|
|
|
5482
5758
|
// ── OpenCode ──
|
|
5483
|
-
wireSetupMcpToggle('opencode', '/api/opencode/mcp', '~/.config/opencode/
|
|
5484
|
-
// Config preview — OpenCode
|
|
5759
|
+
wireSetupMcpToggle('opencode', '/api/opencode/mcp', '~/.config/opencode/opencode.json');
|
|
5760
|
+
// Config preview — OpenCode 2 keeps MCP servers under "mcp" → "servers" of opencode.json
|
|
5485
5761
|
{
|
|
5486
5762
|
const preview = overlay.querySelector('#setup-opencode-config-preview');
|
|
5487
5763
|
let cachedConfig = '';
|
|
@@ -5489,15 +5765,18 @@ export async function openSettingsModal(options = {}) {
|
|
|
5489
5765
|
const ep = setupStatus.paths?.envPath || '<path-to>/synabun/.env';
|
|
5490
5766
|
fetch('/api/opencode/mcp').then(r => r.json()).then(data => {
|
|
5491
5767
|
if (data.ok && data.data && Object.keys(data.data).length) {
|
|
5492
|
-
cachedConfig = JSON.stringify({ mcp: data.data }, null, 2);
|
|
5768
|
+
cachedConfig = JSON.stringify({ mcp: { servers: data.data } }, null, 2);
|
|
5493
5769
|
preview.textContent = cachedConfig;
|
|
5494
5770
|
} else if (data.ok) {
|
|
5771
|
+
const launch = mcpLaunch();
|
|
5495
5772
|
cachedConfig = JSON.stringify({
|
|
5496
5773
|
mcp: {
|
|
5497
|
-
|
|
5498
|
-
|
|
5499
|
-
|
|
5500
|
-
|
|
5774
|
+
servers: {
|
|
5775
|
+
SynaBun: {
|
|
5776
|
+
type: 'local',
|
|
5777
|
+
command: [launch.command, ...launch.args],
|
|
5778
|
+
environment: { DOTENV_PATH: ep },
|
|
5779
|
+
},
|
|
5501
5780
|
},
|
|
5502
5781
|
},
|
|
5503
5782
|
}, null, 2);
|
|
@@ -5591,7 +5870,7 @@ export async function openSettingsModal(options = {}) {
|
|
|
5591
5870
|
overlay.querySelectorAll('.bc-dropdown.open').forEach(dd => {
|
|
5592
5871
|
if (!dd.contains(e.target)) dd.classList.remove('open');
|
|
5593
5872
|
});
|
|
5594
|
-
});
|
|
5873
|
+
}, { signal: panelLife.signal });
|
|
5595
5874
|
|
|
5596
5875
|
// Sync dropdown display from hidden input value (for applyBrowserConfig)
|
|
5597
5876
|
function syncBcDropdowns() {
|
|
@@ -6091,12 +6370,12 @@ export async function openSettingsModal(options = {}) {
|
|
|
6091
6370
|
+ '</div>';
|
|
6092
6371
|
html += '<div style="padding:4px 0">';
|
|
6093
6372
|
if (data.parent) {
|
|
6094
|
-
html += `<div class="cc-browse-item" data-path="${
|
|
6373
|
+
html += `<div class="cc-browse-item" data-path="${kit.esc(data.parent)}" style="padding:4px 10px;cursor:pointer;font-size:12px;display:flex;align-items:center;gap:6px;color:var(--t-muted)">`
|
|
6095
6374
|
+ '<svg viewBox="0 0 24 24" style="width:13px;height:13px;fill:none;stroke:currentColor;stroke-width:2"><polyline points="15 18 9 12 15 6"/></svg>'
|
|
6096
6375
|
+ '' + kit.te('settings.redesign.browser.parent') + '</div>';
|
|
6097
6376
|
}
|
|
6098
6377
|
for (const d of data.directories) {
|
|
6099
|
-
html += `<div class="cc-browse-item" data-path="${
|
|
6378
|
+
html += `<div class="cc-browse-item" data-path="${kit.esc(data.current + '/' + d)}" style="padding:4px 10px;cursor:pointer;font-size:12px;display:flex;align-items:center;gap:6px">`
|
|
6100
6379
|
+ '<svg viewBox="0 0 24 24" style="width:13px;height:13px;fill:none;stroke:currentColor;stroke-width:2"><path d="M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z"/></svg>'
|
|
6101
6380
|
+ escapeHtml(d) + '</div>';
|
|
6102
6381
|
}
|
|
@@ -7274,7 +7553,10 @@ export async function openSettingsModal(options = {}) {
|
|
|
7274
7553
|
const ocpToolsEl = overlay.querySelector('#stg-ocp-tools');
|
|
7275
7554
|
const ocpHistoryMetaEl = overlay.querySelector('#stg-ocp-history-meta');
|
|
7276
7555
|
const ocpHistoryClearBtn = overlay.querySelector('#stg-ocp-clear-history');
|
|
7277
|
-
const
|
|
7556
|
+
const ocpInstallEl = overlay.querySelector('#stg-ocp-install');
|
|
7557
|
+
const ocpConfigPathEl = overlay.querySelector('#stg-ocp-config-path');
|
|
7558
|
+
const ocpConfigJsoncEl = overlay.querySelector('#stg-ocp-config-jsonc');
|
|
7559
|
+
const esc = s => String(s || '').replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"');
|
|
7278
7560
|
|
|
7279
7561
|
// ── Helpers ──
|
|
7280
7562
|
let _ocpConnected = new Set();
|
|
@@ -7296,6 +7578,7 @@ export async function openSettingsModal(options = {}) {
|
|
|
7296
7578
|
let _ollamaConfiguring = false;
|
|
7297
7579
|
let _ollamaPollTimer = null;
|
|
7298
7580
|
let _ollamaLastModelNames = '';
|
|
7581
|
+
let _ollamaConfigureError = '';
|
|
7299
7582
|
let _ocpHistoryStats = null;
|
|
7300
7583
|
|
|
7301
7584
|
function formatBytes(bytes) {
|
|
@@ -7326,7 +7609,7 @@ export async function openSettingsModal(options = {}) {
|
|
|
7326
7609
|
ocpHistoryMetaEl.textContent = kit.tx('settings.redesign.opencode.historyWipeInProgress');
|
|
7327
7610
|
return;
|
|
7328
7611
|
}
|
|
7329
|
-
const totalBytes =
|
|
7612
|
+
const totalBytes = Number(data.databaseBytes) || 0;
|
|
7330
7613
|
ocpHistoryMetaEl.textContent = kit.tx('settings.redesign.opencode.stored', { formatCount: formatCount(data.sessions, 'session'), formatBytes: formatBytes(totalBytes) });
|
|
7331
7614
|
ocpHistoryClearBtn.disabled = false;
|
|
7332
7615
|
} catch (error) {
|
|
@@ -7338,8 +7621,7 @@ export async function openSettingsModal(options = {}) {
|
|
|
7338
7621
|
if (document.querySelector('.ocp-history-clear-overlay')) return;
|
|
7339
7622
|
const confirmation = _ocpHistoryStats?.confirmationPhrase || 'DELETE OPENCODE HISTORY';
|
|
7340
7623
|
const sessionCount = Number(_ocpHistoryStats?.sessions) || 0;
|
|
7341
|
-
const totalBytes =
|
|
7342
|
-
+ (Number(_ocpHistoryStats?.artifactBytes) || 0);
|
|
7624
|
+
const totalBytes = Number(_ocpHistoryStats?.databaseBytes) || 0;
|
|
7343
7625
|
const modal = document.createElement('div');
|
|
7344
7626
|
modal.className = 'tag-delete-overlay ocp-history-clear-overlay';
|
|
7345
7627
|
modal.innerHTML = `
|
|
@@ -7353,9 +7635,9 @@ export async function openSettingsModal(options = {}) {
|
|
|
7353
7635
|
<p class="ocp-history-modal-subtitle">${formatCount(sessionCount, 'session')} ${kit.th('settings.redesign.opencode.currentlyStored', { formatBytes: formatBytes(totalBytes) })}</p>
|
|
7354
7636
|
</div>
|
|
7355
7637
|
</div>
|
|
7356
|
-
<div class="ocp-history-warning"><strong>${kit.te('settings.redesign.opencode.thisCannotBeUndone')}</strong> ${kit.te('settings.redesign.opencode.
|
|
7638
|
+
<div class="ocp-history-warning"><strong>${kit.te('settings.redesign.opencode.thisCannotBeUndone')}</strong> ${kit.te('settings.redesign.opencode.historyClearSteps')}</div>
|
|
7357
7639
|
<ul class="ocp-history-scope">
|
|
7358
|
-
<li>${kit.te('settings.redesign.opencode.
|
|
7640
|
+
<li>${kit.te('settings.redesign.opencode.historyClearDeletes')}</li>
|
|
7359
7641
|
<li>${kit.te('settings.redesign.opencode.deletesSynabunSDerivedOpencodeResume')}</li>
|
|
7360
7642
|
<li>${kit.te('settings.redesign.opencode.keepsProviderCredentialsAccountConfigSet')}</li>
|
|
7361
7643
|
</ul>
|
|
@@ -7403,7 +7685,7 @@ export async function openSettingsModal(options = {}) {
|
|
|
7403
7685
|
confirmButton.disabled = true;
|
|
7404
7686
|
confirmButton.textContent = kit.tx('settings.redesign.opencode.clearing');
|
|
7405
7687
|
feedback.dataset.state = 'working';
|
|
7406
|
-
feedback.textContent = kit.tx('settings.redesign.opencode.
|
|
7688
|
+
feedback.textContent = kit.tx('settings.redesign.opencode.historyClearWorking');
|
|
7407
7689
|
const requestId = crypto.randomUUID?.() || `${Date.now()}-${Math.random()}`;
|
|
7408
7690
|
sessionStorage.setItem('synabun-opencode-history-clear-request', requestId);
|
|
7409
7691
|
try {
|
|
@@ -7416,9 +7698,7 @@ export async function openSettingsModal(options = {}) {
|
|
|
7416
7698
|
if (!response.ok || !data.ok) throw new Error(data.error || kit.tx('settings.redesign.opencode.historyWipeFailedHttp', { status: response.status }));
|
|
7417
7699
|
|
|
7418
7700
|
const result = data.result || {};
|
|
7419
|
-
const reclaimed = Math.max(0,
|
|
7420
|
-
(Number(result.databaseBytesBefore) || 0) - (Number(result.databaseBytesAfter) || 0)
|
|
7421
|
-
+ (Number(result.artifactBytesDeleted) || 0));
|
|
7701
|
+
const reclaimed = Math.max(0, (Number(result.databaseBytesBefore) || 0) - (Number(result.databaseBytesAfter) || 0));
|
|
7422
7702
|
const warnings = Array.isArray(result.warnings) ? result.warnings.filter(Boolean) : [];
|
|
7423
7703
|
feedback.dataset.state = warnings.length ? 'warning' : 'working';
|
|
7424
7704
|
feedback.textContent = warnings.length
|
|
@@ -7459,7 +7739,7 @@ export async function openSettingsModal(options = {}) {
|
|
|
7459
7739
|
dotColor = '#facc15';
|
|
7460
7740
|
statusText = kit.tx('settings.redesign.opencode.configuring');
|
|
7461
7741
|
statusCls = 'stg-provider-status-key';
|
|
7462
|
-
bodyHtml = '<div class="stg-provider-note">' + kit.te('settings.redesign.opencode.
|
|
7742
|
+
bodyHtml = '<div class="stg-provider-note">' + kit.te('settings.redesign.opencode.writingProviderConfig') + '</div>';
|
|
7463
7743
|
} else if (!_ollamaRunning) {
|
|
7464
7744
|
dotColor = 'rgba(248,113,113,0.7)';
|
|
7465
7745
|
statusText = kit.tx('settings.redesign.opencode.notDetected');
|
|
@@ -7470,6 +7750,11 @@ export async function openSettingsModal(options = {}) {
|
|
|
7470
7750
|
statusText = kit.tx('settings.redesign.opencode.runningVersion', { version: _ollamaVersion ? ' v' + _ollamaVersion : '' });
|
|
7471
7751
|
statusCls = 'stg-provider-status-key';
|
|
7472
7752
|
bodyHtml = '<div class="stg-provider-note">' + kit.te('settings.redesign.opencode.noModelsRun') + ' <code class="stg-code">ollama pull <model></code></div>';
|
|
7753
|
+
} else if (_ollamaConfigureError) {
|
|
7754
|
+
dotColor = 'rgba(248,113,113,0.7)';
|
|
7755
|
+
statusText = kit.tx('settings.redesign.opencode.runningVersion', { version: _ollamaVersion ? ' v' + _ollamaVersion : '' });
|
|
7756
|
+
statusCls = 'stg-provider-status-disconnected';
|
|
7757
|
+
bodyHtml = '<div class="stg-provider-note">' + esc(kit.tx('settings.redesign.opencode.error2', { message: _ollamaConfigureError })) + '</div>';
|
|
7473
7758
|
} else if (_ollamaConfigured) {
|
|
7474
7759
|
dotColor = '#4ade80';
|
|
7475
7760
|
statusText = kit.tx('settings.redesign.opencode.runningVersion', { version: _ollamaVersion ? ' v' + _ollamaVersion : '' });
|
|
@@ -7505,27 +7790,16 @@ export async function openSettingsModal(options = {}) {
|
|
|
7505
7790
|
const btn = existing.querySelector('.stg-ollama-remove');
|
|
7506
7791
|
if (btn) { btn.disabled = true; btn.textContent = kit.tx('settings.redesign.opencode.removing'); }
|
|
7507
7792
|
try {
|
|
7508
|
-
await fetch('/api/ollama/configure', { method: 'DELETE' });
|
|
7793
|
+
const resp = await fetch('/api/ollama/configure', { method: 'DELETE' });
|
|
7794
|
+
const data = await resp.json().catch(() => ({}));
|
|
7795
|
+
if (!resp.ok || data.ok === false) throw new Error(data.error || `HTTP ${resp.status}`);
|
|
7509
7796
|
_ollamaConfigured = false;
|
|
7510
7797
|
_ollamaConfiguredCount = 0;
|
|
7511
7798
|
_ollamaUserRemoved = true;
|
|
7512
7799
|
renderOllamaCard();
|
|
7513
|
-
showCCToast(kit.tx('settings.redesign.opencode.
|
|
7514
|
-
|
|
7515
|
-
|
|
7516
|
-
const pollId = setInterval(async () => {
|
|
7517
|
-
polls++;
|
|
7518
|
-
if (polls > 8) { clearInterval(pollId); return; }
|
|
7519
|
-
try {
|
|
7520
|
-
const sr = await fetch('/api/opencode/status');
|
|
7521
|
-
const sd = await sr.json();
|
|
7522
|
-
if (sd.running) {
|
|
7523
|
-
clearInterval(pollId);
|
|
7524
|
-
await refreshOcpProviders();
|
|
7525
|
-
document.dispatchEvent(new CustomEvent('ocp-providers-changed'));
|
|
7526
|
-
}
|
|
7527
|
-
} catch {}
|
|
7528
|
-
}, 2000);
|
|
7800
|
+
showCCToast(kit.tx('settings.redesign.opencode.ollamaProviderRemoved'));
|
|
7801
|
+
await refreshOcpProviders();
|
|
7802
|
+
document.dispatchEvent(new CustomEvent('ocp-providers-changed'));
|
|
7529
7803
|
} catch (e) {
|
|
7530
7804
|
showCCToast(kit.tx('settings.redesign.opencode.error2', { message: e.message }));
|
|
7531
7805
|
}
|
|
@@ -7559,7 +7833,9 @@ export async function openSettingsModal(options = {}) {
|
|
|
7559
7833
|
|
|
7560
7834
|
// Auto-configure if: running + has models + not configured + not user-removed
|
|
7561
7835
|
// Also re-configure if models changed while already configured
|
|
7562
|
-
|
|
7836
|
+
// A refusal (an opencode.json SynaBun cannot change now) is asked again only when the models change.
|
|
7837
|
+
if (modelsChanged) _ollamaConfigureError = '';
|
|
7838
|
+
const shouldConfigure = _ollamaRunning && _ollamaModels.length > 0 && !_ollamaUserRemoved && !_ollamaConfigureError
|
|
7563
7839
|
&& (!_ollamaConfigured || modelsChanged);
|
|
7564
7840
|
|
|
7565
7841
|
if (shouldConfigure && !_ollamaConfiguring) {
|
|
@@ -7567,31 +7843,16 @@ export async function openSettingsModal(options = {}) {
|
|
|
7567
7843
|
renderOllamaCard();
|
|
7568
7844
|
try {
|
|
7569
7845
|
const cfgResp = await fetch('/api/ollama/configure', { method: 'POST' });
|
|
7570
|
-
const cfgData = await cfgResp.json();
|
|
7571
|
-
|
|
7846
|
+
const cfgData = await cfgResp.json().catch(() => ({}));
|
|
7847
|
+
_ollamaConfiguring = false;
|
|
7848
|
+
if (cfgResp.ok && cfgData.ok) {
|
|
7849
|
+
// OpenCode reads the provider at once: its models are listed now.
|
|
7572
7850
|
_ollamaConfigured = true;
|
|
7573
7851
|
_ollamaConfiguredCount = cfgData.modelsConfigured;
|
|
7574
|
-
|
|
7575
|
-
|
|
7576
|
-
let polls = 0;
|
|
7577
|
-
const pollId = setInterval(async () => {
|
|
7578
|
-
polls++;
|
|
7579
|
-
if (polls > 8) { clearInterval(pollId); _ollamaConfiguring = false; renderOllamaCard(); return; }
|
|
7580
|
-
try {
|
|
7581
|
-
const sr = await fetch('/api/opencode/status');
|
|
7582
|
-
const sd = await sr.json();
|
|
7583
|
-
if (sd.running) {
|
|
7584
|
-
clearInterval(pollId);
|
|
7585
|
-
_ollamaConfiguring = false;
|
|
7586
|
-
await refreshOcpProviders();
|
|
7587
|
-
renderOllamaCard();
|
|
7588
|
-
document.dispatchEvent(new CustomEvent('ocp-providers-changed'));
|
|
7589
|
-
}
|
|
7590
|
-
} catch {}
|
|
7591
|
-
}, 2000);
|
|
7592
|
-
}, 1000);
|
|
7852
|
+
await refreshOcpProviders();
|
|
7853
|
+
document.dispatchEvent(new CustomEvent('ocp-providers-changed'));
|
|
7593
7854
|
} else {
|
|
7594
|
-
|
|
7855
|
+
_ollamaConfigureError = cfgData.error || `HTTP ${cfgResp.status}`;
|
|
7595
7856
|
}
|
|
7596
7857
|
} catch {
|
|
7597
7858
|
_ollamaConfiguring = false;
|
|
@@ -7843,7 +8104,7 @@ export async function openSettingsModal(options = {}) {
|
|
|
7843
8104
|
}
|
|
7844
8105
|
for (const om of oauthMethods) {
|
|
7845
8106
|
const label = om.label || om.name || kit.tx('settings.redesign.opencode.reconnectProvider', { name });
|
|
7846
|
-
btns.push(`<button type="button" class="stg-action-btn compact stg-text-btn stg-ocp-oauth-btn" data-provider="${esc(id)}" data-method="${om._idx}">${esc(label)}</button>`);
|
|
8107
|
+
btns.push(`<button type="button" class="stg-action-btn compact stg-text-btn stg-ocp-oauth-btn" data-provider="${esc(id)}" data-method="${om._idx}" data-method-id="${esc(om.id || '')}">${esc(label)}</button>`);
|
|
7847
8108
|
}
|
|
7848
8109
|
authHtml = btns.join('');
|
|
7849
8110
|
removeHtml = `<button type="button" class="stg-action-btn compact stg-text-btn stg-text-btn-danger stg-ocp-remove-btn" data-provider="${esc(id)}" data-env="${esc(primaryEnv)}">${kit.te('settings.redesign.opencode.removeConfirm')}</button>`;
|
|
@@ -7857,7 +8118,7 @@ export async function openSettingsModal(options = {}) {
|
|
|
7857
8118
|
}
|
|
7858
8119
|
for (const om of oauthMethods) {
|
|
7859
8120
|
const label = om.label || om.name || kit.tx('settings.redesign.opencode.loginWith', { name: name });
|
|
7860
|
-
btns.push(`<button type="button" class="stg-action-btn compact stg-ocp-oauth-btn" data-provider="${esc(id)}" data-method="${om._idx}">${esc(label)}</button>`);
|
|
8121
|
+
btns.push(`<button type="button" class="stg-action-btn compact stg-ocp-oauth-btn" data-provider="${esc(id)}" data-method="${om._idx}" data-method-id="${esc(om.id || '')}">${esc(label)}</button>`);
|
|
7861
8122
|
}
|
|
7862
8123
|
authHtml = btns.join('');
|
|
7863
8124
|
}
|
|
@@ -7873,9 +8134,16 @@ export async function openSettingsModal(options = {}) {
|
|
|
7873
8134
|
</div>`;
|
|
7874
8135
|
|
|
7875
8136
|
// OAuth wait indicator (hidden via inline style — JS removes it to show)
|
|
8137
|
+
// OAuth wait (hidden via inline style — JS removes it to show): OpenCode's instructions (a device code),
|
|
8138
|
+
// and, for a sign-in that ends with a code the provider shows, the field it is pasted in.
|
|
7876
8139
|
const oauthWaitHtml = `<div class="stg-ocp-oauth-wait" data-provider="${esc(id)}" style="display:none">
|
|
7877
|
-
|
|
8140
|
+
<span class="stg-ocp-oauth-status">${kit.te('settings.redesign.opencode.waitingForAuthorization')}</span>
|
|
7878
8141
|
<span class="stg-ocp-oauth-code" style="display:none"></span>
|
|
8142
|
+
<div class="stg-ocp-key-row stg-ocp-oauth-code-row" style="display:none">
|
|
8143
|
+
<input type="text" class="stg-input stg-input-sm stg-ocp-oauth-code-input" placeholder="${kit.te('settings.redesign.opencode.oauthCodePlaceholder')}" aria-label="${kit.te('settings.redesign.opencode.oauthCodeFor', { name })}" autocomplete="off" spellcheck="false" style="flex:1">
|
|
8144
|
+
<button type="button" class="stg-action-btn compact stg-ocp-oauth-code-submit">${kit.te('settings.redesign.opencode.oauthConnect')}</button>
|
|
8145
|
+
</div>
|
|
8146
|
+
<div class="stg-ocp-key-error"></div>
|
|
7879
8147
|
</div>`;
|
|
7880
8148
|
|
|
7881
8149
|
const hiddenSet = getHiddenModels();
|
|
@@ -8071,28 +8339,15 @@ export async function openSettingsModal(options = {}) {
|
|
|
8071
8339
|
try {
|
|
8072
8340
|
const resp = await fetch(`/api/opencode/auth/${encodeURIComponent(provId)}`, {
|
|
8073
8341
|
method: 'PUT', headers: { 'Content-Type': 'application/json' },
|
|
8074
|
-
body: JSON.stringify({
|
|
8342
|
+
body: JSON.stringify({ key }),
|
|
8075
8343
|
});
|
|
8076
|
-
const data = await resp.json();
|
|
8344
|
+
const data = await resp.json().catch(() => ({}));
|
|
8077
8345
|
if (resp.ok && data.ok !== false) {
|
|
8078
8346
|
form.style.display = 'none';
|
|
8079
8347
|
input.value = '';
|
|
8080
|
-
showCCToast(kit.tx('settings.redesign.opencode.
|
|
8081
|
-
|
|
8082
|
-
|
|
8083
|
-
const pollId = setInterval(async () => {
|
|
8084
|
-
polls++;
|
|
8085
|
-
if (polls > 8) { clearInterval(pollId); return; }
|
|
8086
|
-
try {
|
|
8087
|
-
const sr = await fetch('/api/opencode/status');
|
|
8088
|
-
const sd = await sr.json();
|
|
8089
|
-
if (sd.running) {
|
|
8090
|
-
clearInterval(pollId);
|
|
8091
|
-
await refreshOcpProviders();
|
|
8092
|
-
document.dispatchEvent(new CustomEvent('ocp-providers-changed'));
|
|
8093
|
-
}
|
|
8094
|
-
} catch {}
|
|
8095
|
-
}, 2000);
|
|
8348
|
+
showCCToast(kit.tx('settings.redesign.opencode.apiKeySaved', { provId: provId }));
|
|
8349
|
+
await refreshOcpProviders();
|
|
8350
|
+
document.dispatchEvent(new CustomEvent('ocp-providers-changed'));
|
|
8096
8351
|
} else {
|
|
8097
8352
|
const msg = data.error || kit.tx('settings.redesign.opencode.failedToSaveKey');
|
|
8098
8353
|
if (errEl) { errEl.textContent = msg; errEl.style.display = ''; }
|
|
@@ -8125,24 +8380,11 @@ export async function openSettingsModal(options = {}) {
|
|
|
8125
8380
|
method: 'DELETE',
|
|
8126
8381
|
headers: { 'Content-Type': 'application/json' },
|
|
8127
8382
|
});
|
|
8128
|
-
const data = await resp.json();
|
|
8383
|
+
const data = await resp.json().catch(() => ({}));
|
|
8129
8384
|
if (resp.ok && data.ok !== false) {
|
|
8130
|
-
showCCToast(kit.tx('settings.redesign.opencode.
|
|
8131
|
-
|
|
8132
|
-
|
|
8133
|
-
const pollId = setInterval(async () => {
|
|
8134
|
-
polls++;
|
|
8135
|
-
if (polls > 8) { clearInterval(pollId); return; }
|
|
8136
|
-
try {
|
|
8137
|
-
const sr = await fetch('/api/opencode/status');
|
|
8138
|
-
const sd = await sr.json();
|
|
8139
|
-
if (sd.running) {
|
|
8140
|
-
clearInterval(pollId);
|
|
8141
|
-
await refreshOcpProviders();
|
|
8142
|
-
document.dispatchEvent(new CustomEvent('ocp-providers-changed'));
|
|
8143
|
-
}
|
|
8144
|
-
} catch {}
|
|
8145
|
-
}, 2000);
|
|
8385
|
+
showCCToast(kit.tx('settings.redesign.opencode.apiKeyRemoved', { provId: provId }));
|
|
8386
|
+
await refreshOcpProviders();
|
|
8387
|
+
document.dispatchEvent(new CustomEvent('ocp-providers-changed'));
|
|
8146
8388
|
} else {
|
|
8147
8389
|
showCCToast(data.error || kit.tx('settings.redesign.opencode.failedToRemoveKey'));
|
|
8148
8390
|
}
|
|
@@ -8159,42 +8401,22 @@ export async function openSettingsModal(options = {}) {
|
|
|
8159
8401
|
btn.addEventListener('click', async () => {
|
|
8160
8402
|
const provId = btn.dataset.provider;
|
|
8161
8403
|
const methodIdx = parseInt(btn.dataset.method, 10);
|
|
8404
|
+
const card = btn.closest('.stg-provider-card');
|
|
8405
|
+
const providerName = card?.querySelector('.stg-provider-name')?.textContent?.trim() || provId;
|
|
8162
8406
|
btn.disabled = true; btn.textContent = kit.tx('settings.redesign.opencode.connecting');
|
|
8163
|
-
const waitEl = ocpProvidersEl.querySelector(`.stg-ocp-oauth-wait[data-provider="${provId}"]`);
|
|
8164
|
-
const codeEl = waitEl?.querySelector('.stg-ocp-oauth-code');
|
|
8165
8407
|
try {
|
|
8166
8408
|
const resp = await fetch(`/api/opencode/oauth/${encodeURIComponent(provId)}/authorize`, {
|
|
8167
8409
|
method: 'POST',
|
|
8168
8410
|
headers: { 'Content-Type': 'application/json' },
|
|
8169
|
-
body: JSON.stringify({ method: isNaN(methodIdx) ? 0 : methodIdx }),
|
|
8411
|
+
body: JSON.stringify({ methodID: btn.dataset.methodId || undefined, method: isNaN(methodIdx) ? 0 : methodIdx }),
|
|
8170
8412
|
});
|
|
8171
|
-
const data = await resp.json();
|
|
8172
|
-
const
|
|
8173
|
-
if (
|
|
8174
|
-
window.open(
|
|
8175
|
-
|
|
8176
|
-
if (authData.code && codeEl) { codeEl.textContent = authData.code; codeEl.style.display = ''; }
|
|
8177
|
-
if (authData.instructions) showCCToast(authData.instructions);
|
|
8178
|
-
// Poll for completion
|
|
8179
|
-
let attempts = 0;
|
|
8180
|
-
const poll = setInterval(async () => {
|
|
8181
|
-
attempts++;
|
|
8182
|
-
if (attempts > 60) { clearInterval(poll); if (waitEl) waitEl.style.display = 'none'; return; }
|
|
8183
|
-
try {
|
|
8184
|
-
const pr = await fetch('/api/opencode/providers/full');
|
|
8185
|
-
const pd = await pr.json();
|
|
8186
|
-
const connected = pd.data?.connected || [];
|
|
8187
|
-
if (connected.includes(provId)) {
|
|
8188
|
-
clearInterval(poll);
|
|
8189
|
-
if (waitEl) waitEl.style.display = 'none';
|
|
8190
|
-
showCCToast(kit.tx('settings.redesign.opencode.connected', { provId: provId }));
|
|
8191
|
-
await refreshOcpProviders();
|
|
8192
|
-
document.dispatchEvent(new CustomEvent('ocp-providers-changed'));
|
|
8193
|
-
}
|
|
8194
|
-
} catch {}
|
|
8195
|
-
}, 2000);
|
|
8413
|
+
const data = await resp.json().catch(() => ({}));
|
|
8414
|
+
const auth = data.data || {};
|
|
8415
|
+
if (resp.ok && data.ok !== false && auth.url) {
|
|
8416
|
+
window.open(auth.url, '_blank', 'noopener');
|
|
8417
|
+
showOcpOAuthWait(provId, providerName, auth);
|
|
8196
8418
|
} else {
|
|
8197
|
-
showCCToast(
|
|
8419
|
+
showCCToast(data.error || kit.tx('settings.redesign.opencode.oauthFailedNoAuthorizationUrlReturned'));
|
|
8198
8420
|
}
|
|
8199
8421
|
} catch (e) {
|
|
8200
8422
|
showCCToast(kit.tx('settings.redesign.opencode.oauthError', { message: e.message }));
|
|
@@ -8204,6 +8426,81 @@ export async function openSettingsModal(options = {}) {
|
|
|
8204
8426
|
});
|
|
8205
8427
|
}
|
|
8206
8428
|
|
|
8429
|
+
// ── A sign-in under way (OpenCode 2's attempt: `auto` finishes by itself, `code` with the code pasted) ──
|
|
8430
|
+
const _ocpOAuthPolls = new Map(); // provider id → timer
|
|
8431
|
+
function endOcpOAuth(provId) {
|
|
8432
|
+
clearInterval(_ocpOAuthPolls.get(provId));
|
|
8433
|
+
_ocpOAuthPolls.delete(provId);
|
|
8434
|
+
const waitEl = ocpProvidersEl?.querySelector(`.stg-ocp-oauth-wait[data-provider="${CSS.escape(provId)}"]`);
|
|
8435
|
+
if (waitEl) waitEl.style.display = 'none';
|
|
8436
|
+
}
|
|
8437
|
+
async function finishOcpOAuth(provId) {
|
|
8438
|
+
endOcpOAuth(provId);
|
|
8439
|
+
showCCToast(kit.tx('settings.redesign.opencode.connected', { provId: provId }));
|
|
8440
|
+
await refreshOcpProviders();
|
|
8441
|
+
document.dispatchEvent(new CustomEvent('ocp-providers-changed'));
|
|
8442
|
+
}
|
|
8443
|
+
function showOcpOAuthWait(provId, providerName, auth) {
|
|
8444
|
+
endOcpOAuth(provId);
|
|
8445
|
+
const waitEl = ocpProvidersEl?.querySelector(`.stg-ocp-oauth-wait[data-provider="${CSS.escape(provId)}"]`);
|
|
8446
|
+
const codeEl = waitEl?.querySelector('.stg-ocp-oauth-code');
|
|
8447
|
+
const codeRow = waitEl?.querySelector('.stg-ocp-oauth-code-row');
|
|
8448
|
+
const errEl = waitEl?.querySelector('.stg-ocp-key-error');
|
|
8449
|
+
if (waitEl) waitEl.style.display = '';
|
|
8450
|
+
if (codeEl) { codeEl.textContent = auth.instructions || ''; codeEl.style.display = auth.instructions ? '' : 'none'; }
|
|
8451
|
+
if (errEl) errEl.textContent = '';
|
|
8452
|
+
if (auth.method === 'code') {
|
|
8453
|
+
if (!codeRow) return;
|
|
8454
|
+
codeRow.style.display = '';
|
|
8455
|
+
const input = codeRow.querySelector('.stg-ocp-oauth-code-input');
|
|
8456
|
+
const submit = codeRow.querySelector('.stg-ocp-oauth-code-submit');
|
|
8457
|
+
input?.focus();
|
|
8458
|
+
const send = async () => {
|
|
8459
|
+
const code = input?.value?.trim();
|
|
8460
|
+
if (!code) return;
|
|
8461
|
+
submit.disabled = true;
|
|
8462
|
+
try {
|
|
8463
|
+
const resp = await fetch(`/api/opencode/oauth/${encodeURIComponent(provId)}/callback`, {
|
|
8464
|
+
method: 'POST', headers: { 'Content-Type': 'application/json' },
|
|
8465
|
+
body: JSON.stringify({ attemptID: auth.attemptID, code }),
|
|
8466
|
+
});
|
|
8467
|
+
const data = await resp.json().catch(() => ({}));
|
|
8468
|
+
if (!resp.ok || data.ok === false) throw new Error(data.error || `HTTP ${resp.status}`);
|
|
8469
|
+
input.value = '';
|
|
8470
|
+
await finishOcpOAuth(provId);
|
|
8471
|
+
} catch (e) {
|
|
8472
|
+
if (errEl) errEl.textContent = kit.tx('settings.redesign.opencode.oauthFailed', { message: e.message });
|
|
8473
|
+
} finally {
|
|
8474
|
+
submit.disabled = false;
|
|
8475
|
+
}
|
|
8476
|
+
};
|
|
8477
|
+
if (submit) submit.onclick = send;
|
|
8478
|
+
if (input) input.onkeydown = (event) => { if (event.key === 'Enter') { event.preventDefault(); send(); } };
|
|
8479
|
+
return;
|
|
8480
|
+
}
|
|
8481
|
+
if (codeRow) codeRow.style.display = 'none';
|
|
8482
|
+
if (!auth.attemptID) return;
|
|
8483
|
+
// Every 2 s for five minutes: then the attempt is let go (OpenCode's own attempt ends by itself).
|
|
8484
|
+
let attempts = 0;
|
|
8485
|
+
_ocpOAuthPolls.set(provId, setInterval(async () => {
|
|
8486
|
+
attempts++;
|
|
8487
|
+
if (attempts > 150) { endOcpOAuth(provId); showCCToast(kit.tx('settings.redesign.opencode.oauthGaveUp')); return; }
|
|
8488
|
+
try {
|
|
8489
|
+
const resp = await fetch(`/api/opencode/oauth/${encodeURIComponent(provId)}/status?attemptID=${encodeURIComponent(auth.attemptID)}`);
|
|
8490
|
+
const data = await resp.json().catch(() => ({}));
|
|
8491
|
+
if (!resp.ok || data.ok === false) throw new Error(data.error || `HTTP ${resp.status}`);
|
|
8492
|
+
const status = data.data?.status;
|
|
8493
|
+
if (status === 'complete') await finishOcpOAuth(provId);
|
|
8494
|
+
else if (status === 'expired') { endOcpOAuth(provId); showCCToast(kit.tx('settings.redesign.opencode.oauthExpired')); }
|
|
8495
|
+
else if (status === 'failed') { endOcpOAuth(provId); showCCToast(kit.tx('settings.redesign.opencode.oauthFailed', { message: data.data?.message || status })); }
|
|
8496
|
+
} catch (e) {
|
|
8497
|
+
endOcpOAuth(provId);
|
|
8498
|
+
showCCToast(kit.tx('settings.redesign.opencode.oauthFailed', { message: e.message }));
|
|
8499
|
+
}
|
|
8500
|
+
}, 2000));
|
|
8501
|
+
}
|
|
8502
|
+
panelLife.signal.addEventListener('abort', () => { for (const provId of [..._ocpOAuthPolls.keys()]) endOcpOAuth(provId); });
|
|
8503
|
+
|
|
8207
8504
|
// ── Delegated card click-to-expand (survives re-renders) ──
|
|
8208
8505
|
if (ocpProvidersEl) {
|
|
8209
8506
|
ocpProvidersEl.addEventListener('click', (e) => {
|
|
@@ -8221,64 +8518,60 @@ export async function openSettingsModal(options = {}) {
|
|
|
8221
8518
|
}
|
|
8222
8519
|
|
|
8223
8520
|
// ── Tools/Permissions rendering ──
|
|
8224
|
-
|
|
8225
|
-
|
|
8226
|
-
|
|
8227
|
-
|
|
8228
|
-
|
|
8229
|
-
|
|
8230
|
-
|
|
8231
|
-
const
|
|
8232
|
-
|
|
8233
|
-
|
|
8234
|
-
|
|
8235
|
-
|
|
8236
|
-
|
|
8237
|
-
|
|
8238
|
-
|
|
8239
|
-
|
|
8240
|
-
|
|
8521
|
+
// What each of OpenCode's own tools may do: the rules of OpenCode's global opencode.json
|
|
8522
|
+
// (GET /api/opencode/config: `actions`, `tools`), read and written there whether or not OpenCode runs.
|
|
8523
|
+
const OCP_EFFECTS = ['allow', 'ask', 'deny'];
|
|
8524
|
+
if (ocpToolsEl) {
|
|
8525
|
+
wireDropdowns(ocpToolsEl, { signal: panelLife.signal });
|
|
8526
|
+
ocpToolsEl.addEventListener('change', async (event) => {
|
|
8527
|
+
const input = event.target;
|
|
8528
|
+
const action = input?.dataset?.tool;
|
|
8529
|
+
if (!action || !OCP_EFFECTS.includes(input.value)) return;
|
|
8530
|
+
try {
|
|
8531
|
+
const resp = await fetch('/api/opencode/config', {
|
|
8532
|
+
method: 'PATCH', headers: { 'Content-Type': 'application/json' },
|
|
8533
|
+
body: JSON.stringify({ tools: { [action]: input.value } }),
|
|
8534
|
+
});
|
|
8535
|
+
const data = await resp.json().catch(() => ({}));
|
|
8536
|
+
if (!resp.ok || data.ok === false) throw new Error(data.error || `HTTP ${resp.status}`);
|
|
8537
|
+
applyOcpConfig(data.data);
|
|
8538
|
+
} catch (err) {
|
|
8539
|
+
showCCToast(kit.tx('settings.redesign.opencode.saveFailed', { message: err.message }));
|
|
8540
|
+
renderToolPermissions();
|
|
8241
8541
|
}
|
|
8542
|
+
}, { signal: panelLife.signal });
|
|
8543
|
+
}
|
|
8242
8544
|
|
|
8243
|
-
|
|
8244
|
-
|
|
8245
|
-
|
|
8246
|
-
|
|
8247
|
-
|
|
8248
|
-
|
|
8249
|
-
|
|
8250
|
-
|
|
8251
|
-
|
|
8252
|
-
|
|
8253
|
-
|
|
8254
|
-
|
|
8255
|
-
|
|
8545
|
+
function renderToolPermissionsFrom(view) {
|
|
8546
|
+
if (!ocpToolsEl) return;
|
|
8547
|
+
const actions = Array.isArray(view?.actions) ? view.actions : [];
|
|
8548
|
+
if (!actions.length) {
|
|
8549
|
+
ocpToolsEl.innerHTML = '<div class="settings-hint" style="opacity:0.4">' + kit.te('settings.redesign.opencode.noToolsAvailable') + '</div>';
|
|
8550
|
+
return;
|
|
8551
|
+
}
|
|
8552
|
+
const tools = view.tools || {};
|
|
8553
|
+
const labels = { allow: kit.tx('settings.redesign.opencode.allow'), ask: kit.tx('settings.redesign.opencode.ask'), deny: kit.tx('settings.redesign.opencode.deny') };
|
|
8554
|
+
ocpToolsEl.innerHTML = `<div class="stg-tool-grid">${actions.map((action) => {
|
|
8555
|
+
const current = OCP_EFFECTS.includes(tools[action]) ? tools[action] : 'allow';
|
|
8556
|
+
const id = `stg-ocp-tool-${String(action).replace(/[^a-z0-9_-]/gi, '-')}`;
|
|
8557
|
+
return `<div class="stg-tool-row">
|
|
8558
|
+
<span class="stg-tool-name">${esc(action)}</span>
|
|
8559
|
+
${dropdownHTML(id, OCP_EFFECTS.map((effect) => ({ value: effect, label: labels[effect], selected: effect === current })), {
|
|
8560
|
+
classes: 'stg-dropdown stg-dropdown-inline stg-ocp-tool-perm',
|
|
8561
|
+
label: kit.tx('settings.redesign.opencode.permissionFor', { tool: action }),
|
|
8562
|
+
inputAttrs: `data-tool="${esc(action)}"`,
|
|
8563
|
+
})}
|
|
8256
8564
|
</div>`;
|
|
8257
|
-
|
|
8258
|
-
|
|
8259
|
-
|
|
8260
|
-
|
|
8261
|
-
|
|
8262
|
-
|
|
8263
|
-
|
|
8264
|
-
|
|
8265
|
-
|
|
8266
|
-
|
|
8267
|
-
if (val === 'deny') { newTools[tid] = false; delete newPerms[tid]; }
|
|
8268
|
-
else if (val === 'ask') { delete newTools[tid]; newPerms[tid] = 'ask'; }
|
|
8269
|
-
else { delete newTools[tid]; delete newPerms[tid]; }
|
|
8270
|
-
patchBody.permission = newPerms;
|
|
8271
|
-
patchBody.tools = newTools;
|
|
8272
|
-
try {
|
|
8273
|
-
await fetch('/api/opencode/config', {
|
|
8274
|
-
method: 'PATCH', headers: { 'Content-Type': 'application/json' },
|
|
8275
|
-
body: JSON.stringify(patchBody),
|
|
8276
|
-
});
|
|
8277
|
-
_ocpConfig.permission = newPerms;
|
|
8278
|
-
_ocpConfig.tools = newTools;
|
|
8279
|
-
} catch {}
|
|
8280
|
-
});
|
|
8281
|
-
});
|
|
8565
|
+
}).join('')}</div>`;
|
|
8566
|
+
}
|
|
8567
|
+
|
|
8568
|
+
async function renderToolPermissions() {
|
|
8569
|
+
if (!ocpToolsEl) return;
|
|
8570
|
+
try {
|
|
8571
|
+
const resp = await fetch('/api/opencode/config');
|
|
8572
|
+
const data = await resp.json();
|
|
8573
|
+
if (!resp.ok || data.ok === false) throw new Error(data.error || `HTTP ${resp.status}`);
|
|
8574
|
+
applyOcpConfig(data.data);
|
|
8282
8575
|
} catch {
|
|
8283
8576
|
ocpToolsEl.innerHTML = '<div class="settings-hint" style="opacity:0.4">' + kit.te('settings.redesign.opencode.failedToLoadTools') + '</div>';
|
|
8284
8577
|
}
|
|
@@ -8296,155 +8589,37 @@ export async function openSettingsModal(options = {}) {
|
|
|
8296
8589
|
return null;
|
|
8297
8590
|
}
|
|
8298
8591
|
|
|
8299
|
-
|
|
8300
|
-
|
|
8301
|
-
|
|
8302
|
-
|
|
8303
|
-
const
|
|
8304
|
-
|
|
8305
|
-
|
|
8306
|
-
|
|
8307
|
-
|
|
8308
|
-
|
|
8309
|
-
|
|
8310
|
-
|
|
8311
|
-
|
|
8312
|
-
|
|
8313
|
-
|
|
8314
|
-
|
|
8315
|
-
|
|
8316
|
-
const isActive = name === active;
|
|
8317
|
-
thead += `<th class="stg-mx-profile-th${isActive ? ' active' : ''}" data-profile="${esc(name)}">
|
|
8318
|
-
<div class="stg-mx-profile-head" data-profile="${esc(name)}">
|
|
8319
|
-
<span class="stg-mx-profile-dot${isActive ? ' on' : ''}"></span>
|
|
8320
|
-
<span class="stg-mx-profile-name">${esc(prof.label || name)}</span>
|
|
8321
|
-
${isActive ? '' : `<button class="stg-mx-profile-del" data-profile="${esc(name)}" title="${kit.te('settings.redesign.mcp.deleteProfile')}">\u00d7</button>`}
|
|
8322
|
-
</div>
|
|
8323
|
-
</th>`;
|
|
8324
|
-
}
|
|
8325
|
-
thead += '<th class="stg-mx-add-th"><button class="stg-mx-add-btn" title="' + kit.te('settings.redesign.mcp.newProfile') + '">+</button></th></tr>';
|
|
8326
|
-
|
|
8327
|
-
// Build table body — switchable groups
|
|
8328
|
-
let tbody = '';
|
|
8329
|
-
for (const [key, info] of switchable) {
|
|
8330
|
-
tbody += `<tr>`;
|
|
8331
|
-
tbody += `<td class="stg-mx-label-td"><div class="stg-mx-label-inner">
|
|
8332
|
-
<span class="stg-mx-grp-name">${esc(info.label)}</span>
|
|
8333
|
-
<span class="stg-mx-grp-count">${info.tools}</span>
|
|
8334
|
-
</div></td>`;
|
|
8335
|
-
for (const [profName, prof] of profEntries) {
|
|
8336
|
-
const isOn = (prof.groups || []).includes(key);
|
|
8337
|
-
tbody += `<td class="stg-mx-cell" data-profile="${esc(profName)}" data-group="${esc(key)}" data-kind="group">
|
|
8338
|
-
<span class="stg-mx-toggle${isOn ? ' on' : ''}"></span>
|
|
8339
|
-
</td>`;
|
|
8340
|
-
}
|
|
8341
|
-
tbody += '<td class="stg-mx-add-spacer"></td></tr>';
|
|
8342
|
-
}
|
|
8343
|
-
|
|
8344
|
-
wrap.innerHTML = `<div class="stg-mcp-matrix-scroll"><table id="stg-mcp-matrix"><thead>${thead}</thead><tbody>${tbody}</tbody></table></div>`;
|
|
8345
|
-
|
|
8346
|
-
// --- Event handlers ---
|
|
8347
|
-
|
|
8348
|
-
// Toggle cells
|
|
8349
|
-
wrap.querySelectorAll('.stg-mx-cell').forEach(cell => {
|
|
8350
|
-
cell.addEventListener('click', async () => {
|
|
8351
|
-
const profName = cell.dataset.profile;
|
|
8352
|
-
const prof = _mcpRegistry.profiles[profName];
|
|
8353
|
-
if (!prof) return;
|
|
8354
|
-
const kind = cell.dataset.kind;
|
|
8355
|
-
if (kind === 'group') {
|
|
8356
|
-
const g = cell.dataset.group;
|
|
8357
|
-
if ((prof.groups || []).includes(g)) {
|
|
8358
|
-
prof.groups = prof.groups.filter(x => x !== g);
|
|
8359
|
-
} else {
|
|
8360
|
-
prof.groups = [...(prof.groups || []), g];
|
|
8361
|
-
}
|
|
8362
|
-
} else {
|
|
8363
|
-
const s = cell.dataset.server;
|
|
8364
|
-
if ((prof.servers || []).includes(s)) {
|
|
8365
|
-
prof.servers = prof.servers.filter(x => x !== s);
|
|
8366
|
-
} else {
|
|
8367
|
-
prof.servers = [...(prof.servers || []), s];
|
|
8368
|
-
}
|
|
8369
|
-
}
|
|
8370
|
-
await fetch(`/api/mcp/registry/profiles/${encodeURIComponent(profName)}`, {
|
|
8371
|
-
method: 'PUT', headers: { 'Content-Type': 'application/json' },
|
|
8372
|
-
body: JSON.stringify(prof),
|
|
8373
|
-
});
|
|
8374
|
-
await refreshMcpTab();
|
|
8375
|
-
});
|
|
8376
|
-
});
|
|
8377
|
-
|
|
8378
|
-
// Activate profile on name click
|
|
8379
|
-
wrap.querySelectorAll('.stg-mx-profile-head').forEach(head => {
|
|
8380
|
-
head.addEventListener('click', async (e) => {
|
|
8381
|
-
if (e.target.closest('.stg-mx-profile-del')) return;
|
|
8382
|
-
const name = head.dataset.profile;
|
|
8383
|
-
if (name === _mcpRegistry.activeProfile) return;
|
|
8384
|
-
await fetch('/api/mcp/registry/activate', {
|
|
8385
|
-
method: 'POST', headers: { 'Content-Type': 'application/json' },
|
|
8386
|
-
body: JSON.stringify({ profile: name }),
|
|
8387
|
-
});
|
|
8388
|
-
showCCToast(kit.tx('settings.redesign.mcp.profileSwitchedTo', { name: name }));
|
|
8389
|
-
await refreshMcpTab();
|
|
8390
|
-
});
|
|
8391
|
-
});
|
|
8392
|
-
|
|
8393
|
-
// Delete profile
|
|
8394
|
-
wrap.querySelectorAll('.stg-mx-profile-del').forEach(btn => {
|
|
8395
|
-
btn.addEventListener('click', async (e) => {
|
|
8396
|
-
e.stopPropagation();
|
|
8397
|
-
await fetch(`/api/mcp/registry/profiles/${encodeURIComponent(btn.dataset.profile)}`, { method: 'DELETE' });
|
|
8398
|
-
await refreshMcpTab();
|
|
8399
|
-
});
|
|
8592
|
+
let _settingsToolCatalog = null;
|
|
8593
|
+
const defaultToolsWrap = overlay.querySelector('#stg-mcp-default-tools');
|
|
8594
|
+
if (defaultToolsWrap) wireDropdowns(defaultToolsWrap, { signal: panelLife.signal });
|
|
8595
|
+
function renderDefaultTools() {
|
|
8596
|
+
const wrap = overlay.querySelector('#stg-mcp-default-tools');
|
|
8597
|
+
if (!wrap) return;
|
|
8598
|
+
wrap.innerHTML = _settingsToolCatalog ? toolPickerHTML('stg-default-tools', _settingsToolCatalog.defaultTools, { catalog: _settingsToolCatalog, provider: 'all', label: toolWord('defaultTools') }) : '';
|
|
8599
|
+
wrap.querySelector('#stg-default-tools')?.addEventListener('change', async (event) => {
|
|
8600
|
+
const tools = readToolPicker(event.target, { inherit: false });
|
|
8601
|
+
try {
|
|
8602
|
+
const response = await fetch('/api/mcp/tools/default', { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ tools }) });
|
|
8603
|
+
const result = await response.json();
|
|
8604
|
+
if (!response.ok || !result.ok) throw new Error(result.error || toolWord('saveFailed'));
|
|
8605
|
+
_settingsToolCatalog.defaultTools = result.tools;
|
|
8606
|
+
showCCToast(toolWord('saved'));
|
|
8607
|
+
} catch (error) { showCCToast(error.message || toolWord('saveFailed')); }
|
|
8608
|
+
renderDefaultTools();
|
|
8400
8609
|
});
|
|
8401
|
-
|
|
8402
|
-
// Inline add profile
|
|
8403
|
-
const addBtn = wrap.querySelector('.stg-mx-add-btn');
|
|
8404
|
-
if (addBtn) {
|
|
8405
|
-
addBtn.addEventListener('click', () => {
|
|
8406
|
-
const th = addBtn.closest('th');
|
|
8407
|
-
th.innerHTML = `<input class="stg-mx-inline-input" type="text" placeholder="${kit.te('settings.redesign.mcp.name')}" autofocus>`;
|
|
8408
|
-
const input = th.querySelector('input');
|
|
8409
|
-
input.focus();
|
|
8410
|
-
const create = async () => {
|
|
8411
|
-
const name = input.value.trim();
|
|
8412
|
-
if (!name) { await refreshMcpTab(); return; }
|
|
8413
|
-
await fetch(`/api/mcp/registry/profiles/${encodeURIComponent(name)}`, {
|
|
8414
|
-
method: 'PUT', headers: { 'Content-Type': 'application/json' },
|
|
8415
|
-
body: JSON.stringify({ label: name, groups: [], servers: [] }),
|
|
8416
|
-
});
|
|
8417
|
-
await refreshMcpTab();
|
|
8418
|
-
};
|
|
8419
|
-
input.addEventListener('keydown', (e) => {
|
|
8420
|
-
if (e.key === 'Enter') { e.preventDefault(); e.stopPropagation(); create(); }
|
|
8421
|
-
if (e.key === 'Escape') { e.preventDefault(); e.stopPropagation(); refreshMcpTab(); }
|
|
8422
|
-
});
|
|
8423
|
-
input.addEventListener('blur', create);
|
|
8424
|
-
});
|
|
8425
|
-
}
|
|
8426
8610
|
}
|
|
8427
8611
|
|
|
8428
8612
|
function renderAlwaysOn() {
|
|
8429
8613
|
const el = overlay.querySelector('#stg-mcp-always-on');
|
|
8430
|
-
if (!el
|
|
8431
|
-
|
|
8432
|
-
const alwaysOn = Object.entries(tg).filter(([, info]) => info.alwaysOn);
|
|
8433
|
-
if (!alwaysOn.length) { el.innerHTML = ''; return; }
|
|
8434
|
-
el.innerHTML = `
|
|
8435
|
-
<div class="stg-mcp-section-header">${kit.te('settings.redesign.mcp.alwaysOn')}</div>
|
|
8436
|
-
<div class="stg-mx-always-on-row">
|
|
8437
|
-
${alwaysOn.map(([, info]) =>
|
|
8438
|
-
`<span class="stg-mx-always-chip">${esc(info.label)}<span class="stg-mx-grp-count">${info.tools}</span></span>`
|
|
8439
|
-
).join('')}
|
|
8440
|
-
</div>`;
|
|
8614
|
+
if (!el) return;
|
|
8615
|
+
el.innerHTML = _settingsToolCatalog ? `<div class="stg-mcp-section-header">${esc(toolWord('alwaysOn'))}</div><div class="stg-mx-always-on-row">${_settingsToolCatalog.alwaysOnTools.map(name => `<span class="stg-mx-always-chip">${esc(name)}</span>`).join('')}</div>` : '';
|
|
8441
8616
|
}
|
|
8442
8617
|
|
|
8443
8618
|
function renderExtServerList() {
|
|
8444
8619
|
const el = overlay.querySelector('#stg-mcp-ext-servers');
|
|
8445
8620
|
if (!el || !_mcpRegistry) return;
|
|
8446
|
-
const servers = _mcpRegistry.servers || {};
|
|
8447
|
-
const
|
|
8621
|
+
const servers = { ...(_mcpRegistry.servers || {}) };
|
|
8622
|
+
for (const row of _settingsToolCatalog?.servers || []) { const name = row.ref.slice(4); if (!servers[name]) servers[name] = { type: toolWord('configuredExternally'), discovered: true }; }
|
|
8448
8623
|
const entries = Object.entries(servers).filter(([, info]) => !info.builtin);
|
|
8449
8624
|
if (!entries.length) {
|
|
8450
8625
|
el.innerHTML = '<div class="stg-mcp-empty-note">' + kit.te('settings.redesign.mcp.noExternalServersConfigured') + '</div>';
|
|
@@ -8459,38 +8634,31 @@ export async function openSettingsModal(options = {}) {
|
|
|
8459
8634
|
<span class="stg-mx-ext-name">${esc(name)}</span>
|
|
8460
8635
|
<span class="stg-mx-ext-type">${esc(info.type || '?')}</span>
|
|
8461
8636
|
</div>
|
|
8462
|
-
<div class="stg-mx-ext-sub">${
|
|
8637
|
+
<div class="stg-mx-ext-sub">${esc(toolWord('agentAccessHelp'))}</div>
|
|
8463
8638
|
</div>
|
|
8464
|
-
|
|
8639
|
+
${info.discovered ? '' : `<button class="stg-mx-srv-del stg-mx-ext-remove" ${kit.inv('MCP004')} data-server="${esc(name)}" title="${kit.te('settings.redesign.mcp.removeServer')}">\u00d7</button>`}
|
|
8465
8640
|
</div>
|
|
8466
|
-
<div class="stg-
|
|
8467
|
-
${
|
|
8468
|
-
const isOn = (prof.servers || []).includes(name);
|
|
8469
|
-
return `<button class="stg-mx-ext-toggle${isOn ? ' on' : ''}" data-profile="${esc(profName)}" data-server="${esc(name)}" title="${isOn ? kit.te('settings.redesign.mcp.disable') : kit.te('settings.redesign.mcp.enable')} ${esc(name)} ${kit.te('settings.redesign.mcp.for')} ${esc(prof.label || profName)}">
|
|
8470
|
-
<span class="stg-mx-ext-toggle-name">${esc(prof.label || profName)}</span>
|
|
8471
|
-
</button>`;
|
|
8472
|
-
}).join('')}
|
|
8641
|
+
<div class="stg-field"><span>${esc(toolWord('agentAccess'))}</span>
|
|
8642
|
+
<button type="button" class="cc-toggle${_mcpRegistry.agentAccess?.[name] === false ? '' : ' on'}" role="switch" ${kit.inv('MCP005')} data-agent-server="${esc(name)}" aria-label="${esc(toolWord('agentAccess'))}: ${esc(name)}" aria-checked="${_mcpRegistry.agentAccess?.[name] !== false}"></button>
|
|
8473
8643
|
</div>
|
|
8474
8644
|
</div>`
|
|
8475
8645
|
).join('');
|
|
8476
8646
|
|
|
8477
|
-
el.querySelectorAll('
|
|
8478
|
-
|
|
8479
|
-
const
|
|
8480
|
-
|
|
8481
|
-
|
|
8482
|
-
|
|
8483
|
-
|
|
8484
|
-
|
|
8485
|
-
|
|
8486
|
-
|
|
8487
|
-
|
|
8488
|
-
|
|
8489
|
-
|
|
8490
|
-
|
|
8491
|
-
|
|
8492
|
-
});
|
|
8493
|
-
await refreshMcpTab();
|
|
8647
|
+
el.querySelectorAll('[data-agent-server]').forEach(input => {
|
|
8648
|
+
input.addEventListener('click', async () => {
|
|
8649
|
+
const enabled = input.getAttribute('aria-checked') !== 'true';
|
|
8650
|
+
input.disabled = true;
|
|
8651
|
+
try {
|
|
8652
|
+
const response = await fetch(`/api/mcp/registry/agent-access/${encodeURIComponent(input.dataset.agentServer)}`, {
|
|
8653
|
+
method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ enabled }),
|
|
8654
|
+
});
|
|
8655
|
+
const result = await response.json();
|
|
8656
|
+
if (!response.ok || !result.ok) throw new Error(result.error || toolWord('saveFailed'));
|
|
8657
|
+
_mcpRegistry.agentAccess = result.agentAccess;
|
|
8658
|
+
input.classList.toggle('on', enabled);
|
|
8659
|
+
input.setAttribute('aria-checked', String(enabled));
|
|
8660
|
+
} catch (error) { showCCToast(error.message || toolWord('saveFailed')); }
|
|
8661
|
+
finally { input.disabled = false; }
|
|
8494
8662
|
});
|
|
8495
8663
|
});
|
|
8496
8664
|
|
|
@@ -8645,7 +8813,8 @@ export async function openSettingsModal(options = {}) {
|
|
|
8645
8813
|
|
|
8646
8814
|
async function refreshMcpTab() {
|
|
8647
8815
|
await fetchMcpRegistry();
|
|
8648
|
-
|
|
8816
|
+
_settingsToolCatalog = await fetchToolCatalog(true);
|
|
8817
|
+
renderDefaultTools();
|
|
8649
8818
|
renderAlwaysOn();
|
|
8650
8819
|
renderExtServerList();
|
|
8651
8820
|
renderPluginsList();
|
|
@@ -9157,55 +9326,62 @@ export async function openSettingsModal(options = {}) {
|
|
|
9157
9326
|
});
|
|
9158
9327
|
|
|
9159
9328
|
// ── Config save helpers ──
|
|
9329
|
+
// PATCH /api/opencode/config (OpenCode 2's global opencode.json): the answer is the configuration as it is now.
|
|
9160
9330
|
async function saveOcpConfig(patch) {
|
|
9161
9331
|
try {
|
|
9162
|
-
await fetch('/api/opencode/config', {
|
|
9332
|
+
const resp = await fetch('/api/opencode/config', {
|
|
9163
9333
|
method: 'PATCH', headers: { 'Content-Type': 'application/json' },
|
|
9164
9334
|
body: JSON.stringify(patch),
|
|
9165
9335
|
});
|
|
9166
|
-
|
|
9336
|
+
const data = await resp.json().catch(() => ({}));
|
|
9337
|
+
if (!resp.ok || data.ok === false) throw new Error(data.error || `HTTP ${resp.status}`);
|
|
9338
|
+
applyOcpConfig(data.data);
|
|
9339
|
+
return true;
|
|
9340
|
+
} catch (err) {
|
|
9341
|
+
showCCToast(kit.tx('settings.redesign.opencode.saveFailed', { message: err.message }));
|
|
9342
|
+
loadOcpConfig();
|
|
9343
|
+
return false;
|
|
9344
|
+
}
|
|
9167
9345
|
}
|
|
9168
9346
|
|
|
9169
9347
|
// ── Compaction handlers ──
|
|
9170
9348
|
const compactAutoEl = overlay.querySelector('#stg-ocp-compact-auto');
|
|
9171
|
-
const compactPruneEl = overlay.querySelector('#stg-ocp-compact-prune');
|
|
9172
9349
|
const compactReservedEl = overlay.querySelector('#stg-ocp-compact-reserved');
|
|
9173
9350
|
|
|
9351
|
+
// An emptied field is OpenCode's own default again (buffer: null).
|
|
9174
9352
|
function saveCompaction() {
|
|
9175
|
-
|
|
9176
|
-
|
|
9177
|
-
|
|
9178
|
-
|
|
9179
|
-
reserved: Number(compactReservedEl?.value) || 10000,
|
|
9180
|
-
},
|
|
9181
|
-
});
|
|
9353
|
+
const raw = String(compactReservedEl?.value ?? '').trim();
|
|
9354
|
+
const buffer = raw === '' ? null : Math.round(Number(raw));
|
|
9355
|
+
if (buffer !== null && !(Number.isFinite(buffer) && buffer >= 0 && buffer <= 1_000_000)) { loadOcpConfig(); return; }
|
|
9356
|
+
saveOcpConfig({ compaction: { auto: compactAutoEl?.checked ?? true, buffer } });
|
|
9182
9357
|
}
|
|
9183
9358
|
compactAutoEl?.addEventListener('change', saveCompaction);
|
|
9184
|
-
compactPruneEl?.addEventListener('change', saveCompaction);
|
|
9185
9359
|
compactReservedEl?.addEventListener('change', saveCompaction);
|
|
9186
9360
|
|
|
9187
|
-
// ── Snapshot toggle (
|
|
9361
|
+
// ── Snapshot toggle (OpenCode's `snapshots`, read at once) ──
|
|
9188
9362
|
const snapshotToggle = overlay.querySelector('#stg-ocp-snapshot-toggle');
|
|
9189
|
-
|
|
9190
|
-
|
|
9191
|
-
|
|
9192
|
-
|
|
9193
|
-
|
|
9194
|
-
|
|
9195
|
-
|
|
9196
|
-
|
|
9197
|
-
|
|
9198
|
-
|
|
9199
|
-
|
|
9200
|
-
|
|
9201
|
-
|
|
9202
|
-
|
|
9203
|
-
|
|
9204
|
-
|
|
9205
|
-
|
|
9206
|
-
|
|
9207
|
-
|
|
9208
|
-
|
|
9363
|
+
snapshotToggle?.addEventListener('click', () => {
|
|
9364
|
+
const next = !snapshotToggle.classList.contains('on');
|
|
9365
|
+
snapshotToggle.classList.toggle('on', next); // optimistic; the answer (or a failed save's reload) sets it
|
|
9366
|
+
saveOcpConfig({ snapshots: next });
|
|
9367
|
+
});
|
|
9368
|
+
|
|
9369
|
+
// The configuration as GET / PATCH /api/opencode/config give it: the file SynaBun edits, compaction,
|
|
9370
|
+
// snapshots and the tools' permissions.
|
|
9371
|
+
function applyOcpConfig(view) {
|
|
9372
|
+
if (!view || typeof view !== 'object') return;
|
|
9373
|
+
_ocpConfig = view;
|
|
9374
|
+
const comp = view.compaction || {};
|
|
9375
|
+
if (compactAutoEl) compactAutoEl.checked = comp.auto !== false;
|
|
9376
|
+
if (compactReservedEl && document.activeElement !== compactReservedEl) compactReservedEl.value = Number.isFinite(comp.buffer) ? String(comp.buffer) : '';
|
|
9377
|
+
snapshotToggle?.classList.toggle('on', view.snapshots !== false);
|
|
9378
|
+
if (ocpConfigPathEl && view.file) ocpConfigPathEl.textContent = view.file;
|
|
9379
|
+
const jsonc = (Array.isArray(view.files) ? view.files : []).find((file) => file?.exists && /\.jsonc$/i.test(String(file.path || '')));
|
|
9380
|
+
if (ocpConfigJsoncEl) {
|
|
9381
|
+
ocpConfigJsoncEl.hidden = !jsonc;
|
|
9382
|
+
ocpConfigJsoncEl.innerHTML = jsonc ? kit.th('settings.redesign.opencode.jsoncWins', { path: `<code class="stg-code">${esc(jsonc.path)}</code>` }) : '';
|
|
9383
|
+
}
|
|
9384
|
+
renderToolPermissionsFrom(view);
|
|
9209
9385
|
}
|
|
9210
9386
|
|
|
9211
9387
|
// Capability filter pills — use CSS vars, not inline color
|
|
@@ -9243,28 +9419,16 @@ export async function openSettingsModal(options = {}) {
|
|
|
9243
9419
|
});
|
|
9244
9420
|
|
|
9245
9421
|
// ── Open config file in Synabun's editor ──
|
|
9422
|
+
// OpenCode 2's global opencode.json (made with its $schema when there is none yet).
|
|
9246
9423
|
overlay.querySelector('#stg-ocp-open-config')?.addEventListener('click', async () => {
|
|
9247
9424
|
try {
|
|
9248
|
-
|
|
9249
|
-
|
|
9250
|
-
|
|
9251
|
-
|
|
9252
|
-
|
|
9253
|
-
|
|
9254
|
-
if (revealResp.ok && revealData?.path) {
|
|
9255
|
-
const basePath = String(revealData.path).replace(/\\/g, '/').replace(/\/+$/, '');
|
|
9256
|
-
filePath = basePath.endsWith('/config.json') ? basePath : `${basePath}/config.json`;
|
|
9257
|
-
} else {
|
|
9258
|
-
const fileResp = await fetch('/api/opencode/config/file');
|
|
9259
|
-
const fileData = await fileResp.json();
|
|
9260
|
-
if (!fileResp.ok || !fileData?.path) {
|
|
9261
|
-
showCCToast(fileData?.error || revealData?.error || kit.tx('settings.redesign.opencode.failedToOpenOpencodeConfig'));
|
|
9262
|
-
return;
|
|
9263
|
-
}
|
|
9264
|
-
filePath = fileData.path;
|
|
9425
|
+
const fileResp = await fetch('/api/opencode/config/file');
|
|
9426
|
+
const fileData = await fileResp.json().catch(() => ({}));
|
|
9427
|
+
if (!fileResp.ok || !fileData?.path) {
|
|
9428
|
+
showCCToast(fileData?.error || kit.tx('settings.redesign.opencode.failedToOpenOpencodeConfig'));
|
|
9429
|
+
return;
|
|
9265
9430
|
}
|
|
9266
|
-
|
|
9267
|
-
emit('open-file-editor', { filePath });
|
|
9431
|
+
emit('open-file-editor', { filePath: fileData.path });
|
|
9268
9432
|
} catch (err) {
|
|
9269
9433
|
console.error('[settings] Failed to open OpenCode config:', err);
|
|
9270
9434
|
showCCToast(kit.tx('settings.redesign.opencode.failedToOpenOpencodeConfig'));
|
|
@@ -9292,38 +9456,46 @@ export async function openSettingsModal(options = {}) {
|
|
|
9292
9456
|
} catch {}
|
|
9293
9457
|
}
|
|
9294
9458
|
|
|
9295
|
-
// ── Load config values into UI ──
|
|
9459
|
+
// ── Load config values into UI (read from the files: OpenCode need not run) ──
|
|
9296
9460
|
async function loadOcpConfig() {
|
|
9297
|
-
|
|
9298
|
-
|
|
9299
|
-
|
|
9300
|
-
|
|
9301
|
-
|
|
9302
|
-
|
|
9303
|
-
|
|
9304
|
-
|
|
9305
|
-
|
|
9461
|
+
await renderToolPermissions();
|
|
9462
|
+
}
|
|
9463
|
+
|
|
9464
|
+
// The OpenCode SynaBun found: its version, or why SynaBun does not drive it and how to get OpenCode 2.
|
|
9465
|
+
function renderOcpInstall(installed) {
|
|
9466
|
+
const note = describeOpencodeInstall(installed, { th: kit.th, tx: kit.tx });
|
|
9467
|
+
if (ocpInstallEl) {
|
|
9468
|
+
ocpInstallEl.hidden = !note.html;
|
|
9469
|
+
ocpInstallEl.dataset.tone = note.tone;
|
|
9470
|
+
ocpInstallEl.innerHTML = note.html;
|
|
9471
|
+
}
|
|
9472
|
+
return note;
|
|
9306
9473
|
}
|
|
9307
9474
|
|
|
9308
9475
|
// ── Master refresh ──
|
|
9309
9476
|
async function refreshOcpStatus() {
|
|
9310
9477
|
setOcpStatus(kit.tx('settings.redesign.opencode.checking'), 'dim');
|
|
9478
|
+
loadOcpConfig();
|
|
9311
9479
|
try {
|
|
9312
9480
|
const resp = await fetch('/api/opencode/status');
|
|
9313
9481
|
const data = await resp.json();
|
|
9482
|
+
const note = renderOcpInstall(data.installed);
|
|
9314
9483
|
if (data.clearingHistory) {
|
|
9315
9484
|
setOcpStatus(kit.tx('settings.redesign.opencode.clearingHistory'), 'warn');
|
|
9316
9485
|
} else if (data.running) {
|
|
9317
9486
|
setOcpStatus(kit.tx('settings.redesign.opencode.runningVersion', { version: data.version ? ' v' + data.version : '' }), 'ok');
|
|
9318
|
-
await
|
|
9319
|
-
refreshOcpProviders(),
|
|
9320
|
-
loadOcpConfig(),
|
|
9321
|
-
renderToolPermissions(),
|
|
9322
|
-
]);
|
|
9487
|
+
await refreshOcpProviders();
|
|
9323
9488
|
} else {
|
|
9324
|
-
setOcpStatus(kit.tx('settings.redesign.opencode.
|
|
9489
|
+
if (note.status === 'notSupported') setOcpStatus(kit.tx('settings.redesign.opencode.notSupportedStatus'), 'err');
|
|
9490
|
+
else if (note.status === 'notInstalled') setOcpStatus(kit.tx('settings.redesign.opencode.notInstalledStatus'), 'dim');
|
|
9491
|
+
else setOcpStatus(kit.tx('settings.redesign.opencode.offline'), 'dim');
|
|
9325
9492
|
if (ocpProvidersEl) ocpProvidersEl.innerHTML = '<div class="settings-hint" style="opacity:0.4">' + kit.te('settings.redesign.opencode.startTheServerToSeeProviders') + '</div>';
|
|
9326
|
-
|
|
9493
|
+
// Something else stands in the way (another serve on the port, a serve that did not answer): the server's sentence.
|
|
9494
|
+
if (!note.html && data.problem && ocpInstallEl) {
|
|
9495
|
+
ocpInstallEl.hidden = false;
|
|
9496
|
+
ocpInstallEl.dataset.tone = 'warn';
|
|
9497
|
+
ocpInstallEl.textContent = data.problem;
|
|
9498
|
+
}
|
|
9327
9499
|
}
|
|
9328
9500
|
} catch (err) {
|
|
9329
9501
|
setOcpStatus(kit.tx('settings.redesign.opencode.error'), 'err');
|
|
@@ -9382,7 +9554,7 @@ export async function openSettingsModal(options = {}) {
|
|
|
9382
9554
|
// ── Search: the index is built from the strings of the active locale; what the panes load is read at query time ──
|
|
9383
9555
|
// In another locale the English names are searched too (someone who learned the settings in English still finds
|
|
9384
9556
|
// them): from the bundle i18n already holds as its fallback, or, when it has none, loaded once as the field is first used.
|
|
9385
|
-
const searchIndex = (en) => buildSettingsSearchIndex({ t: kit.tx, en, variants: variantTabs.map(tab => ({ id: tab.id, label: tab.label })) });
|
|
9557
|
+
const searchIndex = (en) => memoSearchIndex(en, variantTabs, () => buildSettingsSearchIndex({ t: kit.tx, en, variants: variantTabs.map(tab => ({ id: tab.id, label: tab.label })) }));
|
|
9386
9558
|
const english = getLocale() === 'en' ? null : englishTranslator();
|
|
9387
9559
|
wireSettingsSearch(overlay, {
|
|
9388
9560
|
index: searchIndex(english),
|
|
@@ -9580,7 +9752,18 @@ export function initSettings() {
|
|
|
9580
9752
|
const reopenPage = takeSettingsReopen();
|
|
9581
9753
|
if (reopenPage) setTimeout(() => openSettingsModal({ tab: reopenPage, expand: ['stg-sec-language'] }), 0);
|
|
9582
9754
|
|
|
9755
|
+
// Launch: what Settings is built from, and its search index (the English names too), read and made once the
|
|
9756
|
+
// page has painted, so the first open and the first search wait for nothing.
|
|
9757
|
+
registerLaunchPrefetch('settings-data', () => prefetchSettingsData(), { order: 60, owner: true });
|
|
9758
|
+
registerLaunchPrefetch('settings-search-index', () => prebuildSettingsSearchIndex(), { order: 62 });
|
|
9759
|
+
// Changes other windows announce: the next open shows them.
|
|
9760
|
+
on('sync:skin:changed', () => refreshSettingsData(['skins']));
|
|
9761
|
+
on('sync:greeting:changed', () => refreshSettingsData(['greeting']));
|
|
9762
|
+
on('sync:assistant:user-context-changed', () => refreshSettingsData(['userContext']));
|
|
9763
|
+
|
|
9583
9764
|
// Expose sync functions for inline onclick attributes (if any remain)
|
|
9584
9765
|
window.checkSyncStatus = checkSyncStatus;
|
|
9585
9766
|
window.copySyncPrompt = copySyncPrompt;
|
|
9586
9767
|
}
|
|
9768
|
+
|
|
9769
|
+
configureToolPickerTranslation((key, params) => kit.tx(key, params));
|