synabun 2.0.10 → 2.2.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 +207 -0
- package/README.md +25 -2
- package/THIRD-PARTY-LICENSES.md +22 -0
- package/bin/synabun.js +71 -24
- package/docs/whatsapp.md +13 -13
- 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/lib/claude-hooks.js +172 -18
- package/lib/client-config-repair.js +30 -17
- package/lib/desktop-app.js +7 -1
- package/lib/external-tools.js +7 -6
- 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 +37 -4
- package/lib/tools-cli.js +45 -0
- package/lib/update-snapshot.js +302 -0
- package/lib/virustotal.mjs +203 -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/budget-judgments.d.ts +1 -1
- package/mcp-server/dist/services/budget-judgments.d.ts.map +1 -1
- package/mcp-server/dist/services/budget-judgments.js +5 -3
- package/mcp-server/dist/services/budget-judgments.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/identity.d.ts +2 -2
- package/mcp-server/dist/services/identity.js +2 -2
- 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/memory-sessions.d.ts +1 -1
- package/mcp-server/dist/services/memory-sessions.d.ts.map +1 -1
- package/mcp-server/dist/services/memory-sessions.js +28 -2
- package/mcp-server/dist/services/memory-sessions.js.map +1 -1
- package/mcp-server/dist/services/neural-interface.d.ts +18 -11
- 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 +22 -63
- 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/typesafe-config.js +1 -1
- package/mcp-server/dist/services/typesafe-config.js.map +1 -1
- 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 +14 -14
- package/mcp-server/dist/tools/agents.d.ts.map +1 -1
- package/mcp-server/dist/tools/agents.js +29 -19
- 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/choice.d.ts +6 -0
- package/mcp-server/dist/tools/choice.d.ts.map +1 -1
- package/mcp-server/dist/tools/choice.js +18 -13
- package/mcp-server/dist/tools/choice.js.map +1 -1
- package/mcp-server/dist/tools/git-tools.js +2 -2
- package/mcp-server/dist/tools/git-tools.js.map +1 -1
- 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/morelogin.d.ts.map +1 -1
- package/mcp-server/dist/tools/morelogin.js +9 -6
- package/mcp-server/dist/tools/morelogin.js.map +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 +642 -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/budget-judgments.ts +5 -3
- package/mcp-server/src/services/codex-config-heal.ts +80 -29
- package/mcp-server/src/services/identity.ts +2 -2
- package/mcp-server/src/services/memory-judge-backfill.ts +7 -1
- package/mcp-server/src/services/memory-sessions.ts +24 -3
- package/mcp-server/src/services/neural-interface.ts +81 -21
- package/mcp-server/src/services/server-instructions.ts +29 -64
- 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/typesafe-config.ts +1 -1
- package/mcp-server/src/services/whiteboard-geometry.ts +94 -9
- package/mcp-server/src/tools/agents.ts +29 -20
- package/mcp-server/src/tools/choice.ts +17 -12
- package/mcp-server/src/tools/git-tools.ts +2 -2
- package/mcp-server/src/tools/image-tools.ts +1 -1
- package/mcp-server/src/tools/morelogin.ts +6 -4
- package/mcp-server/src/tools/skill-tools.ts +592 -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 +3953 -228
- package/neural-interface/i18n/pt-BR.json +3901 -179
- package/neural-interface/lib/agent-sandbox-elevation.js +6 -0
- package/neural-interface/lib/antigravity/acp-client-host.js +554 -0
- package/neural-interface/lib/antigravity/acp-connection.js +299 -0
- package/neural-interface/lib/antigravity/bypass.js +100 -0
- package/neural-interface/lib/antigravity/catalog.js +273 -0
- package/neural-interface/lib/antigravity/home.js +260 -0
- package/neural-interface/lib/antigravity/migrate-gemini.js +139 -0
- package/neural-interface/lib/antigravity/panel-bridge.js +1058 -0
- package/neural-interface/lib/antigravity/policy-hook.mjs +78 -0
- package/neural-interface/lib/antigravity/policy.js +74 -0
- package/neural-interface/lib/antigravity/tool-call.js +178 -0
- package/neural-interface/lib/antigravity/transcripts.js +252 -0
- package/neural-interface/lib/antigravity/usage.js +43 -0
- package/neural-interface/lib/assistant-api.js +128 -28
- package/neural-interface/lib/assistant-brains/antigravity.js +423 -0
- 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 +120 -54
- package/neural-interface/lib/assistant-brains/opencode2-browser-policy/index.js +28 -13
- package/neural-interface/lib/assistant-brains/opencode2-route-gate/index.js +59 -19
- package/neural-interface/lib/assistant-budget-planner.js +7 -3
- package/neural-interface/lib/assistant-budget-split.js +42 -15
- package/neural-interface/lib/assistant-catalog.js +81 -15
- package/neural-interface/lib/assistant-config.js +6 -3
- package/neural-interface/lib/assistant-dispatch.js +1073 -200
- package/neural-interface/lib/assistant-envelope.js +156 -2
- package/neural-interface/lib/assistant-evidence.js +80 -0
- package/neural-interface/lib/assistant-hidden-models.js +7 -5
- package/neural-interface/lib/assistant-persona.js +93 -19
- package/neural-interface/lib/assistant-plan-permissions.js +25 -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 +245 -38
- package/neural-interface/lib/assistant-runtime.js +214 -30
- package/neural-interface/lib/assistant-task-prompt.js +87 -21
- package/neural-interface/lib/assistant-tools.js +51 -0
- package/neural-interface/lib/assistant-usage.js +29 -0
- package/neural-interface/lib/backup-service.js +626 -4
- 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 +84 -0
- package/neural-interface/lib/browser-network-guard.js +476 -0
- package/neural-interface/lib/browser-policy-roots.js +64 -0
- package/neural-interface/lib/browser-tool-policy.js +198 -38
- package/neural-interface/lib/claude-agent-bridge.js +11 -2
- package/neural-interface/lib/claude-history-cache.js +210 -0
- package/neural-interface/lib/claude-hooks-status.js +103 -0
- package/neural-interface/lib/claude-model-catalog.js +5 -1
- package/neural-interface/lib/claude-temporary.js +5 -11
- package/neural-interface/lib/cli-install-api.js +1 -1
- package/neural-interface/lib/cli-install.js +163 -154
- package/neural-interface/lib/cli-resolver.js +13 -3
- package/neural-interface/lib/cli-update-planner.js +52 -41
- package/neural-interface/lib/cloudflared/tunnel.js +42 -2
- package/neural-interface/lib/codex-mcp-approvals.js +11 -0
- package/neural-interface/lib/codex-rollout-reader.js +52 -0
- package/neural-interface/lib/codex-worker-app-server.js +191 -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 +68 -68
- package/neural-interface/lib/editor-git-api.js +3 -20
- package/neural-interface/lib/exec-loop-command.js +13 -10
- package/neural-interface/lib/file-explorer-api.js +74 -40
- package/neural-interface/lib/file-explorer-watch.js +9 -3
- package/neural-interface/lib/git-bash/install.js +52 -6
- package/neural-interface/lib/git-bash/manager.js +59 -17
- package/neural-interface/lib/git-bash/pins.js +12 -0
- package/neural-interface/lib/git-read.js +2 -2
- package/neural-interface/lib/git-status-runner.js +390 -0
- package/neural-interface/lib/git-status-watchdog.js +40 -0
- package/neural-interface/lib/greeting/budgets.js +1 -0
- package/neural-interface/lib/greeting/engine.js +2 -2
- package/neural-interface/lib/greeting/sources.js +10 -12
- package/neural-interface/lib/greeting/writer.js +1 -1
- package/neural-interface/lib/http-guards.js +8 -2
- package/neural-interface/lib/install-details.js +4 -1
- package/neural-interface/lib/mcp-agent-servers.js +67 -0
- package/neural-interface/lib/mcp-client-config.js +35 -33
- package/neural-interface/lib/mcp-external-servers.js +332 -0
- package/neural-interface/lib/mcp-installer.js +2 -1
- package/neural-interface/lib/{mcp-profile-refresh-coordinator.js → mcp-tools-refresh-coordinator.js} +4 -4
- package/neural-interface/lib/memory-session-capture.js +17 -3
- package/neural-interface/lib/morelogin.js +177 -25
- package/neural-interface/lib/native-loop-providers.js +965 -66
- package/neural-interface/lib/native-loop-runtime.js +290 -42
- package/neural-interface/lib/opencode-config.js +557 -0
- package/neural-interface/lib/opencode-db-schema.js +17 -34
- package/neural-interface/lib/opencode-history.js +48 -218
- package/neural-interface/lib/opencode-mcp-ready.js +108 -0
- package/neural-interface/lib/opencode-native-boundary.js +53 -0
- package/neural-interface/lib/opencode-protocol.js +249 -112
- package/neural-interface/lib/opencode-runtime-config.js +32 -22
- package/neural-interface/lib/opencode-serve-records.js +39 -21
- package/neural-interface/lib/opencode-v2-client.js +48 -153
- package/neural-interface/lib/opencode-v2-ws-requests.js +36 -210
- package/neural-interface/lib/opencode-worker-data.js +27 -0
- package/neural-interface/lib/opencode2-client.js +73 -37
- package/neural-interface/lib/opencode2-proxy.js +23 -63
- package/neural-interface/lib/opencode2-translate.js +51 -2
- package/neural-interface/lib/owner-authority.js +249 -0
- package/neural-interface/lib/page-session.js +275 -0
- package/neural-interface/lib/path-confine.js +12 -9
- package/neural-interface/lib/peer-identity.js +793 -0
- package/neural-interface/lib/pty-host/core.js +28 -1
- package/neural-interface/lib/pty-host/lifetime.js +12 -0
- package/neural-interface/lib/pty-host/manager.js +12 -1
- package/neural-interface/lib/remote-policy.js +1 -1
- package/neural-interface/lib/request-gate.js +20 -2
- package/neural-interface/lib/rulesets/api.js +7 -6
- package/neural-interface/lib/rulesets/fs-safe.js +1 -1
- package/neural-interface/lib/rulesets/installer.js +180 -36
- package/neural-interface/lib/rulesets/json-edit.js +1 -1
- package/neural-interface/lib/rulesets/render.js +1 -1
- package/neural-interface/lib/session-cache-builder.js +81 -31
- package/neural-interface/lib/session-title-generator.js +58 -36
- 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 +298 -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 +285 -0
- package/neural-interface/lib/skills/creation.js +843 -0
- package/neural-interface/lib/skills/deploy.js +569 -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 +216 -0
- package/neural-interface/lib/skills/eval-confinement.js +399 -0
- package/neural-interface/lib/skills/eval-sandbox.js +280 -0
- package/neural-interface/lib/skills/eval-trace.js +135 -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 +203 -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 +441 -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 +269 -0
- package/neural-interface/lib/skills/runtime-checks.js +358 -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 +530 -0
- package/neural-interface/lib/skills/telemetry.js +119 -0
- package/neural-interface/lib/sqlite-integrity-worker.js +22 -0
- package/neural-interface/lib/synabun-version.js +1 -1
- package/neural-interface/lib/system-activity.js +1 -1
- package/neural-interface/lib/tool-lists.js +124 -0
- package/neural-interface/lib/whatsapp/brain.js +7 -3
- package/neural-interface/lib/whatsapp/bridge.js +6 -3
- package/neural-interface/lib/whatsapp/cards.js +22 -9
- package/neural-interface/lib/whatsapp/config.js +1 -1
- package/neural-interface/lib/worker-confinement.js +1268 -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-question.js +74 -0
- package/neural-interface/lib/worker-relay-ports.js +227 -0
- package/neural-interface/lib/worker-resume.js +238 -0
- package/neural-interface/lib/worker-sandbox.js +329 -0
- package/neural-interface/package-lock.json +34 -12
- package/neural-interface/package.json +3 -2
- package/neural-interface/public/index.html +3 -1
- package/neural-interface/public/index2d.html +19 -11
- package/neural-interface/public/onboarding.html +3 -3
- package/neural-interface/public/shared/account-menu.js +8 -5
- package/neural-interface/public/shared/agent-runtime-options.js +125 -31
- package/neural-interface/public/shared/agy/agy-bypass.js +47 -0
- package/neural-interface/public/shared/agy/agy-client.js +266 -0
- package/neural-interface/public/shared/agy/agy-commands.js +120 -0
- package/neural-interface/public/shared/agy/agy-copy.js +326 -0
- package/neural-interface/public/shared/agy/agy-diff.js +161 -0
- package/neural-interface/public/shared/agy/agy-events.js +469 -0
- package/neural-interface/public/shared/agy/agy-greeting.js +26 -0
- package/neural-interface/public/shared/agy/agy-options.js +232 -0
- package/neural-interface/public/shared/agy/agy-panel.js +2543 -0
- package/neural-interface/public/shared/agy/agy-render.js +392 -0
- package/neural-interface/public/shared/agy/agy-setup.js +149 -0
- package/neural-interface/public/shared/agy/agy-styles.js +604 -0
- package/neural-interface/public/shared/agy/agy-tabs-logic.js +181 -0
- 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 +36 -6
- package/neural-interface/public/shared/assistant/asst-api.js +1 -1
- 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 +105 -44
- package/neural-interface/public/shared/assistant/asst-budget.js +5 -4
- package/neural-interface/public/shared/assistant/asst-control.js +205 -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 +16 -7
- package/neural-interface/public/shared/assistant/asst-limit-dock.js +45 -0
- package/neural-interface/public/shared/assistant/asst-limit-wait.js +327 -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 +7 -6
- package/neural-interface/public/shared/assistant/asst-panel.js +151 -19
- package/neural-interface/public/shared/assistant/asst-rack-styles.js +66 -49
- package/neural-interface/public/shared/assistant/asst-render.js +445 -82
- package/neural-interface/public/shared/assistant/asst-route.js +304 -53
- package/neural-interface/public/shared/assistant/asst-routes-editor.js +9 -8
- package/neural-interface/public/shared/assistant/asst-scenes.js +56 -1
- package/neural-interface/public/shared/assistant/asst-sidepanel.js +117 -10
- package/neural-interface/public/shared/assistant/asst-slash.js +17 -9
- package/neural-interface/public/shared/assistant/asst-state.js +87 -24
- package/neural-interface/public/shared/assistant/asst-stop.js +127 -0
- package/neural-interface/public/shared/assistant/asst-styles.js +93 -21
- package/neural-interface/public/shared/assistant/asst-tool-kinds.js +190 -23
- package/neural-interface/public/shared/assistant/asst-usage.js +2 -1
- package/neural-interface/public/shared/automation-context.js +5 -2
- package/neural-interface/public/shared/cc-dropdown.js +89 -19
- 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 +34 -10
- package/neural-interface/public/shared/cdx/cdx-panel.js +59 -151
- package/neural-interface/public/shared/cdx/cdx-preview.js +12 -0
- package/neural-interface/public/shared/cdx/cdx-protocol.js +39 -11
- package/neural-interface/public/shared/cdx/cdx-render.js +57 -38
- package/neural-interface/public/shared/cdx/cdx-requests.js +14 -4
- 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 +127 -44
- package/neural-interface/public/shared/cdx/cdx-ws.js +0 -1
- package/neural-interface/public/shared/cli-install-controller.js +218 -0
- package/neural-interface/public/shared/cli-install-view.js +11 -12
- package/neural-interface/public/shared/cli-status.js +11 -10
- package/neural-interface/public/shared/constants.js +5 -4
- 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 +367 -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-restore.js +162 -0
- package/neural-interface/public/shared/cp/cp-tool-cards.js +10 -3
- package/neural-interface/public/shared/crash-notice-view.js +168 -0
- package/neural-interface/public/shared/desktop-bridge.js +17 -0
- package/neural-interface/public/shared/desktop-titlebar-view.js +12 -0
- package/neural-interface/public/shared/desktop-titlebar.js +13 -4
- package/neural-interface/public/shared/desktop-update-view.js +21 -0
- package/neural-interface/public/shared/editor/editor-panel-logic.js +2 -2
- package/neural-interface/public/shared/explorer/fx-api.js +4 -4
- package/neural-interface/public/shared/explorer/fx-links.js +3 -3
- package/neural-interface/public/shared/explorer/fx-logic.js +1 -1
- package/neural-interface/public/shared/explorer/fx-main.js +48 -16
- package/neural-interface/public/shared/explorer/fx-tree.js +1 -0
- package/neural-interface/public/shared/greeting/greeting-card.js +2 -2
- package/neural-interface/public/shared/greeting/greeting-view.js +1 -0
- package/neural-interface/public/shared/html-shell.js +30 -15
- package/neural-interface/public/shared/i18n.js +2 -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-changes-logic.js +3 -101
- package/neural-interface/public/shared/ocp-v2/ocp-v2-childpanel.js +2 -0
- 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-outcome-notifications.js +16 -0
- package/neural-interface/public/shared/ocp-v2/ocp-v2-panel.js +74 -259
- package/neural-interface/public/shared/ocp-v2/ocp-v2-plan.js +1 -2
- package/neural-interface/public/shared/ocp-v2/ocp-v2-projectbar.js +95 -202
- package/neural-interface/public/shared/ocp-v2/ocp-v2-rehydrate.js +13 -4
- package/neural-interface/public/shared/ocp-v2/ocp-v2-render.js +13 -22
- package/neural-interface/public/shared/ocp-v2/ocp-v2-send.js +4 -4
- 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 +2 -91
- 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 +17 -21
- 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 +60 -50
- package/neural-interface/public/shared/page-session.js +96 -0
- package/neural-interface/public/shared/provider-icons.js +4 -2
- 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 +195 -61
- package/neural-interface/public/shared/settings/settings-claude-hooks.js +135 -0
- package/neural-interface/public/shared/settings/settings-data.js +3 -1
- package/neural-interface/public/shared/settings/settings-ia.js +6 -6
- package/neural-interface/public/shared/settings/settings-opencode.js +89 -0
- package/neural-interface/public/shared/settings/settings-rules.js +2 -2
- 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/showcase-privacy.js +6 -1
- package/neural-interface/public/shared/sidepanel-session-status.js +3 -3
- package/neural-interface/public/shared/skills/ss-args.js +96 -0
- package/neural-interface/public/shared/skills/ss-creation.js +222 -0
- package/neural-interface/public/shared/skills/ss-dialog.js +133 -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 +149 -0
- package/neural-interface/public/shared/skills/ss-presets.js +117 -0
- package/neural-interface/public/shared/skills/ss-profiles.js +513 -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 +237 -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 +230 -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-fixes-view.js +359 -0
- package/neural-interface/public/shared/skills/studio/sx-fixes.js +327 -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 +282 -0
- package/neural-interface/public/shared/skills/studio/sx-install.js +313 -0
- package/neural-interface/public/shared/skills/studio/sx-library-view.js +748 -0
- package/neural-interface/public/shared/skills/studio/sx-library.js +479 -0
- package/neural-interface/public/shared/skills/studio/sx-main.js +690 -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 +249 -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 +426 -0
- package/neural-interface/public/shared/state.js +1 -1
- package/neural-interface/public/shared/streaming-markdown.js +58 -0
- package/neural-interface/public/shared/styleguide/sg-panel.js +4 -2
- package/neural-interface/public/shared/styles.css +1822 -1756
- package/neural-interface/public/shared/synabun-mascot.js +1115 -9
- package/neural-interface/public/shared/text-cache.js +29 -0
- 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/topright-overflow-logic.js +54 -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-antigravity-panel.js +19 -0
- package/neural-interface/public/shared/ui-assistant.js +2 -0
- package/neural-interface/public/shared/ui-automation-studio.js +111 -140
- package/neural-interface/public/shared/ui-claude-panel.js +376 -82
- package/neural-interface/public/shared/ui-cli-banner.js +23 -167
- 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 +15 -3
- package/neural-interface/public/shared/ui-file-explorer.js +48 -6
- package/neural-interface/public/shared/ui-file-links.js +2 -2
- package/neural-interface/public/shared/ui-games.js +2 -2
- package/neural-interface/public/shared/ui-image-gallery.js +50 -47
- package/neural-interface/public/shared/ui-judgments.js +1 -1
- package/neural-interface/public/shared/ui-keybinds.js +33 -2
- package/neural-interface/public/shared/ui-link.js +3 -3
- package/neural-interface/public/shared/ui-menubar.js +10 -20
- package/neural-interface/public/shared/ui-native-loop-router-state.js +5 -0
- package/neural-interface/public/shared/ui-native-loop-router.js +11 -1
- package/neural-interface/public/shared/ui-navbar.js +49 -24
- package/neural-interface/public/shared/ui-notifications.js +1 -0
- package/neural-interface/public/shared/ui-panels.js +96 -65
- package/neural-interface/public/shared/ui-resume.js +9 -7
- package/neural-interface/public/shared/ui-schedules-studio.js +546 -431
- package/neural-interface/public/shared/ui-scroll-follow.js +16 -5
- package/neural-interface/public/shared/ui-sessions.js +27 -15
- package/neural-interface/public/shared/ui-settings.js +683 -526
- package/neural-interface/public/shared/ui-sidepanel-session-entry.js +9 -2
- package/neural-interface/public/shared/ui-sidepanel-sessions.js +4 -4
- package/neural-interface/public/shared/ui-sidepanel-state.js +23 -12
- package/neural-interface/public/shared/ui-sidepanel-tray.js +48 -7
- package/neural-interface/public/shared/ui-sidepanel-windows.js +18 -13
- package/neural-interface/public/shared/ui-skills.js +33 -2830
- package/neural-interface/public/shared/ui-styleguide.js +5 -2
- package/neural-interface/public/shared/ui-sync.js +15 -5
- package/neural-interface/public/shared/ui-terminal.js +270 -190
- package/neural-interface/public/shared/ui-tool-updates.js +6 -4
- package/neural-interface/public/shared/ui-tooltip.js +81 -51
- package/neural-interface/public/shared/ui-topright-overflow.js +233 -0
- package/neural-interface/public/shared/ui-trash.js +6 -4
- 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 -65
- package/neural-interface/public/shared/ui-tutorial.css +47 -100
- package/neural-interface/public/shared/ui-tutorial.js +285 -509
- package/neural-interface/public/shared/ui-update.js +38 -2
- package/neural-interface/public/shared/ui-whiteboard.js +312 -59
- package/neural-interface/public/shared/ui-worker-end.js +5 -1
- 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 +3 -3
- 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-questions.js +34 -0
- package/neural-interface/public/shared/worker-restart.js +12 -0
- package/neural-interface/public/sidepanel-session.html +2 -2
- 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 +32 -1
- package/neural-interface/public/variant/2d/minimap.js +11 -13
- package/neural-interface/public/variant/3d/main.js +69 -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/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 +32 -0
- package/neural-interface/server.js +4553 -2855
- package/neural-interface/templates/rulesets/README.md +1 -1
- package/neural-interface/templates/rulesets/core.md +1 -1
- package/neural-interface/templates/rulesets/legacy-hashes.json +23 -5
- package/neural-interface/templates/rulesets/manifest.json +8 -8
- package/npm-shrinkwrap.json +34 -12
- package/package.json +17 -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 +6 -6
- 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
- /package/neural-interface/templates/rulesets/{gemini.md → antigravity.md} +0 -0
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
// ═══════════════════════════════════════════
|
|
2
|
+
// SynaBun Assistant — the open card at the bottom of the transcript
|
|
3
|
+
// ═══════════════════════════════════════════
|
|
4
|
+
// A card that waits on the user (a follow-ups set, a question, a clarify, a route) must not be
|
|
5
|
+
// buried by what streams in after it: while it is open it is drawn in a slot under every other
|
|
6
|
+
// row (still above the "Working…" row), and rows appended meanwhile go above it. Once answered,
|
|
7
|
+
// dismissed or expired it goes back where it belongs: a follow-ups card under its message, a
|
|
8
|
+
// control or route card in the transcript. Cards are moved, never redrawn, so the picks, the
|
|
9
|
+
// typed answer, focus and the mascot's rig survive the move; a home row that was pruned (or a
|
|
10
|
+
// cleared transcript) takes its card with it.
|
|
11
|
+
//
|
|
12
|
+
// createAskTail(container, { anchor: () => node | null, slotClass? })
|
|
13
|
+
// → { pin(handle), sync(), insert(node), clear() }
|
|
14
|
+
// `handle`: { el, isOpen() → bool } (asst-render.js adds seq / onChange). `anchor` is the row
|
|
15
|
+
// that stays last (the "Working…" row); `insert` places a new row above the open cards, or
|
|
16
|
+
// above that row when none waits.
|
|
17
|
+
|
|
18
|
+
export const ASK_TAIL_CLASS = 'asst-ask-tail';
|
|
19
|
+
|
|
20
|
+
export function createAskTail(container, { anchor = () => null, slotClass = ASK_TAIL_CLASS } = {}) {
|
|
21
|
+
const pinned = new Set(); // { el, isOpen, home: { parent, next } | null, at: 'slot' | 'home' | null }
|
|
22
|
+
let slot = null;
|
|
23
|
+
let seq = 0;
|
|
24
|
+
|
|
25
|
+
const anchorNode = () => {
|
|
26
|
+
const node = anchor();
|
|
27
|
+
return node && node.parentNode === container ? node : null;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
function place(node, before = null) {
|
|
31
|
+
if (!node) return;
|
|
32
|
+
if (before && before.parentNode === container) {
|
|
33
|
+
if (node.parentNode === container && node.nextSibling === before) return; // already where it belongs
|
|
34
|
+
container.insertBefore(node, before);
|
|
35
|
+
} else {
|
|
36
|
+
if (node.parentNode === container && node === container.lastChild) return; // already last
|
|
37
|
+
container.appendChild(node);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function ensureSlot() {
|
|
42
|
+
if (!slot || slot.parentNode !== container) {
|
|
43
|
+
slot = container.ownerDocument.createElement('div');
|
|
44
|
+
slot.className = slotClass;
|
|
45
|
+
}
|
|
46
|
+
return slot;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/** The card back where it was: its own row (a follow-ups card), else the transcript above the open cards. */
|
|
50
|
+
function restore(handle) {
|
|
51
|
+
const { el, home } = handle;
|
|
52
|
+
const parent = home?.parent;
|
|
53
|
+
if (!parent?.isConnected) { el.remove(); return; }
|
|
54
|
+
// A direct row goes back above every card that still waits, never under one: the open slot is last.
|
|
55
|
+
if (parent === container) { place(el, slot?.parentNode === container ? slot : anchorNode()); return; }
|
|
56
|
+
if (home.next && home.next.parentNode === parent) parent.insertBefore(el, home.next);
|
|
57
|
+
else parent.appendChild(el);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/** Every open card in the slot, in the order they appeared; every closed one back home. */
|
|
61
|
+
function sync() {
|
|
62
|
+
for (const handle of [...pinned]) {
|
|
63
|
+
if (!handle.el?.isConnected) { pinned.delete(handle); continue; }
|
|
64
|
+
// Its message row was pruned (the transcript cap): the card goes with it, never waits alone.
|
|
65
|
+
if (handle.at === 'slot' && handle.home?.parent && !handle.home.parent.isConnected) {
|
|
66
|
+
handle.el.remove();
|
|
67
|
+
pinned.delete(handle);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
const open = [];
|
|
71
|
+
for (const handle of pinned) {
|
|
72
|
+
if (handle.isOpen?.()) { open.push(handle); continue; }
|
|
73
|
+
if (handle.at !== 'slot') continue;
|
|
74
|
+
restore(handle);
|
|
75
|
+
handle.at = 'home';
|
|
76
|
+
}
|
|
77
|
+
if (!open.length) {
|
|
78
|
+
slot?.remove();
|
|
79
|
+
slot = null;
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
const box = ensureSlot();
|
|
83
|
+
place(box, anchorNode());
|
|
84
|
+
open.sort((a, b) => a.seq - b.seq);
|
|
85
|
+
const order = open.map((handle) => handle.el);
|
|
86
|
+
const same = box.childNodes.length === order.length && order.every((el, i) => box.childNodes[i] === el);
|
|
87
|
+
if (!same) for (const el of order) box.appendChild(el);
|
|
88
|
+
for (const handle of open) handle.at = 'slot';
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/** A card that waits on the user joins the bottom slot now; sync() puts it back once it closes. */
|
|
92
|
+
function pin(handle) {
|
|
93
|
+
if (!handle?.el) return handle;
|
|
94
|
+
handle.seq ||= (seq += 1);
|
|
95
|
+
if (pinned.has(handle)) { sync(); return handle; }
|
|
96
|
+
handle.home = handle.el.parentNode ? { parent: handle.el.parentNode, next: handle.el.nextSibling } : null;
|
|
97
|
+
handle.at = null;
|
|
98
|
+
pinned.add(handle);
|
|
99
|
+
sync();
|
|
100
|
+
return handle;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/** A new transcript row: above the open cards (and above the "Working…" row when it is last). */
|
|
104
|
+
function insert(node) {
|
|
105
|
+
place(node, slot?.parentNode === container ? slot : anchorNode());
|
|
106
|
+
return node;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/** Forget the cards and put them back home (a cleared transcript keeps none). */
|
|
110
|
+
function clear() {
|
|
111
|
+
for (const handle of pinned) if (handle.at === 'slot') restore(handle);
|
|
112
|
+
pinned.clear();
|
|
113
|
+
slot?.remove();
|
|
114
|
+
slot = null;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
return { pin, sync, insert, clear, get slot() { return slot; } };
|
|
118
|
+
}
|
|
@@ -0,0 +1,397 @@
|
|
|
1
|
+
// ═══════════════════════════════════════════
|
|
2
|
+
// SynaBun Assistant — the cards the mascot presents: the shared kit
|
|
3
|
+
// ═══════════════════════════════════════════
|
|
4
|
+
// What a finished turn offers (the follow-ups card, asst-followups-card.js)
|
|
5
|
+
// and what the brain asks (the question cards of asst-control.js: agent_clarify,
|
|
6
|
+
// AskUserQuestion, Codex's choice and requestUserInput, OpenCode's question)
|
|
7
|
+
// are drawn the same way: the mascot beside the card, presenting, asking and
|
|
8
|
+
// reacting, its words in a bubble and the answers under it. This module is the
|
|
9
|
+
// part they share:
|
|
10
|
+
//
|
|
11
|
+
// - createPresenter(): the mascot's slot. A painted still (paintMascot +
|
|
12
|
+
// stillFrame, never a rig) until the renderer makes it live (setLive): then a
|
|
13
|
+
// rig from synabun-mascot.js, drawn by the one shared ticker and parked off
|
|
14
|
+
// screen by the rig's own observer. Only the newest open card of a panel is
|
|
15
|
+
// live, and none while calm (replay, reduced motion, a drag): asst-render.js
|
|
16
|
+
// activateRigs. `target` is what the panel's director (asst-mascot.js) dresses
|
|
17
|
+
// while this presenter is the character on screen; `waits` marks a card that
|
|
18
|
+
// waits on the user itself (a question card), which the director never
|
|
19
|
+
// poses "blocked" at its own card.
|
|
20
|
+
// - askOption() / setOptionChecked(): one option row (key · label · description
|
|
21
|
+
// · badge), the same markup in both kinds of card.
|
|
22
|
+
// - optionKeyIntent() (pure) and the wiring around it: one tab stop per option
|
|
23
|
+
// group (arrows, Home, End), 1-9 pick an option while focus is in the card,
|
|
24
|
+
// Esc goes back to the composer; the mascot looks at the option pointed at
|
|
25
|
+
// or focused (wireGaze) and watches a text field being typed in (wireWatch).
|
|
26
|
+
// Nothing touches the DOM at import: the server imports asst-control.js
|
|
27
|
+
// (WhatsApp cards) and node:test draws cards with a stand-in document.
|
|
28
|
+
|
|
29
|
+
import * as mascotLib from '../synabun-mascot.js';
|
|
30
|
+
import { isReducedMotion } from '../synabun-ticker.js';
|
|
31
|
+
import { lookToward } from './asst-mascot.js';
|
|
32
|
+
import { injectAskStyles } from './asst-ask-styles.js';
|
|
33
|
+
|
|
34
|
+
export { injectAskStyles };
|
|
35
|
+
|
|
36
|
+
/** The presenter's SVG, before the sheet's container queries shrink it. */
|
|
37
|
+
export const PRESENTER_SIZE = Object.freeze({ width: 64, height: 32 });
|
|
38
|
+
const HOP_MS = 320; // the hop (300 ms) and a frame to land on
|
|
39
|
+
const WATCH_IDLE_MS = 1500; // a field nobody typed in for this long: back to the card's own pose
|
|
40
|
+
const WATCH_EVERY_MS = 120; // the caret is looked at no more often than this
|
|
41
|
+
// Where a still looks: toward what the card offers (the answers sit right of the face and under it).
|
|
42
|
+
const STILL_LOOK = Object.freeze({ present: { x: 0.5, y: 0.35 }, pick: { x: 0.5, y: 0.25 }, ask: { x: 0.35, y: -0.15 }, success: { x: 0, y: 0 }, idle: { x: 0, y: 0 } });
|
|
43
|
+
|
|
44
|
+
function el(tag, className, text) {
|
|
45
|
+
const node = document.createElement(tag);
|
|
46
|
+
if (className) node.className = className;
|
|
47
|
+
if (text != null) node.textContent = text;
|
|
48
|
+
return node;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function reduced() {
|
|
52
|
+
try { return !!isReducedMotion(); } catch { return false; }
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const centre = (node) => {
|
|
56
|
+
try {
|
|
57
|
+
const r = node?.getBoundingClientRect?.();
|
|
58
|
+
return r && r.width ? { x: r.left + r.width / 2, y: r.top + r.height / 2 } : null;
|
|
59
|
+
} catch { return null; }
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
/** A text field: typing there never picks an option (1-9 are its characters). */
|
|
63
|
+
export function isTextField(node) {
|
|
64
|
+
const tag = String(node?.tagName || '').toUpperCase();
|
|
65
|
+
return tag === 'INPUT' || tag === 'TEXTAREA' || !!node?.isContentEditable;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* createPresenter({ pose, options, waits, entrance })
|
|
70
|
+
* → { el, target, setLive(on), isLive(), setPose(name, opts), react(name), lookAt(node|null), setHome(node),
|
|
71
|
+
* watch(point), unwatch(), settle(pose, opts, { hop }), destroy() }
|
|
72
|
+
* `pose` / `options`: the card's own pose (present, pick, ask). `entrance`: the reaction it plays the first
|
|
73
|
+
* time it goes live (none on a card drawn while calm). settle(): the card is answered or left behind: the
|
|
74
|
+
* still it ends on ('success' is the "got it" face), after the hop when `hop` and it is live.
|
|
75
|
+
*/
|
|
76
|
+
export function createPresenter({ pose = 'present', options = null, waits = false, entrance = null } = {}) {
|
|
77
|
+
const face = el('span', 'asst-ask-face');
|
|
78
|
+
face.setAttribute('aria-hidden', 'true');
|
|
79
|
+
let want = { pose, options: options && typeof options === 'object' ? { ...options } : {} };
|
|
80
|
+
let rig = null;
|
|
81
|
+
let held = false; // the director dresses the rig (asst-mascot.js hold / restore)
|
|
82
|
+
let watching = false;
|
|
83
|
+
let watchedAt = -Infinity;
|
|
84
|
+
let final = null; // { pose, opts }: settled, a still from now on
|
|
85
|
+
let leaving = 0; // the hop plays out before the settled still
|
|
86
|
+
let destroyed = false;
|
|
87
|
+
let home = null; // what it looks at when nothing is pointed at
|
|
88
|
+
let looking = null; // what it looks at now
|
|
89
|
+
let entered = false;
|
|
90
|
+
|
|
91
|
+
const stillOpts = (name, opts = {}) => ({ ...opts, look: opts.look || STILL_LOOK[name] || { x: 0, y: 0 } });
|
|
92
|
+
|
|
93
|
+
function dropRig() {
|
|
94
|
+
if (!rig) return;
|
|
95
|
+
try { rig.destroy(); } catch { /* gone */ }
|
|
96
|
+
rig = null;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function paintStill(name, opts) {
|
|
100
|
+
dropRig();
|
|
101
|
+
try {
|
|
102
|
+
face.innerHTML = mascotLib.mascotSvg({ width: PRESENTER_SIZE.width, height: PRESENTER_SIZE.height, className: 'synabun-mascot asst-still' });
|
|
103
|
+
const svg = face.firstElementChild;
|
|
104
|
+
if (svg && typeof svg.setAttribute === 'function') {
|
|
105
|
+
svg.setAttribute('data-pose', name);
|
|
106
|
+
mascotLib.paintMascot(svg, mascotLib.stillFrame(name, stillOpts(name, opts)));
|
|
107
|
+
}
|
|
108
|
+
} catch { /* no SVG here (a stand-in document): the card reads without its face */ }
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function look(node) {
|
|
112
|
+
looking = node || null;
|
|
113
|
+
if (!rig || held || watching || final) return;
|
|
114
|
+
const point = centre(looking || home);
|
|
115
|
+
if (!point) return;
|
|
116
|
+
let box = null;
|
|
117
|
+
try { box = rig.el.getBoundingClientRect(); } catch { box = null; }
|
|
118
|
+
const to = lookToward(box, point);
|
|
119
|
+
try { rig.look(to.x, to.y); } catch { /* gone */ }
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function applyWant() {
|
|
123
|
+
if (!rig || held || watching || final) return;
|
|
124
|
+
try { rig.setPose(want.pose, want.options); } catch { /* keeps its pose */ }
|
|
125
|
+
look(looking);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function setLive(on) {
|
|
129
|
+
if (destroyed || final) return;
|
|
130
|
+
if (on && !rig) {
|
|
131
|
+
try {
|
|
132
|
+
face.textContent = '';
|
|
133
|
+
rig = mascotLib.createMascot(face, { width: PRESENTER_SIZE.width, height: PRESENTER_SIZE.height, pose: want.pose, poseOptions: want.options });
|
|
134
|
+
} catch { rig = null; paintStill(want.pose, want.options); return; }
|
|
135
|
+
look(looking);
|
|
136
|
+
if (!entered) {
|
|
137
|
+
entered = true;
|
|
138
|
+
if (entrance && !reduced()) { try { rig.react(entrance); } catch { /* still */ } }
|
|
139
|
+
}
|
|
140
|
+
} else if (!on && rig) {
|
|
141
|
+
watching = false;
|
|
142
|
+
paintStill(want.pose, want.options);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function settle(name = 'success', opts = {}, { hop = false } = {}) {
|
|
147
|
+
if (destroyed) return;
|
|
148
|
+
final = { pose: name, opts };
|
|
149
|
+
watching = false;
|
|
150
|
+
face.classList.add('is-settled');
|
|
151
|
+
if (leaving) { clearTimeout(leaving); leaving = 0; }
|
|
152
|
+
if (hop && rig && !reduced()) {
|
|
153
|
+
try { rig.react('hop'); } catch { /* lands at once */ }
|
|
154
|
+
leaving = setTimeout(() => { leaving = 0; if (!destroyed && final) paintStill(final.pose, final.opts); }, HOP_MS);
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
paintStill(name, opts);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
const target = {
|
|
161
|
+
kind: 'card',
|
|
162
|
+
waits: !!waits,
|
|
163
|
+
get rig() { return rig; },
|
|
164
|
+
get el() { return rig ? rig.el : face; },
|
|
165
|
+
hold(on) { held = !!on; },
|
|
166
|
+
restore() { applyWant(); },
|
|
167
|
+
};
|
|
168
|
+
|
|
169
|
+
paintStill(want.pose, want.options);
|
|
170
|
+
|
|
171
|
+
return {
|
|
172
|
+
el: face,
|
|
173
|
+
target,
|
|
174
|
+
setLive,
|
|
175
|
+
isLive: () => !!rig && !final,
|
|
176
|
+
/** The card's own pose; while the director holds the rig (or a field is watched) it waits for its turn. */
|
|
177
|
+
setPose(name, opts = null) {
|
|
178
|
+
want = { pose: name, options: opts && typeof opts === 'object' ? { ...opts } : {} };
|
|
179
|
+
if (final || destroyed) return;
|
|
180
|
+
if (rig) applyWant();
|
|
181
|
+
else paintStill(want.pose, want.options);
|
|
182
|
+
},
|
|
183
|
+
react(name) {
|
|
184
|
+
if (!rig || final || destroyed || reduced()) return;
|
|
185
|
+
try { rig.react(name); } catch { /* still */ }
|
|
186
|
+
},
|
|
187
|
+
/** Eyes on `node` (an option, a button); null: back to what it offers first. */
|
|
188
|
+
lookAt: look,
|
|
189
|
+
setHome(node) { home = node || null; if (!looking) look(null); },
|
|
190
|
+
/** Someone types in the card's field: the watch pose, its eyes on the caret (`point`, client px). */
|
|
191
|
+
watch(point) {
|
|
192
|
+
if (!rig || held || final || !point) return;
|
|
193
|
+
if (!watching) {
|
|
194
|
+
watching = true;
|
|
195
|
+
try { rig.setPose('watch'); } catch { /* keeps its pose */ }
|
|
196
|
+
}
|
|
197
|
+
const now = Date.now();
|
|
198
|
+
if (now - watchedAt < WATCH_EVERY_MS) return;
|
|
199
|
+
watchedAt = now;
|
|
200
|
+
let box = null;
|
|
201
|
+
try { box = rig.el.getBoundingClientRect(); } catch { box = null; }
|
|
202
|
+
const to = lookToward(box, point, { horizontal: true });
|
|
203
|
+
try { rig.look(to.x, to.y); } catch { /* gone */ }
|
|
204
|
+
},
|
|
205
|
+
unwatch() {
|
|
206
|
+
if (!watching) return;
|
|
207
|
+
watching = false;
|
|
208
|
+
applyWant();
|
|
209
|
+
},
|
|
210
|
+
settle,
|
|
211
|
+
destroy() {
|
|
212
|
+
if (destroyed) return;
|
|
213
|
+
destroyed = true;
|
|
214
|
+
if (leaving) { clearTimeout(leaving); leaving = 0; }
|
|
215
|
+
dropRig();
|
|
216
|
+
},
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
// ── Options ──
|
|
221
|
+
|
|
222
|
+
/**
|
|
223
|
+
* One option row: [key] label · description [badge] [↵]. `role` 'radio' / 'checkbox' (or none: a plain
|
|
224
|
+
* action button). The key shows its number shortcut (1-9); a checked checkbox shows ✓ instead.
|
|
225
|
+
*/
|
|
226
|
+
export function askOption({ label, description = '', index = 0, role = '', badge = '', go = false, title = '' } = {}) {
|
|
227
|
+
const btn = el('button', 'asst-ask-option');
|
|
228
|
+
btn.type = 'button';
|
|
229
|
+
btn.dataset.index = String(index);
|
|
230
|
+
btn.setAttribute('data-ask-option', '');
|
|
231
|
+
if (role) {
|
|
232
|
+
btn.setAttribute('role', role);
|
|
233
|
+
btn.setAttribute('aria-checked', 'false');
|
|
234
|
+
}
|
|
235
|
+
if (title) btn.title = title;
|
|
236
|
+
const key = el('span', 'asst-ask-key', index < 9 ? String(index + 1) : '');
|
|
237
|
+
key.setAttribute('aria-hidden', 'true');
|
|
238
|
+
btn.appendChild(key);
|
|
239
|
+
const main = el('span', 'asst-ask-main');
|
|
240
|
+
main.appendChild(el('span', 'asst-ask-label', label));
|
|
241
|
+
if (description) main.appendChild(el('span', 'asst-ask-desc', description));
|
|
242
|
+
btn.appendChild(main);
|
|
243
|
+
if (badge) btn.appendChild(el('span', 'asst-ask-badge', badge));
|
|
244
|
+
if (go) {
|
|
245
|
+
const arrow = el('span', 'asst-ask-go', '↵');
|
|
246
|
+
arrow.setAttribute('aria-hidden', 'true');
|
|
247
|
+
btn.appendChild(arrow);
|
|
248
|
+
}
|
|
249
|
+
btn._askKey = key;
|
|
250
|
+
return btn;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
/** Check or clear an option row (aria-checked, the gold, a checkbox's ✓). */
|
|
254
|
+
export function setOptionChecked(btn, on) {
|
|
255
|
+
if (!btn) return;
|
|
256
|
+
btn.classList.toggle('selected', !!on);
|
|
257
|
+
if (btn.getAttribute?.('role') || btn.attributes?.role) btn.setAttribute('aria-checked', on ? 'true' : 'false');
|
|
258
|
+
const key = btn._askKey;
|
|
259
|
+
const role = btn.getAttribute?.('role') || btn.attributes?.role || '';
|
|
260
|
+
if (key && role === 'checkbox') {
|
|
261
|
+
const index = Number(btn.dataset.index);
|
|
262
|
+
key.textContent = on ? '✓' : (index < 9 ? String(index + 1) : '');
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
/**
|
|
267
|
+
* What a key does in a card: { composer } (Esc: back to the message box), { pick: i } (1-9, never while
|
|
268
|
+
* typing in a field), { move: i } (arrows, Home, End inside an option group: `index` is the focused option,
|
|
269
|
+
* -1 when focus is elsewhere), or null.
|
|
270
|
+
*/
|
|
271
|
+
export function optionKeyIntent({ key, index = -1, count = 0, typing = false, alt = false, ctrl = false, meta = false } = {}) {
|
|
272
|
+
if (key === 'Escape') return { composer: true };
|
|
273
|
+
if (typing || alt || ctrl || meta) return null;
|
|
274
|
+
if (/^[1-9]$/.test(String(key))) {
|
|
275
|
+
const i = Number(key) - 1;
|
|
276
|
+
return i < count ? { pick: i } : null;
|
|
277
|
+
}
|
|
278
|
+
if (index < 0 || count < 1) return null;
|
|
279
|
+
switch (key) {
|
|
280
|
+
case 'ArrowDown':
|
|
281
|
+
case 'ArrowRight': return { move: (index + 1) % count };
|
|
282
|
+
case 'ArrowUp':
|
|
283
|
+
case 'ArrowLeft': return { move: (index - 1 + count) % count };
|
|
284
|
+
case 'Home': return { move: 0 };
|
|
285
|
+
case 'End': return { move: count - 1 };
|
|
286
|
+
default: return null;
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
const usable = (btn) => !!btn && !btn.disabled && !btn.hidden;
|
|
291
|
+
|
|
292
|
+
/**
|
|
293
|
+
* One tab stop for an option group (roving tabindex): the checked option, else the first. Arrows, Home and
|
|
294
|
+
* End move focus; `onMove(button, index)` follows it (a radio group checks the option, as radios do).
|
|
295
|
+
*/
|
|
296
|
+
export function wireOptionGroup(group, { options = () => [], onMove = null } = {}) {
|
|
297
|
+
const all = () => (typeof options === 'function' ? options() : []) || [];
|
|
298
|
+
function sync(current = null) {
|
|
299
|
+
const list = all();
|
|
300
|
+
const checked = list.find((b) => (b.getAttribute?.('aria-checked') || b.attributes?.['aria-checked']) === 'true');
|
|
301
|
+
const pick = list.includes(current) ? current : (checked || list.find(usable) || list[0]);
|
|
302
|
+
for (const b of list) b.tabIndex = b === pick ? 0 : -1;
|
|
303
|
+
}
|
|
304
|
+
group.addEventListener('keydown', (e) => {
|
|
305
|
+
const list = all().filter(usable);
|
|
306
|
+
const index = list.indexOf(e.target);
|
|
307
|
+
if (index < 0) return;
|
|
308
|
+
const intent = optionKeyIntent({ key: e.key, index, count: list.length, alt: e.altKey, ctrl: e.ctrlKey, meta: e.metaKey });
|
|
309
|
+
if (!intent || intent.move == null) return;
|
|
310
|
+
e.preventDefault();
|
|
311
|
+
e.stopPropagation();
|
|
312
|
+
const next = list[intent.move];
|
|
313
|
+
sync(next);
|
|
314
|
+
try { next.focus(); } catch { /* not focusable here */ }
|
|
315
|
+
onMove?.(next, intent.move);
|
|
316
|
+
});
|
|
317
|
+
group.addEventListener('focusin', (e) => { if (all().includes(e.target)) sync(e.target); });
|
|
318
|
+
sync();
|
|
319
|
+
return { sync };
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
/**
|
|
323
|
+
* The card's own keys, wherever focus is inside it: 1-9 pick an option of `options()` (the group in play),
|
|
324
|
+
* Esc goes back to the composer, Enter is `onEnter(event)`'s (true: handled). A key a group or a field
|
|
325
|
+
* already handled (defaultPrevented) is left alone.
|
|
326
|
+
*/
|
|
327
|
+
export function wireCardKeys(card, { options = () => [], onPick = null, onComposer = null, onEnter = null } = {}) {
|
|
328
|
+
card.addEventListener('keydown', (e) => {
|
|
329
|
+
if (e.defaultPrevented) return;
|
|
330
|
+
const list = ((typeof options === 'function' ? options() : []) || []).filter(usable);
|
|
331
|
+
const typing = isTextField(e.target);
|
|
332
|
+
const intent = optionKeyIntent({ key: e.key, count: list.length, typing, alt: e.altKey, ctrl: e.ctrlKey, meta: e.metaKey });
|
|
333
|
+
if (intent?.composer) {
|
|
334
|
+
e.preventDefault();
|
|
335
|
+
e.stopPropagation();
|
|
336
|
+
onComposer?.();
|
|
337
|
+
return;
|
|
338
|
+
}
|
|
339
|
+
if (intent?.pick != null) {
|
|
340
|
+
e.preventDefault();
|
|
341
|
+
e.stopPropagation();
|
|
342
|
+
onPick?.(intent.pick, list[intent.pick]);
|
|
343
|
+
return;
|
|
344
|
+
}
|
|
345
|
+
if (e.key === 'Enter' && typeof onEnter === 'function' && onEnter(e) === true) {
|
|
346
|
+
e.preventDefault();
|
|
347
|
+
e.stopPropagation();
|
|
348
|
+
}
|
|
349
|
+
});
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
/** The presenter looks at the option pointed at or focused inside `root`, and at what it offers first when none is. */
|
|
353
|
+
export function wireGaze(root, presenter, { selector = '[data-ask-option]' } = {}) {
|
|
354
|
+
const optionOf = (node) => {
|
|
355
|
+
const hit = node?.closest?.(selector);
|
|
356
|
+
return hit && root.contains?.(hit) ? hit : null;
|
|
357
|
+
};
|
|
358
|
+
const focusedOption = () => {
|
|
359
|
+
try { return optionOf(document.activeElement); } catch { return null; }
|
|
360
|
+
};
|
|
361
|
+
root.addEventListener('pointerover', (e) => { const hit = optionOf(e.target); if (hit) presenter.lookAt(hit); });
|
|
362
|
+
root.addEventListener('pointerleave', () => presenter.lookAt(focusedOption()));
|
|
363
|
+
root.addEventListener('focusin', (e) => presenter.lookAt(optionOf(e.target)));
|
|
364
|
+
root.addEventListener('focusout', (e) => { if (!root.contains?.(e.relatedTarget)) presenter.lookAt(null); });
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
/** Where a text field's caret is, roughly (client px): its left edge plus the typed width, mid-height. */
|
|
368
|
+
function caretOf(input) {
|
|
369
|
+
try {
|
|
370
|
+
const r = input.getBoundingClientRect();
|
|
371
|
+
const typed = String(input.value || '').slice(0, input.selectionStart ?? undefined).length;
|
|
372
|
+
return { x: r.left + Math.min(r.width - 12, 12 + typed * 7), y: r.top + r.height / 2 };
|
|
373
|
+
} catch { return null; }
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
/** The presenter watches `input` while it is typed in (its eyes on the caret), and goes back to its pose 1.5 s after. */
|
|
377
|
+
export function wireWatch(input, presenter) {
|
|
378
|
+
let timer = 0;
|
|
379
|
+
const stop = () => { if (timer) clearTimeout(timer); timer = 0; presenter.unwatch(); };
|
|
380
|
+
input.addEventListener('input', () => {
|
|
381
|
+
presenter.watch(caretOf(input));
|
|
382
|
+
if (timer) clearTimeout(timer);
|
|
383
|
+
timer = setTimeout(stop, WATCH_IDLE_MS);
|
|
384
|
+
});
|
|
385
|
+
input.addEventListener('blur', stop);
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
/** "1–4" for four options, "1" for one: the keys a hint names. */
|
|
389
|
+
export function keyRange(count) {
|
|
390
|
+
const n = Math.max(0, Math.min(9, Number(count) || 0));
|
|
391
|
+
return n > 1 ? `1–${n}` : n === 1 ? '1' : '';
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
/** The key that sends a multi-select from anywhere in its card, as the platform names it. */
|
|
395
|
+
export function sendModifier() {
|
|
396
|
+
try { return /Mac|iPhone|iPad/.test(globalThis.navigator?.platform || globalThis.navigator?.userAgent || '') ? '⌘' : 'Ctrl'; } catch { return 'Ctrl'; }
|
|
397
|
+
}
|