synabun 2.0.10 → 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 +86 -0
- package/README.md +25 -2
- package/THIRD-PARTY-LICENSES.md +22 -0
- 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/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 +37 -4
- 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 +3463 -200
- package/neural-interface/i18n/pt-BR.json +3411 -151
- package/neural-interface/lib/assistant-api.js +127 -28
- 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-catalog.js +6 -7
- package/neural-interface/lib/assistant-dispatch.js +871 -156
- package/neural-interface/lib/assistant-envelope.js +3 -1
- 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 +56 -18
- 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-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-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 +68 -68
- package/neural-interface/lib/exec-loop-command.js +4 -8
- 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 +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 +547 -47
- package/neural-interface/lib/native-loop-runtime.js +268 -41
- 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 +44 -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/peer-identity.js +763 -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 +17 -30
- 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 +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/tool-lists.js +124 -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 +18 -12
- package/neural-interface/package.json +2 -2
- package/neural-interface/public/index.html +3 -1
- 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 +35 -19
- 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 +33 -2
- 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 +49 -35
- 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-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 +7 -6
- package/neural-interface/public/shared/assistant/asst-panel.js +106 -11
- package/neural-interface/public/shared/assistant/asst-rack-styles.js +66 -49
- package/neural-interface/public/shared/assistant/asst-render.js +401 -77
- package/neural-interface/public/shared/assistant/asst-route.js +83 -12
- package/neural-interface/public/shared/assistant/asst-scenes.js +2 -0
- 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 +11 -15
- package/neural-interface/public/shared/assistant/asst-stop.js +127 -0
- package/neural-interface/public/shared/assistant/asst-styles.js +59 -21
- package/neural-interface/public/shared/assistant/asst-tool-kinds.js +156 -16
- 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 +8 -7
- package/neural-interface/public/shared/cdx/cdx-panel.js +45 -149
- package/neural-interface/public/shared/cdx/cdx-protocol.js +28 -1
- package/neural-interface/public/shared/cdx/cdx-render.js +47 -29
- 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 +11 -3
- package/neural-interface/public/shared/cdx/cdx-tabs.js +52 -39
- 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/crash-notice-view.js +168 -0
- package/neural-interface/public/shared/desktop-update-view.js +20 -0
- package/neural-interface/public/shared/editor/editor-panel-logic.js +2 -2
- package/neural-interface/public/shared/html-shell.js +9 -11
- 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 +47 -252
- 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-render.js +6 -21
- 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 +0 -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 +14 -47
- 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 +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-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/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 +1525 -1779
- 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-automation-studio.js +91 -128
- package/neural-interface/public/shared/ui-claude-panel.js +77 -5
- 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-image-gallery.js +50 -47
- package/neural-interface/public/shared/ui-keybinds.js +26 -1
- package/neural-interface/public/shared/ui-menubar.js +8 -18
- package/neural-interface/public/shared/ui-native-loop-router-state.js +5 -0
- package/neural-interface/public/shared/ui-native-loop-router.js +7 -1
- package/neural-interface/public/shared/ui-navbar.js +4 -24
- package/neural-interface/public/shared/ui-panels.js +96 -65
- package/neural-interface/public/shared/ui-schedules-studio.js +507 -423
- package/neural-interface/public/shared/ui-sessions.js +5 -3
- package/neural-interface/public/shared/ui-settings.js +594 -462
- package/neural-interface/public/shared/ui-sidepanel-sessions.js +1 -1
- package/neural-interface/public/shared/ui-sidepanel-state.js +23 -12
- package/neural-interface/public/shared/ui-sidepanel-windows.js +16 -11
- 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 +124 -180
- 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 +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 +311 -58
- 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 +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 +3934 -2454
- 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 +4 -4
- 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
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+
// ═══════════════════════════════════════════
|
|
2
|
+
// SynaBun Assistant — waiting for a usage limit to reset
|
|
3
|
+
// ═══════════════════════════════════════════
|
|
4
|
+
// Where a provider's usage limit stopped work (the Assistant's own turn: its
|
|
5
|
+
// error row; a worker: its run card), this card waits with the user: the
|
|
6
|
+
// mascot dozes inside a gold ring that fills toward the reset, a live
|
|
7
|
+
// hh:mm:ss countdown and the local reset time beside it. At zero it wakes
|
|
8
|
+
// ("Usage limit restored") and offers Retry now. With no reset time known it
|
|
9
|
+
// waits with "Reset time unknown" and offers the retry at once. The clock is
|
|
10
|
+
// the shared ticker at 1 fps, only while it counts and only while the card is
|
|
11
|
+
// on the page; under prefers-reduced-motion the mascot is a still and nothing
|
|
12
|
+
// eases. Its sheet is its own (injectLimitWaitStyles), like the rack's.
|
|
13
|
+
|
|
14
|
+
import * as mascotLib from '../synabun-mascot.js';
|
|
15
|
+
import { isReducedMotion, subscribe } from '../synabun-ticker.js';
|
|
16
|
+
import { limitCountdown, resetMs } from './asst-limit.js';
|
|
17
|
+
|
|
18
|
+
const RING_R = 24;
|
|
19
|
+
const DAY_MS = 86_400_000;
|
|
20
|
+
|
|
21
|
+
function el(tag, className, text) {
|
|
22
|
+
const node = document.createElement(tag);
|
|
23
|
+
if (className) node.className = className;
|
|
24
|
+
if (text != null) node.textContent = text;
|
|
25
|
+
return node;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function reducedMotion() {
|
|
29
|
+
try { return !!isReducedMotion(); } catch { return !!globalThis.matchMedia?.('(prefers-reduced-motion: reduce)').matches; }
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/** The reset as the user's clock reads it: "06:01", or "Fri 06:01" when it is not within the next day. */
|
|
33
|
+
function localTime(ms, now) {
|
|
34
|
+
const opts = { hour: '2-digit', minute: '2-digit' };
|
|
35
|
+
if (Math.abs(ms - now) >= DAY_MS || new Date(ms).toDateString() !== new Date(now).toDateString()) opts.weekday = 'short';
|
|
36
|
+
try { return new Date(ms).toLocaleString([], opts); } catch { return new Date(ms).toISOString().slice(11, 16); }
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* createLimitWait({ t, resetsAt, since, provider, onRetry, retryLabel, announce(text) })
|
|
41
|
+
* → { el, get state(), update({ resetsAt, since }), retried(), stale(), destroy() }
|
|
42
|
+
* `resetsAt`: epoch ms / s or ISO, or null (unknown). `since`: when the wait began (the ring's start).
|
|
43
|
+
* `provider`: the name in the meta line ("Codex"). `onRetry()`: Retry now; null: no button.
|
|
44
|
+
*/
|
|
45
|
+
export function createLimitWait(opts = {}) {
|
|
46
|
+
injectLimitWaitStyles();
|
|
47
|
+
const t = (key, fallback, params) => {
|
|
48
|
+
const v = typeof opts.t === 'function' ? opts.t(key, fallback, params) : undefined;
|
|
49
|
+
if (v && v !== key && typeof v === 'string') return v;
|
|
50
|
+
return params ? String(fallback).replace(/\{(\w+)\}/g, (_, k) => (params[k] != null ? params[k] : `{${k}}`)) : fallback;
|
|
51
|
+
};
|
|
52
|
+
let resetsAt = resetMs(opts.resetsAt);
|
|
53
|
+
let since = Number.isFinite(opts.since) ? opts.since : Date.now();
|
|
54
|
+
let state = '';
|
|
55
|
+
let unsub = null;
|
|
56
|
+
let rig = null;
|
|
57
|
+
let pose = '';
|
|
58
|
+
let seenConnected = false;
|
|
59
|
+
let destroyed = false;
|
|
60
|
+
|
|
61
|
+
const card = el('div', 'asst-limit-wait');
|
|
62
|
+
card.setAttribute('role', 'group');
|
|
63
|
+
card.innerHTML = `
|
|
64
|
+
<div class="asst-limit-wait-stage" aria-hidden="true">
|
|
65
|
+
<svg class="asst-limit-ring" viewBox="0 0 56 56" width="56" height="56" focusable="false">
|
|
66
|
+
<circle class="asst-limit-ring-track" cx="28" cy="28" r="${RING_R}"></circle>
|
|
67
|
+
<circle class="asst-limit-ring-fill" cx="28" cy="28" r="${RING_R}" pathLength="100" stroke-dasharray="100" stroke-dashoffset="100"></circle>
|
|
68
|
+
</svg>
|
|
69
|
+
<span class="asst-limit-wait-face"></span>
|
|
70
|
+
</div>
|
|
71
|
+
<div class="asst-limit-wait-main">
|
|
72
|
+
<div class="asst-limit-wait-title"></div>
|
|
73
|
+
<div class="asst-limit-wait-clock" role="timer" aria-live="off"></div>
|
|
74
|
+
<div class="asst-limit-wait-meta"></div>
|
|
75
|
+
<div class="asst-limit-wait-actions"></div>
|
|
76
|
+
</div>
|
|
77
|
+
<span class="asst-limit-wait-live" role="status" aria-live="polite"></span>`;
|
|
78
|
+
const $ = (sel) => card.querySelector(sel);
|
|
79
|
+
const fill = $('.asst-limit-ring-fill');
|
|
80
|
+
const face = $('.asst-limit-wait-face');
|
|
81
|
+
const title = $('.asst-limit-wait-title');
|
|
82
|
+
const clock = $('.asst-limit-wait-clock');
|
|
83
|
+
const meta = $('.asst-limit-wait-meta');
|
|
84
|
+
const actions = $('.asst-limit-wait-actions');
|
|
85
|
+
const live = $('.asst-limit-wait-live');
|
|
86
|
+
|
|
87
|
+
let retryBtn = null;
|
|
88
|
+
if (typeof opts.onRetry === 'function') {
|
|
89
|
+
retryBtn = el('button', 'asst-btn asst-btn-primary asst-limit-wait-retry');
|
|
90
|
+
retryBtn.type = 'button';
|
|
91
|
+
retryBtn.addEventListener('click', () => {
|
|
92
|
+
if (state !== 'restored' && state !== 'unknown') return;
|
|
93
|
+
retried();
|
|
94
|
+
try { opts.onRetry(); } catch { /* the host reports its own failure */ }
|
|
95
|
+
});
|
|
96
|
+
actions.appendChild(retryBtn);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function setPose(next) {
|
|
100
|
+
if (pose === next || destroyed) return;
|
|
101
|
+
pose = next;
|
|
102
|
+
if (reducedMotion()) {
|
|
103
|
+
try { rig?.destroy(); } catch { /* gone */ }
|
|
104
|
+
rig = null;
|
|
105
|
+
face.innerHTML = mascotLib.mascotSvg({ width: 34, height: 17, className: 'synabun-mascot asst-still' });
|
|
106
|
+
try { mascotLib.paintMascot(face.firstElementChild, mascotLib.stillFrame(next)); } catch { /* the plain face */ }
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
if (!rig) {
|
|
110
|
+
face.textContent = '';
|
|
111
|
+
try { rig = mascotLib.createMascot(face, { width: 34, height: 17, pose: next }); return; } catch { rig = null; }
|
|
112
|
+
}
|
|
113
|
+
try { rig?.setPose(next, next === 'success' ? { then: 'idle' } : undefined); } catch { /* keeps its pose */ }
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function setRing(progress, indeterminate = false) {
|
|
117
|
+
fill.setAttribute('stroke-dashoffset', String(Math.round((1 - Math.max(0, Math.min(1, progress))) * 1000) / 10));
|
|
118
|
+
card.classList.toggle('is-indeterminate', indeterminate);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function paintMeta(now) {
|
|
122
|
+
const bits = [];
|
|
123
|
+
if (opts.provider) bits.push(t('assistant.limitWait.provider', '{provider} usage limit', { provider: opts.provider }));
|
|
124
|
+
if (resetsAt !== null) bits.push((state === 'restored' || state === 'retried' || (state === 'stale' && resetsAt <= now)) ? t('assistant.limitWait.resetAt', 'reset at {time}', { time: localTime(resetsAt, now) }) : t('assistant.limitWait.resetsAt', 'resets at {time}', { time: localTime(resetsAt, now) }));
|
|
125
|
+
meta.textContent = bits.join(' · ');
|
|
126
|
+
meta.hidden = !bits.length;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function setState(next, now = Date.now()) {
|
|
130
|
+
const was = state;
|
|
131
|
+
state = next;
|
|
132
|
+
card.dataset.state = next;
|
|
133
|
+
card.dataset.known = resetsAt === null ? '0' : '1';
|
|
134
|
+
card.setAttribute('aria-label', next === 'restored' || next === 'retried' ? t('assistant.limitWait.restored', 'Usage limit restored') : t('assistant.limitWait.waiting', 'Waiting for the usage limit to reset'));
|
|
135
|
+
title.textContent = next === 'restored' || next === 'retried'
|
|
136
|
+
? t('assistant.limitWait.restored', 'Usage limit restored')
|
|
137
|
+
: next === 'stale' && resetsAt !== null && resetsAt <= now ? t('assistant.limitWait.restored', 'Usage limit restored')
|
|
138
|
+
: t('assistant.limitWait.waiting', 'Waiting for the usage limit to reset');
|
|
139
|
+
if (next === 'unknown') { clock.textContent = t('assistant.limitWait.unknown', 'Reset time unknown'); clock.classList.add('is-text'); clock.removeAttribute('aria-label'); }
|
|
140
|
+
if (next === 'restored' || next === 'retried') { clock.textContent = '00:00:00'; clock.classList.remove('is-text'); clock.removeAttribute('aria-label'); }
|
|
141
|
+
if (retryBtn) {
|
|
142
|
+
retryBtn.hidden = !(next === 'restored' || next === 'unknown' || next === 'retried');
|
|
143
|
+
retryBtn.disabled = next === 'retried' || next === 'stale';
|
|
144
|
+
const label = next === 'retried' ? t('assistant.limitWait.retried', 'Retried')
|
|
145
|
+
: next === 'unknown' ? (opts.retryLabel || t('assistant.limitWait.tryAgain', 'Try again'))
|
|
146
|
+
: (opts.retryLabel || t('assistant.limitWait.retry', 'Retry now'));
|
|
147
|
+
retryBtn.textContent = label;
|
|
148
|
+
}
|
|
149
|
+
if (next === 'waiting') { setPose('sleep'); setRing(limitCountdown({ resetsAt, since }, now).progress || 0); }
|
|
150
|
+
else if (next === 'unknown') { setPose('wait'); setRing(0.18, true); }
|
|
151
|
+
else if (next === 'restored') { setPose(was === 'waiting' ? 'success' : 'idle'); setRing(1); }
|
|
152
|
+
else { setPose('idle'); setRing(resetsAt !== null && resetsAt <= now ? 1 : 0); }
|
|
153
|
+
if (next === 'restored' && was === 'waiting') live.textContent = t('assistant.limitWait.announceRestored', 'The usage limit has reset.');
|
|
154
|
+
paintMeta(now);
|
|
155
|
+
syncTicker();
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
function tick(now = Date.now()) {
|
|
159
|
+
if (destroyed) return;
|
|
160
|
+
if (card.isConnected) seenConnected = true;
|
|
161
|
+
else if (seenConnected) { destroy(); return; }
|
|
162
|
+
if (state !== 'waiting') { syncTicker(); return; }
|
|
163
|
+
const view = limitCountdown({ resetsAt, since }, now);
|
|
164
|
+
if (view.done) { setState('restored', now); return; }
|
|
165
|
+
if (clock.textContent !== view.clock) {
|
|
166
|
+
clock.textContent = view.clock;
|
|
167
|
+
clock.classList.remove('is-text');
|
|
168
|
+
clock.setAttribute('aria-label', t('assistant.limitWait.left', '{clock} left', { clock: view.clock }));
|
|
169
|
+
}
|
|
170
|
+
setRing(view.progress);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
function syncTicker() {
|
|
174
|
+
const want = state === 'waiting' && !destroyed;
|
|
175
|
+
if (want && !unsub) { try { unsub = subscribe((now) => tick(typeof now === 'number' && now > 1e12 ? now : Date.now()), { fps: 1 }); } catch { unsub = null; } }
|
|
176
|
+
else if (!want && unsub) { try { unsub(); } catch { /* gone */ } unsub = null; }
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
function start(now = Date.now()) {
|
|
180
|
+
if (resetsAt === null) setState('unknown', now);
|
|
181
|
+
else if (resetsAt <= now) setState('restored', now);
|
|
182
|
+
else { setState('waiting', now); tick(now); }
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
function retried() {
|
|
186
|
+
if (state === 'retried' || state === 'stale') return;
|
|
187
|
+
setState('retried');
|
|
188
|
+
}
|
|
189
|
+
function stale() {
|
|
190
|
+
if (state === 'retried' || state === 'stale') return;
|
|
191
|
+
setState('stale');
|
|
192
|
+
if (state === 'stale') {
|
|
193
|
+
clock.classList.add('is-text');
|
|
194
|
+
clock.removeAttribute('aria-label');
|
|
195
|
+
clock.textContent = resetsAt !== null ? localTime(resetsAt, Date.now()) : t('assistant.limitWait.unknown', 'Reset time unknown');
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
function destroy() {
|
|
199
|
+
if (destroyed) return;
|
|
200
|
+
destroyed = true;
|
|
201
|
+
if (unsub) { try { unsub(); } catch { /* gone */ } unsub = null; }
|
|
202
|
+
try { rig?.destroy(); } catch { /* gone */ }
|
|
203
|
+
rig = null;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
start();
|
|
207
|
+
return {
|
|
208
|
+
el: card,
|
|
209
|
+
get state() { return state; },
|
|
210
|
+
get resetsAt() { return resetsAt; },
|
|
211
|
+
/** A reset time learned later (a structured report): the card counts to it. Ignored once it is settled. */
|
|
212
|
+
update({ resetsAt: next, since: from } = {}) {
|
|
213
|
+
if (destroyed || state === 'retried' || state === 'stale') return;
|
|
214
|
+
const ms = resetMs(next);
|
|
215
|
+
if (Number.isFinite(from)) since = from;
|
|
216
|
+
if (ms === null || ms === resetsAt) return;
|
|
217
|
+
resetsAt = ms;
|
|
218
|
+
start();
|
|
219
|
+
},
|
|
220
|
+
retried,
|
|
221
|
+
stale,
|
|
222
|
+
destroy,
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
const CSS = `
|
|
227
|
+
.asst-limit-wait {
|
|
228
|
+
--lw-tone: var(--asst-brand);
|
|
229
|
+
display: grid;
|
|
230
|
+
grid-template-columns: 56px minmax(0, 1fr);
|
|
231
|
+
align-items: center;
|
|
232
|
+
gap: 12px;
|
|
233
|
+
min-width: 0;
|
|
234
|
+
padding: 12px 14px;
|
|
235
|
+
border-radius: var(--asst-r-card);
|
|
236
|
+
background: linear-gradient(135deg, color-mix(in srgb, var(--asst-brand) 8%, transparent), var(--asst-fill-1) 62%);
|
|
237
|
+
box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--asst-brand) 26%, transparent), var(--asst-card-hi);
|
|
238
|
+
color: var(--asst-text);
|
|
239
|
+
font-size: 13px;
|
|
240
|
+
transition: box-shadow var(--asst-dur-base) var(--asst-ease-standard), background-color var(--asst-dur-base) var(--asst-ease-standard);
|
|
241
|
+
}
|
|
242
|
+
.asst-limit-wait:is([data-state="stale"], [data-state="retried"]) {
|
|
243
|
+
--lw-tone: var(--asst-text-3);
|
|
244
|
+
background: transparent;
|
|
245
|
+
box-shadow: inset 0 0 0 1px var(--asst-line);
|
|
246
|
+
}
|
|
247
|
+
.asst-limit-wait-stage { position: relative; width: 56px; height: 56px; }
|
|
248
|
+
.asst-limit-ring { display: block; width: 56px; height: 56px; transform: rotate(-90deg); overflow: visible; }
|
|
249
|
+
.asst-limit-ring-track { fill: none; stroke: var(--asst-line-2); stroke-width: 3; }
|
|
250
|
+
.asst-limit-ring-fill {
|
|
251
|
+
fill: none;
|
|
252
|
+
stroke: var(--lw-tone);
|
|
253
|
+
stroke-width: 3;
|
|
254
|
+
stroke-linecap: round;
|
|
255
|
+
transform-box: fill-box;
|
|
256
|
+
transform-origin: center;
|
|
257
|
+
transition: stroke-dashoffset 1s linear, stroke var(--asst-dur-base) var(--asst-ease-standard);
|
|
258
|
+
}
|
|
259
|
+
.asst-limit-wait[data-state="restored"] .asst-limit-ring-fill { filter: drop-shadow(0 0 3px color-mix(in srgb, var(--asst-brand) 55%, transparent)); }
|
|
260
|
+
.asst-limit-wait.is-indeterminate .asst-limit-ring-fill { stroke-dasharray: 18 82; animation: asst-limit-orbit 2.6s linear infinite; }
|
|
261
|
+
@keyframes asst-limit-orbit { to { transform: rotate(360deg); } }
|
|
262
|
+
.asst-limit-wait-face { position: absolute; inset: 0; display: grid; place-items: center; color: var(--lw-tone); line-height: 0; }
|
|
263
|
+
.asst-limit-wait-face svg { display: block; width: 34px; height: 17px; }
|
|
264
|
+
.asst-limit-wait-main { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
|
|
265
|
+
.asst-limit-wait-title { color: var(--asst-text-2); font-size: 12px; font-weight: 500; }
|
|
266
|
+
.asst-limit-wait[data-state="restored"] .asst-limit-wait-title { color: var(--asst-brand); }
|
|
267
|
+
.asst-limit-wait-clock { color: var(--asst-text); font-family: var(--asst-mono); font-size: 20px; font-weight: 500; line-height: 1.2; font-variant-numeric: tabular-nums; letter-spacing: .02em; }
|
|
268
|
+
.asst-limit-wait-clock.is-text { font-family: inherit; font-size: 13px; color: var(--asst-text-2); letter-spacing: 0; }
|
|
269
|
+
.asst-limit-wait-meta { color: var(--asst-text-3); font-size: 11.5px; overflow-wrap: anywhere; }
|
|
270
|
+
.asst-limit-wait-actions { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 6px; }
|
|
271
|
+
.asst-limit-wait-actions:not(:has(> :not([hidden]))) { display: none; }
|
|
272
|
+
.asst-limit-wait-live { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }
|
|
273
|
+
.asst-limit-row .asst-msg-content { width: 100%; }
|
|
274
|
+
.asst-run .asst-limit-wait { grid-column: 2 / span 2; margin-top: 8px; padding: 10px 12px; }
|
|
275
|
+
@container asst (max-width: 320px) {
|
|
276
|
+
.asst-limit-wait { grid-template-columns: 44px minmax(0, 1fr); gap: 10px; padding: 10px; }
|
|
277
|
+
.asst-limit-wait-stage, .asst-limit-ring { width: 44px; height: 44px; }
|
|
278
|
+
.asst-limit-wait-face svg { width: 28px; height: 14px; }
|
|
279
|
+
.asst-limit-wait-clock { font-size: 17px; }
|
|
280
|
+
}
|
|
281
|
+
body.ui-interacting .asst-limit-ring-fill { animation-play-state: paused !important; }
|
|
282
|
+
@media (prefers-reduced-motion: reduce) {
|
|
283
|
+
.asst-limit-wait, .asst-limit-wait * { animation: none !important; transition: none !important; }
|
|
284
|
+
}
|
|
285
|
+
`;
|
|
286
|
+
|
|
287
|
+
/** The card's sheet, once per page. */
|
|
288
|
+
export function injectLimitWaitStyles() {
|
|
289
|
+
if (typeof document === 'undefined' || document.getElementById('asst-limit-wait-styles')) return;
|
|
290
|
+
const style = document.createElement('style');
|
|
291
|
+
style.id = 'asst-limit-wait-styles';
|
|
292
|
+
style.textContent = CSS;
|
|
293
|
+
document.head.appendChild(style);
|
|
294
|
+
}
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
// ═══════════════════════════════════════════
|
|
2
|
+
// SynaBun Assistant — usage limits: is it one, and when does it reset?
|
|
3
|
+
// ═══════════════════════════════════════════
|
|
4
|
+
// A provider that stops a turn on its usage limit says so in words ("You've
|
|
5
|
+
// hit your session limit · resets 5:10am (America/Sao_Paulo)", "try again in
|
|
6
|
+
// 2 hours 3 minutes", "blocked until about 06:01 UTC"). limitFromText reads
|
|
7
|
+
// that message: whether it is a usage limit, and the reset time it names
|
|
8
|
+
// (epoch ms, or null when it names none). The time is read against the moment
|
|
9
|
+
// the message was written (`now`), so a reload draws the same countdown. A
|
|
10
|
+
// structured reset time (the Claude SDK's rate_limit_info, an account's usage
|
|
11
|
+
// window) always wins over the words; this is what is left when there is
|
|
12
|
+
// none. limitCountdown is the waiting card's clock. No imports and no DOM:
|
|
13
|
+
// the dispatcher (lib/assistant-dispatch.js) reads a worker's failure with it.
|
|
14
|
+
|
|
15
|
+
const LIMIT_RE = /\b(?:usage limit|session limit|weekly limit|daily limit|hourly limit|5-hour limit|rate[- ]limit(?:ed)?|limit reached|limit exceeded|hit (?:your|the) (?:\w+ ){0,2}limit|out of (?:usage|credits|quota)|quota (?:exceeded|reached)|exceeded (?:your |the )?(?:current )?quota|too many requests)\b/i;
|
|
16
|
+
const DAY_MS = 86_400_000;
|
|
17
|
+
const MONTHS = ['jan', 'feb', 'mar', 'apr', 'may', 'jun', 'jul', 'aug', 'sep', 'oct', 'nov', 'dec'];
|
|
18
|
+
const UNIT_MS = { d: DAY_MS, h: 3_600_000, m: 60_000, s: 1000 };
|
|
19
|
+
|
|
20
|
+
/** The offset of `timeZone` from UTC at the instant `ms` (wall time − UTC), in ms; null for a zone this runtime does not know. */
|
|
21
|
+
function zoneOffset(ms, timeZone) {
|
|
22
|
+
try {
|
|
23
|
+
const parts = new Intl.DateTimeFormat('en-US', {
|
|
24
|
+
timeZone, hourCycle: 'h23', year: 'numeric', month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit', second: '2-digit',
|
|
25
|
+
}).formatToParts(new Date(ms));
|
|
26
|
+
const get = (type) => Number(parts.find((p) => p.type === type)?.value);
|
|
27
|
+
const wall = Date.UTC(get('year'), get('month') - 1, get('day'), get('hour') % 24, get('minute'), get('second'));
|
|
28
|
+
return Number.isFinite(wall) ? wall - (Math.floor(ms / 1000) * 1000) : null;
|
|
29
|
+
} catch { return null; }
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/** A wall-clock time in `zone` ('UTC', an IANA name, or null for this machine's own) → epoch ms. */
|
|
33
|
+
export function wallTimeToEpoch({ year, month, day, hour, minute = 0 }, zone = null) {
|
|
34
|
+
if (zone === 'UTC') return Date.UTC(year, month, day, hour, minute);
|
|
35
|
+
if (zone) {
|
|
36
|
+
const guess = Date.UTC(year, month, day, hour, minute);
|
|
37
|
+
const first = zoneOffset(guess, zone);
|
|
38
|
+
if (first !== null) {
|
|
39
|
+
const at = guess - first;
|
|
40
|
+
const second = zoneOffset(at, zone);
|
|
41
|
+
return second !== null && second !== first ? guess - second : at;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
return new Date(year, month, day, hour, minute).getTime();
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/** Year / month / day of the instant `ms` as `zone` sees it. */
|
|
48
|
+
function dateIn(ms, zone) {
|
|
49
|
+
if (zone === 'UTC') { const d = new Date(ms); return { year: d.getUTCFullYear(), month: d.getUTCMonth(), day: d.getUTCDate() }; }
|
|
50
|
+
if (zone) {
|
|
51
|
+
const offset = zoneOffset(ms, zone);
|
|
52
|
+
if (offset !== null) { const d = new Date(ms + offset); return { year: d.getUTCFullYear(), month: d.getUTCMonth(), day: d.getUTCDate() }; }
|
|
53
|
+
}
|
|
54
|
+
const d = new Date(ms);
|
|
55
|
+
return { year: d.getFullYear(), month: d.getMonth(), day: d.getDate() };
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function isZone(name) {
|
|
59
|
+
if (!name) return false;
|
|
60
|
+
if (/^(?:utc|gmt|z)$/i.test(name)) return true;
|
|
61
|
+
try { new Intl.DateTimeFormat('en-US', { timeZone: name }); return true; } catch { return false; }
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/** "in 2 hours 3 minutes" / "in 4h 12m" → ms, or null. */
|
|
65
|
+
function relativeMs(text) {
|
|
66
|
+
const m = /\b(?:in|after|within)\s+(?:about\s+|around\s+|~\s*)?((?:\d+(?:\.\d+)?\s*(?:days?|d|hours?|hrs?|hr|h|minutes?|mins?|min|m|seconds?|secs?|sec|s)\b[\s,]*(?:and\s+)?)+)/i.exec(text);
|
|
67
|
+
if (!m) return null;
|
|
68
|
+
let total = 0;
|
|
69
|
+
for (const part of m[1].matchAll(/(\d+(?:\.\d+)?)\s*(days?|d|hours?|hrs?|hr|h|minutes?|mins?|min|m|seconds?|secs?|sec|s)\b/gi)) {
|
|
70
|
+
const unit = part[2].toLowerCase();
|
|
71
|
+
const key = unit.startsWith('d') ? 'd' : unit.startsWith('h') ? 'h' : unit.startsWith('m') ? 'm' : 's';
|
|
72
|
+
total += Number(part[1]) * UNIT_MS[key];
|
|
73
|
+
}
|
|
74
|
+
return total > 0 ? total : null;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* A clock time the message names after "resets", "until", "try again at"…: the next such moment
|
|
79
|
+
* from `now` (today, or tomorrow when it has passed), in the zone it names ("UTC", "(America/Sao_Paulo)")
|
|
80
|
+
* or this machine's own. A month and day before it ("Oct 23, 5:52 PM") pin the date.
|
|
81
|
+
*/
|
|
82
|
+
function clockTime(text, now) {
|
|
83
|
+
const re = /\b(?:resets?|reset at|until|till|try again at|again at|available (?:again )?at|back at)\s+(?:on\s+)?(?:at\s+)?(?:about\s+|around\s+|approximately\s+|~\s*)?(?:([A-Za-z]{3,9})\.?\s+(\d{1,2})(?:st|nd|rd|th)?,?\s+(?:(\d{4}),?\s+)?(?:at\s+)?)?(\d{1,2})(?::(\d{2}))?\s*([ap]\.?m\.?)?(?:\s*(?:\(\s*)?(UTC|GMT|Z|[A-Za-z]+\/[A-Za-z_]+(?:\/[A-Za-z_]+)?)(?:\s*\))?)?/gi;
|
|
84
|
+
for (const m of text.matchAll(re)) {
|
|
85
|
+
const [, monthName, dayText, yearText, hourText, minuteText, meridiem, zoneText] = m;
|
|
86
|
+
let hour = Number(hourText);
|
|
87
|
+
const minute = minuteText ? Number(minuteText) : 0;
|
|
88
|
+
// "until 3 runs finish" is no time: a clock needs minutes or am / pm.
|
|
89
|
+
if (!minuteText && !meridiem) continue;
|
|
90
|
+
if (minute > 59 || hour > 23 || (meridiem && (hour < 1 || hour > 12))) continue;
|
|
91
|
+
if (meridiem) { const pm = /^p/i.test(meridiem); hour = (hour % 12) + (pm ? 12 : 0); }
|
|
92
|
+
const zone = zoneText && isZone(zoneText) ? (/^(?:utc|gmt|z)$/i.test(zoneText) ? 'UTC' : zoneText) : null;
|
|
93
|
+
const monthIndex = monthName ? MONTHS.indexOf(monthName.slice(0, 3).toLowerCase()) : -1;
|
|
94
|
+
if (monthName && monthIndex >= 0) {
|
|
95
|
+
const today = dateIn(now, zone);
|
|
96
|
+
let year = yearText ? Number(yearText) : today.year;
|
|
97
|
+
let at = wallTimeToEpoch({ year, month: monthIndex, day: Number(dayText), hour, minute }, zone);
|
|
98
|
+
if (!yearText && at < now - DAY_MS) { year += 1; at = wallTimeToEpoch({ year, month: monthIndex, day: Number(dayText), hour, minute }, zone); }
|
|
99
|
+
return at;
|
|
100
|
+
}
|
|
101
|
+
if (monthName) continue; // a word that is no month ("resets daily 5pm"): try the next match
|
|
102
|
+
const today = dateIn(now, zone);
|
|
103
|
+
let at = wallTimeToEpoch({ ...today, hour, minute }, zone);
|
|
104
|
+
if (at < now - 60_000) {
|
|
105
|
+
const next = new Date(Date.UTC(today.year, today.month, today.day + 1));
|
|
106
|
+
at = wallTimeToEpoch({ year: next.getUTCFullYear(), month: next.getUTCMonth(), day: next.getUTCDate(), hour, minute }, zone);
|
|
107
|
+
}
|
|
108
|
+
return at;
|
|
109
|
+
}
|
|
110
|
+
return null;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/** An instant written out: epoch seconds or ms after "limit reached|" (the Claude CLI's old form), or an ISO timestamp. */
|
|
114
|
+
function absoluteTime(text) {
|
|
115
|
+
const epoch = /limit reached\|(\d{10,13})\b/i.exec(text);
|
|
116
|
+
if (epoch) { const n = Number(epoch[1]); return n < 1e12 ? n * 1000 : n; }
|
|
117
|
+
const isoMatch = /\b(\d{4}-\d{2}-\d{2}[T ]\d{2}:\d{2}(?::\d{2}(?:\.\d+)?)?(?:Z|[+-]\d{2}:?\d{2})?)\b/.exec(text);
|
|
118
|
+
if (isoMatch) { const ms = Date.parse(isoMatch[1].replace(' ', 'T')); if (Number.isFinite(ms)) return ms; }
|
|
119
|
+
return null;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Is `text` a provider's usage-limit message? → { resetsAt: epoch ms | null } or null.
|
|
124
|
+
* `now`: when the message was written (its reset time is the next one from then).
|
|
125
|
+
* A reset time more than a day before `now` or 31 days after it is none.
|
|
126
|
+
*/
|
|
127
|
+
export function limitFromText(text, { now = Date.now() } = {}) {
|
|
128
|
+
const src = String(text ?? '').replace(/\s+/g, ' ').trim();
|
|
129
|
+
if (!src || src.length > 4000 || !LIMIT_RE.test(src)) return null;
|
|
130
|
+
let resetsAt = absoluteTime(src);
|
|
131
|
+
if (resetsAt === null) { const rel = relativeMs(src); if (rel !== null) resetsAt = now + rel; }
|
|
132
|
+
if (resetsAt === null) resetsAt = clockTime(src, now);
|
|
133
|
+
if (resetsAt !== null && (!Number.isFinite(resetsAt) || resetsAt < now - DAY_MS || resetsAt > now + 31 * DAY_MS)) resetsAt = null;
|
|
134
|
+
return { resetsAt };
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/** A reset time in any form the panel meets (epoch s or ms, ISO text) → epoch ms, or null. Seconds below 1e11 (until the year 5138). */
|
|
138
|
+
export function resetMs(value) {
|
|
139
|
+
if (value === null || value === undefined || value === '') return null;
|
|
140
|
+
if (typeof value === 'number') return Number.isFinite(value) && value > 0 ? (value < 1e11 ? value * 1000 : value) : null;
|
|
141
|
+
const n = Number(value);
|
|
142
|
+
if (Number.isFinite(n) && String(value).trim() !== '') return resetMs(n);
|
|
143
|
+
const ms = Date.parse(String(value));
|
|
144
|
+
return Number.isFinite(ms) ? ms : null;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/** ms → "hh:mm:ss" (hours past 99 keep growing), counted up so the last second shows 00:00:01. */
|
|
148
|
+
export function fmtCountdown(ms) {
|
|
149
|
+
const total = Math.max(0, Math.ceil((Number(ms) || 0) / 1000));
|
|
150
|
+
const h = Math.floor(total / 3600);
|
|
151
|
+
const m = Math.floor((total % 3600) / 60);
|
|
152
|
+
const s = total % 60;
|
|
153
|
+
return `${String(h).padStart(2, '0')}:${String(m).padStart(2, '0')}:${String(s).padStart(2, '0')}`;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* The waiting card's clock at `now`: { known, done, remainingMs, clock, progress }.
|
|
158
|
+
* `since`: when the wait began (the ring fills from there to `resetsAt`). No reset time: known false.
|
|
159
|
+
*/
|
|
160
|
+
export function limitCountdown({ resetsAt = null, since = null } = {}, now = Date.now()) {
|
|
161
|
+
const at = resetMs(resetsAt);
|
|
162
|
+
if (at === null) return { known: false, done: false, remainingMs: null, clock: '', progress: null };
|
|
163
|
+
const remainingMs = Math.max(0, at - now);
|
|
164
|
+
const start = Number.isFinite(since) && since < at ? since : Math.min(now, at);
|
|
165
|
+
const span = Math.max(1, at - start);
|
|
166
|
+
const progress = remainingMs <= 0 ? 1 : Math.min(1, Math.max(0, 1 - remainingMs / span));
|
|
167
|
+
return { known: true, done: remainingMs <= 0, remainingMs, clock: fmtCountdown(remainingMs), progress };
|
|
168
|
+
}
|
|
@@ -3,12 +3,15 @@
|
|
|
3
3
|
// ═══════════════════════════════════════════
|
|
4
4
|
// The renderer (asst-render.js) owns the rigs and says which one is on screen:
|
|
5
5
|
// the hero (the empty state's character; a send hands it over to the
|
|
6
|
-
// "Thinking" row's at once), the
|
|
6
|
+
// "Thinking" row's at once), the face of the newest open card the mascot
|
|
7
|
+
// presents (a follow-ups or question card: asst-ask.js, kind 'card'), the
|
|
8
|
+
// live rack's stage or the "Thinking" row's.
|
|
7
9
|
// This module dresses that one in the panel's own state, above the pose the
|
|
8
10
|
// renderer gives it:
|
|
9
11
|
//
|
|
10
12
|
// offline the socket dropped (the Reconnecting banner); one startle on reconnect
|
|
11
|
-
// blocked a card waits on you: eyes on it, two pulses, then still
|
|
13
|
+
// blocked a card waits on you: eyes on it, two pulses, then still (never on a
|
|
14
|
+
// question card's own face, `waits`: that card asks in its own pose)
|
|
12
15
|
// success, error once, when a turn that did real work ends (then idle)
|
|
13
16
|
// watch while you type: eyes on the caret column, look() at most
|
|
14
17
|
// every 120 ms; back 1.5 s after the last key, at once on send
|
|
@@ -17,6 +20,12 @@
|
|
|
17
20
|
// sleep the hero only, after 90 s with no input while nothing runs;
|
|
18
21
|
// the rig wakes itself on a pointer move or a key
|
|
19
22
|
//
|
|
23
|
+
// present(true) hands the character to a presenter (the Workspace tour,
|
|
24
|
+
// public/shared/tour/): while it lasts the director dresses nothing, never
|
|
25
|
+
// puts the hero to sleep, does not watch the caret and ignores chip
|
|
26
|
+
// attention; present(false) gives it back and the director dresses it again
|
|
27
|
+
// for whatever is true by then.
|
|
28
|
+
//
|
|
20
29
|
// Anything else is the renderer's pose: the hero idles, the "Thinking" row's
|
|
21
30
|
// character thinks, the stage acts out its call. A pose the director sets is
|
|
22
31
|
// held: the renderer leaves it alone until the director lets go and asks it
|
|
@@ -67,8 +76,8 @@ export function lookToward(box, point, { horizontal = false } = {}) {
|
|
|
67
76
|
|
|
68
77
|
/**
|
|
69
78
|
* createMascotDirector({ getTarget, caret, env })
|
|
70
|
-
* getTarget() → the renderer's visible mascot { kind: 'hero'|'stage'|'working',
|
|
71
|
-
* rig, el, parked?(), hold(on), restore() } or null; caret() → the caret's
|
|
79
|
+
* getTarget() → the renderer's visible mascot { kind: 'hero'|'card'|'stage'|'working',
|
|
80
|
+
* rig, el, parked?(), waits?, hold(on), restore() } or null; caret() → the caret's
|
|
72
81
|
* client point or null.
|
|
73
82
|
* Control API: attend(owner, { pose, rectOf, options, open }), leave(owner),
|
|
74
83
|
* controlCommitted({ pose, options, reaction }); type(present, pointOf?) also
|
|
@@ -103,13 +112,15 @@ export function createMascotDirector({ getTarget = () => null, caret = () => nul
|
|
|
103
112
|
let sleepTimer = 0;
|
|
104
113
|
let lastInput = now();
|
|
105
114
|
let stopInput = null;
|
|
115
|
+
let presenting = false; // a presenter (the tour) owns the character's pose
|
|
106
116
|
|
|
107
117
|
const safe = (fn) => { try { return fn(); } catch { return undefined; } };
|
|
108
118
|
const boxOf = (t) => safe(() => t.el?.getBoundingClientRect?.()) || null;
|
|
109
119
|
|
|
110
120
|
function desired(t) {
|
|
111
121
|
if (offline) return { pose: 'offline' };
|
|
112
|
-
|
|
122
|
+
// A question card's own face is the card that waits: it asks in its own pose (asst-ask.js), never "blocked" at itself.
|
|
123
|
+
if (blocked && !t.waits) {
|
|
113
124
|
// The card's head (its title and command sit top left), not its centre.
|
|
114
125
|
const card = safe(blocked);
|
|
115
126
|
const point = card && card.width ? { x: card.left + Math.min(card.width * 0.3, 160), y: card.top + Math.min(card.height / 2, 28) } : null;
|
|
@@ -133,7 +144,7 @@ export function createMascotDirector({ getTarget = () => null, caret = () => nul
|
|
|
133
144
|
if (destroyed) return;
|
|
134
145
|
const t = safe(getTarget) || null;
|
|
135
146
|
if (t !== target) {
|
|
136
|
-
if (target && holding) safe(() => target.hold(false));
|
|
147
|
+
if (target && holding && !presenting) safe(() => target.hold(false));
|
|
137
148
|
target = t;
|
|
138
149
|
holding = false;
|
|
139
150
|
shown = '';
|
|
@@ -141,7 +152,7 @@ export function createMascotDirector({ getTarget = () => null, caret = () => nul
|
|
|
141
152
|
listenInput();
|
|
142
153
|
armSleep();
|
|
143
154
|
}
|
|
144
|
-
if (!t) return;
|
|
155
|
+
if (!t || presenting) return; // a presenter holds the pose: nothing is dressed until it lets go
|
|
145
156
|
const want = desired(t);
|
|
146
157
|
if (!want) {
|
|
147
158
|
if (!holding) return;
|
|
@@ -171,7 +182,7 @@ export function createMascotDirector({ getTarget = () => null, caret = () => nul
|
|
|
171
182
|
|
|
172
183
|
function canSleep() {
|
|
173
184
|
const t = target;
|
|
174
|
-
return !destroyed && !!t && t.kind === 'hero' && !safe(() => t.parked?.()) && !turnActive && !typing && !attention?.open && !blocked && !offline && !oneShot;
|
|
185
|
+
return !destroyed && !presenting && !!t && t.kind === 'hero' && !safe(() => t.parked?.()) && !turnActive && !typing && !attention?.open && !blocked && !offline && !oneShot;
|
|
175
186
|
}
|
|
176
187
|
|
|
177
188
|
function armSleep() {
|
|
@@ -202,7 +213,7 @@ export function createMascotDirector({ getTarget = () => null, caret = () => nul
|
|
|
202
213
|
function type(present = true, pointOf = null) {
|
|
203
214
|
if (destroyed) return;
|
|
204
215
|
noteInput();
|
|
205
|
-
if (!present) { stopTyping(); return; }
|
|
216
|
+
if (!present || presenting) { stopTyping(); return; }
|
|
206
217
|
if (oneShot?.control) dropOneShot(); // a new edit resumes watching after an acknowledgement
|
|
207
218
|
const first = !typing;
|
|
208
219
|
typing = true;
|
|
@@ -252,7 +263,7 @@ export function createMascotDirector({ getTarget = () => null, caret = () => nul
|
|
|
252
263
|
stopTyping,
|
|
253
264
|
/** Chip previews relax after a short dwell; only open menus hold attention and block sleep. */
|
|
254
265
|
attend(owner, spec) {
|
|
255
|
-
if (destroyed || (attention?.open && attention.owner !== owner && !spec.open)) return;
|
|
266
|
+
if (destroyed || presenting || (attention?.open && attention.owner !== owner && !spec.open)) return;
|
|
256
267
|
noteInput();
|
|
257
268
|
clearAttention();
|
|
258
269
|
attention = { owner, ...spec };
|
|
@@ -272,7 +283,7 @@ export function createMascotDirector({ getTarget = () => null, caret = () => nul
|
|
|
272
283
|
},
|
|
273
284
|
/** Only acknowledged user commits call this; loads, live updates and keystrokes do not. */
|
|
274
285
|
controlCommitted({ pose, options = {}, reaction = 'nod' }) {
|
|
275
|
-
if (destroyed) return;
|
|
286
|
+
if (destroyed || presenting) return;
|
|
276
287
|
const t = safe(getTarget);
|
|
277
288
|
if (!t || t.kind !== 'hero' || safe(() => t.parked?.()) || turnActive || offline || blocked) return;
|
|
278
289
|
stopTyping();
|
|
@@ -319,8 +330,34 @@ export function createMascotDirector({ getTarget = () => null, caret = () => nul
|
|
|
319
330
|
retarget() { apply(); },
|
|
320
331
|
/** The user is here (focus, a click): a sleeping hero wakes, the quiet starts over. */
|
|
321
332
|
touch() { if (!destroyed) noteInput(); },
|
|
333
|
+
/**
|
|
334
|
+
* A presenter takes the character (true) or gives it back (false). While on:
|
|
335
|
+
* no pose, sleep, caret watch, chip attention or acknowledgement from here;
|
|
336
|
+
* the hold this director had is handed over as it is (the presenter holds it).
|
|
337
|
+
*/
|
|
338
|
+
present(on) {
|
|
339
|
+
const next = !!on;
|
|
340
|
+
if (destroyed || next === presenting) return;
|
|
341
|
+
presenting = next;
|
|
342
|
+
if (presenting) {
|
|
343
|
+
clearAttention();
|
|
344
|
+
dropOneShot();
|
|
345
|
+
if (typingTimer) { cancel(typingTimer); typingTimer = 0; }
|
|
346
|
+
if (lookTimer) { cancel(lookTimer); lookTimer = 0; }
|
|
347
|
+
typing = false;
|
|
348
|
+
typingPoint = null;
|
|
349
|
+
sleeping = false;
|
|
350
|
+
if (sleepTimer) { cancel(sleepTimer); sleepTimer = 0; }
|
|
351
|
+
holding = false;
|
|
352
|
+
shown = '';
|
|
353
|
+
return;
|
|
354
|
+
}
|
|
355
|
+
lastInput = now();
|
|
356
|
+
apply();
|
|
357
|
+
armSleep();
|
|
358
|
+
},
|
|
322
359
|
/** For tests and devtools: what the director holds right now. */
|
|
323
|
-
state: () => ({ target: target?.kind || null, pose: shown.split('|')[0] || null, typing, attention: attention?.pose || null, menuOpen: !!attention?.open, blocked: !!blocked, offline, sleeping, oneShot: oneShot?.pose || null }),
|
|
360
|
+
state: () => ({ target: target?.kind || null, presenting, pose: shown.split('|')[0] || null, typing, attention: attention?.pose || null, menuOpen: !!attention?.open, blocked: !!blocked, offline, sleeping, oneShot: oneShot?.pose || null }),
|
|
324
361
|
destroy() {
|
|
325
362
|
if (destroyed) return;
|
|
326
363
|
if (target && holding) safe(() => target.hold(false));
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
// `footer`: items under the list that stay in view while the list scrolls.
|
|
18
18
|
// Every menu is measured at the width it is shown at, never wider than its box less the margins,
|
|
19
19
|
// and opens where that box has room (the list scrolls inside it). The box is the Assistant
|
|
20
|
-
// panel or root around the anchor, else the window.
|
|
20
|
+
// panel or root around the anchor, else the app frame (app-frame.js; the window on a page without it).
|
|
21
21
|
// `bounds`: an element (or () => DOMRect) to use as that box instead: the menu is then exactly
|
|
22
22
|
// `width` wide (or the box's room) and follows the box on resize.
|
|
23
23
|
// `onEscape`: returns true when it handled Esc (e.g. went back a view): the menu stays open.
|
|
@@ -44,6 +44,8 @@
|
|
|
44
44
|
// any group left empty. The handle's update({ title, items, footer, focus }) redraws
|
|
45
45
|
// an open menu in place (focus: the data-id to land on; the filter's text still applies).
|
|
46
46
|
|
|
47
|
+
import { frameRect } from '../app-frame.js';
|
|
48
|
+
|
|
47
49
|
let _open = null;
|
|
48
50
|
let _uid = 0;
|
|
49
51
|
let _lastDown = null;
|
|
@@ -290,14 +292,13 @@ const BOUNDED_MIN_WIDTH = 248;
|
|
|
290
292
|
const MENU_MIN_HEIGHT = 96;
|
|
291
293
|
const BOUNDED_MIN_HEIGHT = 140;
|
|
292
294
|
|
|
293
|
-
/** The box a menu stays inside: its bounds clipped to the
|
|
295
|
+
/** The box a menu stays inside: its bounds clipped to the app frame (the frame when it has none). */
|
|
294
296
|
function boundsBox(bounds) {
|
|
295
|
-
const
|
|
296
|
-
const vh = window.innerHeight;
|
|
297
|
+
const f = frameRect();
|
|
297
298
|
let r = null;
|
|
298
299
|
try { r = typeof bounds === 'function' ? bounds() : bounds?.getBoundingClientRect?.(); } catch { r = null; }
|
|
299
|
-
if (!r || !(r.width > 0) || !(r.height > 0)) return { left:
|
|
300
|
-
return { left: Math.max(
|
|
300
|
+
if (!r || !(r.width > 0) || !(r.height > 0)) return { left: f.left, top: f.top, right: f.right, bottom: f.bottom };
|
|
301
|
+
return { left: Math.max(f.left, r.left), top: Math.max(f.top, r.top), right: Math.min(f.right, r.right), bottom: Math.min(f.bottom, r.bottom) };
|
|
301
302
|
}
|
|
302
303
|
|
|
303
304
|
/** Where a menu without `bounds` stays: the Assistant around its anchor (the sidepanel, or a tab's own root). */
|