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
|
@@ -15,8 +15,8 @@ import { emit } from './state.js';
|
|
|
15
15
|
import { getLocale, t } from './i18n.js';
|
|
16
16
|
import { desktopBridge } from './desktop-bridge.js';
|
|
17
17
|
import {
|
|
18
|
-
desktopCheckOutcome, desktopUpdateMeta, desktopUpdateNotice, desktopUpdateTooltip, desktopUpdateView, fillDesktopCopy,
|
|
19
|
-
versionDataFromDesktop,
|
|
18
|
+
desktopCheckOutcome, desktopRestartButton, desktopUpdateMeta, desktopUpdateNotice, desktopUpdateTooltip, desktopUpdateView, fillDesktopCopy,
|
|
19
|
+
readDesktopUpdateState, versionDataFromDesktop,
|
|
20
20
|
} from './desktop-update-view.js';
|
|
21
21
|
|
|
22
22
|
const $ = (id) => document.getElementById(id);
|
|
@@ -83,6 +83,25 @@ function applyTopRightButton(data) {
|
|
|
83
83
|
? desktopUpdateTooltip(_desktopState, { t: desktopT })
|
|
84
84
|
: `SynaBun update available: v${data.current} → v${data.latest} (${srcLabel})`;
|
|
85
85
|
}
|
|
86
|
+
// The desktop app with an update downloaded: the button says Restart to update, as Discord's green one does.
|
|
87
|
+
const restart = has && data.desktop ? desktopRestartButton(_desktopState, { t: desktopT }) : null;
|
|
88
|
+
btn.classList.toggle('update-ready', !!restart);
|
|
89
|
+
let label = btn.querySelector('.update-ready-label');
|
|
90
|
+
if (restart) {
|
|
91
|
+
if (!label) {
|
|
92
|
+
label = document.createElement('span');
|
|
93
|
+
label.className = 'update-ready-label';
|
|
94
|
+
btn.appendChild(label);
|
|
95
|
+
}
|
|
96
|
+
if (label.textContent !== restart.label) label.textContent = restart.label;
|
|
97
|
+
btn.dataset.tooltip = restart.tooltip;
|
|
98
|
+
btn.setAttribute('aria-label', restart.label);
|
|
99
|
+
btn.classList.toggle('update-ready--asked', restart.asked);
|
|
100
|
+
} else {
|
|
101
|
+
label?.remove();
|
|
102
|
+
btn.classList.remove('update-ready--asked');
|
|
103
|
+
btn.removeAttribute('aria-label');
|
|
104
|
+
}
|
|
86
105
|
if (!_btnWired) {
|
|
87
106
|
_btnWired = true;
|
|
88
107
|
// Whole button opens the update flow — it's a dedicated SynaBun-update
|
|
@@ -92,11 +111,28 @@ function applyTopRightButton(data) {
|
|
|
92
111
|
if (!_versionData?.updateAvailable) return;
|
|
93
112
|
e.stopPropagation();
|
|
94
113
|
e.preventDefault();
|
|
114
|
+
if (btn.classList.contains('update-ready')) { restartToUpdate(); return; }
|
|
95
115
|
openUpdateModal();
|
|
96
116
|
});
|
|
97
117
|
}
|
|
98
118
|
}
|
|
99
119
|
|
|
120
|
+
/**
|
|
121
|
+
* Restart to update (the desktop app's ready button): Install now. When SynaBun is idle the app restarts into the
|
|
122
|
+
* update at once (its splash takes the window's place); while something runs, the dialog says what it waits for.
|
|
123
|
+
*/
|
|
124
|
+
async function restartToUpdate() {
|
|
125
|
+
const desktop = desktopBridge();
|
|
126
|
+
if (!desktop) return;
|
|
127
|
+
const btn = $('topright-update-btn');
|
|
128
|
+
btn?.setAttribute('aria-busy', 'true');
|
|
129
|
+
let answer = null;
|
|
130
|
+
try { answer = await desktop.updates.install(); } catch { /* the dialog below says what the app reports */ }
|
|
131
|
+
btn?.removeAttribute('aria-busy');
|
|
132
|
+
if (answer) applyDesktopState(answer, { alert: false });
|
|
133
|
+
if (readDesktopUpdateState(answer || _desktopState).status !== 'installing') openDesktopUpdateModal();
|
|
134
|
+
}
|
|
135
|
+
|
|
100
136
|
function maybeShowUpdateAlert(data, { once = true } = {}) {
|
|
101
137
|
if (!data?.updateAvailable) return;
|
|
102
138
|
const key = `synabun-update-alert:${data.current}->${data.latest}:${synabunUpdateSourceLabel(data)}`;
|
|
@@ -13,6 +13,9 @@ import { storage } from './storage.js';
|
|
|
13
13
|
import { isGuest, hasPermission } from './ui-sync.js';
|
|
14
14
|
import { openSettingsModal } from './ui-settings.js';
|
|
15
15
|
import { fetchRulesStatus } from './api.js';
|
|
16
|
+
import { registerContextMenuArea } from './context-menu-areas.js';
|
|
17
|
+
import { frameRect } from './app-frame.js';
|
|
18
|
+
import { readListItems, writeListItems, fitListLevels, indentListItem, outdentListItem, listMarkers } from './whiteboard-list.js';
|
|
16
19
|
|
|
17
20
|
// ── Helpers ──
|
|
18
21
|
|
|
@@ -412,11 +415,31 @@ function flushListEdits() {
|
|
|
412
415
|
const elId = dom.dataset.wbId;
|
|
413
416
|
const el = _elements.find(e => e.id === elId);
|
|
414
417
|
if (el && ul) {
|
|
415
|
-
|
|
416
|
-
if (
|
|
418
|
+
const { items, levels } = _readListDom(ul);
|
|
419
|
+
if (items.length) writeListItems(el, items, levels);
|
|
420
|
+
else writeListItems(el, [''], [0]);
|
|
417
421
|
}
|
|
418
422
|
}
|
|
419
423
|
|
|
424
|
+
/** Text and level of every <li> of a list, an edit in progress included (levels: whiteboard-list.js). */
|
|
425
|
+
function _readListDom(ul) {
|
|
426
|
+
const lis = [...ul.querySelectorAll('li')];
|
|
427
|
+
return {
|
|
428
|
+
items: lis.map(li => li.innerText.trimEnd()),
|
|
429
|
+
levels: lis.map(li => Number(li.dataset.level) || 0),
|
|
430
|
+
};
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
// The list being edited as it was when editing began (the "before" of its undo entry).
|
|
434
|
+
let _listEditBefore = null;
|
|
435
|
+
|
|
436
|
+
/** A copy of a list element for an undo entry: its own items and levels arrays. */
|
|
437
|
+
function _listSnapshot(el) {
|
|
438
|
+
const copy = { ...el, items: [...(el.items || [])] };
|
|
439
|
+
if (Array.isArray(el.levels)) copy.levels = [...el.levels];
|
|
440
|
+
return copy;
|
|
441
|
+
}
|
|
442
|
+
|
|
420
443
|
/** Flush in-progress text edits from contenteditable DOM back into _elements (without exiting edit mode). */
|
|
421
444
|
function flushTextEdits() {
|
|
422
445
|
const editingText = _root?.querySelector('.wb-text.editing .wb-text-content');
|
|
@@ -874,7 +897,8 @@ function updateElementDOM(dom, el) {
|
|
|
874
897
|
} else if (el.type === 'list') {
|
|
875
898
|
let list = dom.querySelector('ul, ol');
|
|
876
899
|
if (list && !dom.classList.contains('editing')) {
|
|
877
|
-
|
|
900
|
+
// Plain strings without `levels` (every older board) read as level 0.
|
|
901
|
+
const { items, levels } = readListItems(el.items && el.items.length ? el.items : [''], el.levels);
|
|
878
902
|
const wantTag = el.ordered ? 'OL' : 'UL';
|
|
879
903
|
if (list.tagName !== wantTag) {
|
|
880
904
|
// ordered flag changed — swap the container (never while editing)
|
|
@@ -883,10 +907,10 @@ function updateElementDOM(dom, el) {
|
|
|
883
907
|
list = swapped;
|
|
884
908
|
dom._wbListKey = null;
|
|
885
909
|
}
|
|
886
|
-
const key = wantTag + '|' + JSON.stringify(items);
|
|
910
|
+
const key = wantTag + '|' + JSON.stringify(items) + '|' + levels.join(',');
|
|
887
911
|
if (dom._wbListKey !== key) {
|
|
888
|
-
list.innerHTML = items.map(item =>
|
|
889
|
-
`<li>${item.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>')}</li>`
|
|
912
|
+
list.innerHTML = items.map((item, i) =>
|
|
913
|
+
`<li${levels[i] ? ` data-level="${levels[i]}"` : ''}>${item.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>')}</li>`
|
|
890
914
|
).join('');
|
|
891
915
|
dom._wbListKey = key;
|
|
892
916
|
}
|
|
@@ -2060,25 +2084,28 @@ function exitEditMode() {
|
|
|
2060
2084
|
if (editing) editing.blur();
|
|
2061
2085
|
}
|
|
2062
2086
|
|
|
2063
|
-
|
|
2087
|
+
/**
|
|
2088
|
+
* Edit a list. `point` ({ x, y }, viewport px) is where the double-click that opened it landed: the caret goes
|
|
2089
|
+
* exactly there (that item, that line, that character). The items were not editable when the press came, so the
|
|
2090
|
+
* browser placed no caret of its own. Without a point (a list the list tool just made) the caret goes to the end
|
|
2091
|
+
* of the last item.
|
|
2092
|
+
*/
|
|
2093
|
+
function enterListEditMode(dom, elId, point = null) {
|
|
2064
2094
|
const ul = dom.querySelector('ul, ol');
|
|
2065
2095
|
if (!ul) return;
|
|
2066
2096
|
dom.classList.add('editing');
|
|
2097
|
+
// The undo entry of this edit starts from here: the saves made while editing (Tab, Enter, Backspace call
|
|
2098
|
+
// persistDebounced, which flushes the items into the element) must not become its "before".
|
|
2099
|
+
const el = _elements.find(e => e.id === elId);
|
|
2100
|
+
_listEditBefore = el ? _listSnapshot(el) : null;
|
|
2067
2101
|
|
|
2068
2102
|
// Make all existing items editable
|
|
2069
2103
|
[...ul.querySelectorAll('li')].forEach(li => li.setAttribute('contenteditable', 'true'));
|
|
2070
2104
|
|
|
2071
|
-
|
|
2105
|
+
if (point && _placeListCaretAt(dom, point.x, point.y)) return;
|
|
2072
2106
|
const lis = [...ul.querySelectorAll('li')];
|
|
2073
2107
|
const target = lis[lis.length - 1] || null;
|
|
2074
|
-
if (target)
|
|
2075
|
-
target.focus();
|
|
2076
|
-
const range = document.createRange();
|
|
2077
|
-
range.selectNodeContents(target);
|
|
2078
|
-
range.collapse(false);
|
|
2079
|
-
window.getSelection().removeAllRanges();
|
|
2080
|
-
window.getSelection().addRange(range);
|
|
2081
|
-
}
|
|
2108
|
+
if (target) _placeCaret(target, 'end');
|
|
2082
2109
|
}
|
|
2083
2110
|
|
|
2084
2111
|
function exitListEditMode() {
|
|
@@ -2093,18 +2120,156 @@ function exitListEditMode() {
|
|
|
2093
2120
|
if (document.activeElement?.closest('.wb-list')) document.activeElement.blur();
|
|
2094
2121
|
|
|
2095
2122
|
const el = _elements.find(e => e.id === elId);
|
|
2123
|
+
const editBefore = _listEditBefore;
|
|
2124
|
+
_listEditBefore = null;
|
|
2096
2125
|
if (el) {
|
|
2097
|
-
const before =
|
|
2098
|
-
const
|
|
2099
|
-
|
|
2100
|
-
|
|
2101
|
-
|
|
2126
|
+
const before = editBefore?.id === el.id ? editBefore : _listSnapshot(el);
|
|
2127
|
+
const { items, levels } = _readListDom(ul);
|
|
2128
|
+
// Empty items are dropped (the only item stays); the levels of what is left are fitted again.
|
|
2129
|
+
const kept = items.map((text, i) => ({ text, level: levels[i] })).filter((it, i, arr) => it.text !== '' || arr.length === 1);
|
|
2130
|
+
if (kept.length) writeListItems(el, kept.map(it => it.text), kept.map(it => it.level));
|
|
2131
|
+
else writeListItems(el, [''], [0]);
|
|
2132
|
+
pushUndo('edit', before, _listSnapshot(el));
|
|
2102
2133
|
dom._wbListKey = null; // editing mutated the <li>s directly — force a clean rewrite
|
|
2103
2134
|
renderElements();
|
|
2104
2135
|
persistDebounced();
|
|
2105
2136
|
}
|
|
2106
2137
|
}
|
|
2107
2138
|
|
|
2139
|
+
/** A collapsed range at a viewport point (the standard call, then the WebKit/Blink one), or null. */
|
|
2140
|
+
function _caretRangeAtPoint(x, y) {
|
|
2141
|
+
if (typeof document.caretPositionFromPoint === 'function') {
|
|
2142
|
+
const pos = document.caretPositionFromPoint(x, y);
|
|
2143
|
+
if (pos?.offsetNode) {
|
|
2144
|
+
const range = document.createRange();
|
|
2145
|
+
range.setStart(pos.offsetNode, pos.offset);
|
|
2146
|
+
range.collapse(true);
|
|
2147
|
+
return range;
|
|
2148
|
+
}
|
|
2149
|
+
}
|
|
2150
|
+
if (typeof document.caretRangeFromPoint === 'function') return document.caretRangeFromPoint(x, y);
|
|
2151
|
+
return null;
|
|
2152
|
+
}
|
|
2153
|
+
|
|
2154
|
+
/** Focus an item being edited with the caret at its start or end. */
|
|
2155
|
+
function _placeCaret(li, where) {
|
|
2156
|
+
li.focus({ preventScroll: true });
|
|
2157
|
+
const range = document.createRange();
|
|
2158
|
+
range.selectNodeContents(li);
|
|
2159
|
+
range.collapse(where === 'start');
|
|
2160
|
+
const sel = window.getSelection();
|
|
2161
|
+
sel.removeAllRanges();
|
|
2162
|
+
sel.addRange(range);
|
|
2163
|
+
}
|
|
2164
|
+
|
|
2165
|
+
/**
|
|
2166
|
+
* Put the caret of a list being edited under a viewport point: in the item on that row, at the character under
|
|
2167
|
+
* the point. A point beside the text (the padding, a bullet, past the end of a line) lands on the nearest spot
|
|
2168
|
+
* of the nearest item. Returns false when the list has no item.
|
|
2169
|
+
*/
|
|
2170
|
+
function _placeListCaretAt(listDom, x, y) {
|
|
2171
|
+
const lis = [...listDom.querySelectorAll('li')];
|
|
2172
|
+
if (!lis.length) return false;
|
|
2173
|
+
let li = lis[0], best = Infinity;
|
|
2174
|
+
for (const item of lis) {
|
|
2175
|
+
const r = item.getBoundingClientRect();
|
|
2176
|
+
const d = y < r.top ? r.top - y : y > r.bottom ? y - r.bottom : 0;
|
|
2177
|
+
if (d < best) { best = d; li = item; }
|
|
2178
|
+
}
|
|
2179
|
+
const r = li.getBoundingClientRect();
|
|
2180
|
+
const cx = Math.min(Math.max(x, r.left + 1), r.right - 1);
|
|
2181
|
+
const cy = Math.min(Math.max(y, r.top + 1), r.bottom - 1);
|
|
2182
|
+
let range = _caretRangeAtPoint(cx, cy);
|
|
2183
|
+
if (!range || !li.contains(range.startContainer)) {
|
|
2184
|
+
range = document.createRange();
|
|
2185
|
+
range.selectNodeContents(li);
|
|
2186
|
+
range.collapse(x < r.left + r.width / 2);
|
|
2187
|
+
}
|
|
2188
|
+
li.focus({ preventScroll: true });
|
|
2189
|
+
const sel = window.getSelection();
|
|
2190
|
+
sel.removeAllRanges();
|
|
2191
|
+
sel.addRange(range);
|
|
2192
|
+
return true;
|
|
2193
|
+
}
|
|
2194
|
+
|
|
2195
|
+
/** Write levels onto <li>s (level 0 is no attribute, as the render writes it). */
|
|
2196
|
+
function _applyListLevels(lis, levels) {
|
|
2197
|
+
lis.forEach((li, i) => {
|
|
2198
|
+
const level = levels[i] || 0;
|
|
2199
|
+
if (level) li.dataset.level = String(level);
|
|
2200
|
+
else delete li.dataset.level;
|
|
2201
|
+
});
|
|
2202
|
+
}
|
|
2203
|
+
|
|
2204
|
+
/** Fit the levels of a list being edited after an item was removed or merged (whiteboard-list.js rules). */
|
|
2205
|
+
function _refitListDom(ul) {
|
|
2206
|
+
const lis = [...ul.querySelectorAll('li')];
|
|
2207
|
+
_applyListLevels(lis, fitListLevels(lis.map(li => Number(li.dataset.level) || 0), lis.length));
|
|
2208
|
+
}
|
|
2209
|
+
|
|
2210
|
+
/** Tab / Shift+Tab on an item being edited: one level deeper (+1) or shallower (-1). False when it cannot move. */
|
|
2211
|
+
function _shiftListItemLevel(li, dir) {
|
|
2212
|
+
const lis = [...li.parentElement.querySelectorAll('li')];
|
|
2213
|
+
const levels = lis.map(n => Number(n.dataset.level) || 0);
|
|
2214
|
+
const index = lis.indexOf(li);
|
|
2215
|
+
const next = dir > 0 ? indentListItem(levels, index) : outdentListItem(levels, index);
|
|
2216
|
+
if (!next) return false;
|
|
2217
|
+
_applyListLevels(lis, next);
|
|
2218
|
+
return true;
|
|
2219
|
+
}
|
|
2220
|
+
|
|
2221
|
+
/** An item left with no text holds nothing (no stray <br>), so the empty placeholder shows. */
|
|
2222
|
+
function _tidyEmptyItem(li) {
|
|
2223
|
+
if (li.textContent === '' && !li.querySelector('img')) li.replaceChildren();
|
|
2224
|
+
}
|
|
2225
|
+
|
|
2226
|
+
/** Whether the caret (collapsed) sits at the very start of an item being edited. */
|
|
2227
|
+
function _caretAtItemStart(li) {
|
|
2228
|
+
const sel = window.getSelection();
|
|
2229
|
+
if (!sel.rangeCount || !sel.isCollapsed) return false;
|
|
2230
|
+
const caret = sel.getRangeAt(0);
|
|
2231
|
+
if (!li.contains(caret.startContainer)) return false;
|
|
2232
|
+
const before = document.createRange();
|
|
2233
|
+
before.setStart(li, 0);
|
|
2234
|
+
before.setEnd(caret.startContainer, caret.startOffset);
|
|
2235
|
+
return before.toString() === '' && !before.cloneContents().querySelector('br, img');
|
|
2236
|
+
}
|
|
2237
|
+
|
|
2238
|
+
/** Enter: what follows the caret (the selection removed first) leaves the item, for the new one. */
|
|
2239
|
+
function _cutAfterCaret(li) {
|
|
2240
|
+
const sel = window.getSelection();
|
|
2241
|
+
if (!sel.rangeCount) return null;
|
|
2242
|
+
const range = sel.getRangeAt(0);
|
|
2243
|
+
if (!li.contains(range.startContainer)) return null;
|
|
2244
|
+
if (!li.contains(range.endContainer)) range.collapse(true);
|
|
2245
|
+
else if (!range.collapsed) range.deleteContents();
|
|
2246
|
+
const tail = document.createRange();
|
|
2247
|
+
tail.setStart(range.startContainer, range.startOffset);
|
|
2248
|
+
tail.setEnd(li, li.childNodes.length);
|
|
2249
|
+
return tail.extractContents();
|
|
2250
|
+
}
|
|
2251
|
+
|
|
2252
|
+
/** Backspace at the start of an item that has text: it joins the end of the item above, the caret at the seam. */
|
|
2253
|
+
function _mergeIntoPrevious(li) {
|
|
2254
|
+
const prev = li.previousElementSibling;
|
|
2255
|
+
if (!prev) return false;
|
|
2256
|
+
_tidyEmptyItem(prev);
|
|
2257
|
+
const seam = prev.childNodes.length;
|
|
2258
|
+
_tidyEmptyItem(li);
|
|
2259
|
+
while (li.firstChild) prev.appendChild(li.firstChild);
|
|
2260
|
+
const ul = li.parentElement;
|
|
2261
|
+
li.remove();
|
|
2262
|
+
_refitListDom(ul);
|
|
2263
|
+
prev.focus({ preventScroll: true });
|
|
2264
|
+
const range = document.createRange();
|
|
2265
|
+
range.setStart(prev, seam);
|
|
2266
|
+
range.collapse(true);
|
|
2267
|
+
const sel = window.getSelection();
|
|
2268
|
+
sel.removeAllRanges();
|
|
2269
|
+
sel.addRange(range);
|
|
2270
|
+
return true;
|
|
2271
|
+
}
|
|
2272
|
+
|
|
2108
2273
|
|
|
2109
2274
|
// ═══════════════════════════════════════════
|
|
2110
2275
|
// MULTI-SELECT HELPERS
|
|
@@ -2172,6 +2337,19 @@ function onMouseDown(e) {
|
|
|
2172
2337
|
// Block drawing for guests without whiteboard permission (select/view still allowed)
|
|
2173
2338
|
if (isGuest() && !hasPermission('whiteboard') && _activeTool !== 'select') return;
|
|
2174
2339
|
|
|
2340
|
+
// A press inside the list being edited belongs to its text, as in a text editor: the browser puts the caret
|
|
2341
|
+
// under the pointer (any item, any line, any character) and a drag selects. Leaving edit mode here, starting
|
|
2342
|
+
// a move and preventDefault() (below, for any press on an element) is what threw the caret onto the end of
|
|
2343
|
+
// the last item. A press beside the items (padding, bullets) is placed on the nearest item by hand.
|
|
2344
|
+
const editingList = e.target.closest?.('.wb-list.editing');
|
|
2345
|
+
if (editingList) {
|
|
2346
|
+
if (!e.target.closest('li')) {
|
|
2347
|
+
e.preventDefault();
|
|
2348
|
+
_placeListCaretAt(editingList, e.clientX, e.clientY);
|
|
2349
|
+
}
|
|
2350
|
+
return;
|
|
2351
|
+
}
|
|
2352
|
+
|
|
2175
2353
|
// Steal focus from terminal/other elements so Ctrl+V paste works on whiteboard
|
|
2176
2354
|
_root.focus({ preventScroll: true });
|
|
2177
2355
|
|
|
@@ -2799,12 +2977,14 @@ function onDblClick(e) {
|
|
|
2799
2977
|
return;
|
|
2800
2978
|
}
|
|
2801
2979
|
|
|
2802
|
-
// Double-click list → edit
|
|
2980
|
+
// Double-click list → edit, the caret where the double-click landed
|
|
2803
2981
|
const listEl = e.target.closest('.wb-list[data-wb-id]');
|
|
2804
2982
|
if (listEl && _activeTool === 'select') {
|
|
2983
|
+
// Already editing: the double-click is the text's (the browser selects the word under it).
|
|
2984
|
+
if (listEl.classList.contains('editing')) return;
|
|
2805
2985
|
const id = listEl.dataset.wbId;
|
|
2806
2986
|
selectElement(id);
|
|
2807
|
-
enterListEditMode(listEl, id);
|
|
2987
|
+
enterListEditMode(listEl, id, { x: e.clientX, y: e.clientY });
|
|
2808
2988
|
e.preventDefault();
|
|
2809
2989
|
return;
|
|
2810
2990
|
}
|
|
@@ -2915,35 +3095,63 @@ function onKeyDown(e) {
|
|
|
2915
3095
|
const editingListLi = document.activeElement?.closest('.wb-list.editing li') || null;
|
|
2916
3096
|
const isEditing = isEditingText || !!editingListLi;
|
|
2917
3097
|
|
|
2918
|
-
//
|
|
2919
|
-
|
|
3098
|
+
// Keys of a list item being edited (an IME composition keeps its own Enter / Tab / Backspace).
|
|
3099
|
+
// Tab / Shift+Tab → one level deeper / shallower (whiteboard-list.js rules). Always taken here: Tab never
|
|
3100
|
+
// leaves the list or types a tab, even when the item cannot move.
|
|
3101
|
+
if (editingListLi && e.key === 'Tab' && !e.isComposing && !e.ctrlKey && !e.metaKey && !e.altKey) {
|
|
3102
|
+
e.preventDefault();
|
|
3103
|
+
e.stopImmediatePropagation();
|
|
3104
|
+
if (_shiftListItemLevel(editingListLi, e.shiftKey ? -1 : 1)) persistDebounced();
|
|
3105
|
+
return;
|
|
3106
|
+
}
|
|
3107
|
+
|
|
3108
|
+
// List Enter → a new item at the same level; what follows the caret moves into it
|
|
3109
|
+
if (editingListLi && e.key === 'Enter' && !e.isComposing) {
|
|
2920
3110
|
e.preventDefault();
|
|
2921
3111
|
e.stopImmediatePropagation();
|
|
2922
3112
|
const newLi = document.createElement('li');
|
|
2923
3113
|
newLi.setAttribute('contenteditable', 'true');
|
|
3114
|
+
if (editingListLi.dataset.level) newLi.dataset.level = editingListLi.dataset.level;
|
|
3115
|
+
const tail = _cutAfterCaret(editingListLi);
|
|
3116
|
+
if (tail) newLi.appendChild(tail);
|
|
3117
|
+
_tidyEmptyItem(editingListLi);
|
|
3118
|
+
_tidyEmptyItem(newLi);
|
|
2924
3119
|
editingListLi.after(newLi);
|
|
2925
|
-
newLi
|
|
3120
|
+
_placeCaret(newLi, 'start');
|
|
3121
|
+
persistDebounced();
|
|
2926
3122
|
return;
|
|
2927
3123
|
}
|
|
2928
3124
|
|
|
2929
|
-
// List Backspace
|
|
2930
|
-
|
|
3125
|
+
// List Backspace at the start of an item: an indented item goes one level up first; then an empty item is
|
|
3126
|
+
// removed (keep at least 1) and one with text joins the item above.
|
|
3127
|
+
if (editingListLi && e.key === 'Backspace' && !e.isComposing
|
|
3128
|
+
&& (editingListLi.textContent === '' || _caretAtItemStart(editingListLi))) {
|
|
2931
3129
|
const ul = editingListLi.parentElement;
|
|
2932
|
-
|
|
3130
|
+
const plain = !e.ctrlKey && !e.metaKey && !e.altKey;
|
|
3131
|
+
if (plain && (Number(editingListLi.dataset.level) || 0) > 0) {
|
|
2933
3132
|
e.preventDefault();
|
|
2934
3133
|
e.stopImmediatePropagation();
|
|
2935
|
-
|
|
2936
|
-
|
|
2937
|
-
|
|
2938
|
-
|
|
2939
|
-
if (
|
|
2940
|
-
|
|
2941
|
-
|
|
2942
|
-
|
|
2943
|
-
|
|
2944
|
-
|
|
2945
|
-
|
|
3134
|
+
if (_shiftListItemLevel(editingListLi, -1)) persistDebounced();
|
|
3135
|
+
return;
|
|
3136
|
+
}
|
|
3137
|
+
if (editingListLi.textContent === '') {
|
|
3138
|
+
if (ul && ul.children.length > 1) {
|
|
3139
|
+
e.preventDefault();
|
|
3140
|
+
e.stopImmediatePropagation();
|
|
3141
|
+
const prev = editingListLi.previousElementSibling;
|
|
3142
|
+
const next = editingListLi.nextElementSibling;
|
|
3143
|
+
editingListLi.remove();
|
|
3144
|
+
_refitListDom(ul);
|
|
3145
|
+
const focusTarget = prev || next;
|
|
3146
|
+
if (focusTarget) _placeCaret(focusTarget, 'end');
|
|
3147
|
+
persistDebounced();
|
|
2946
3148
|
}
|
|
3149
|
+
return;
|
|
3150
|
+
}
|
|
3151
|
+
if (plain && editingListLi.previousElementSibling) {
|
|
3152
|
+
e.preventDefault();
|
|
3153
|
+
e.stopImmediatePropagation();
|
|
3154
|
+
if (_mergeIntoPrevious(editingListLi)) persistDebounced();
|
|
2947
3155
|
}
|
|
2948
3156
|
return;
|
|
2949
3157
|
}
|
|
@@ -3400,6 +3608,41 @@ export function addImageToWhiteboard(url) {
|
|
|
3400
3608
|
img.src = url;
|
|
3401
3609
|
}
|
|
3402
3610
|
|
|
3611
|
+
// ═══════════════════════════════════════════
|
|
3612
|
+
// THE APP'S RIGHT-CLICK MENU (context-menu-model.js focusItems)
|
|
3613
|
+
// ═══════════════════════════════════════════
|
|
3614
|
+
|
|
3615
|
+
// Each row goes through what the board already does: its toolbar's own buttons (onToolbarClick, with
|
|
3616
|
+
// its guest check and the multi-select mode a tool leaves) and Ctrl+A's selectMultiple.
|
|
3617
|
+
const pressToolbar = (selector) => _toolbar?.querySelector(selector)?.click();
|
|
3618
|
+
|
|
3619
|
+
// The whole focus-mode screen (#static-bg while it is shown): the board, its toolbar, the logo.
|
|
3620
|
+
const focusMenuArea = {
|
|
3621
|
+
id: 'focus-whiteboard',
|
|
3622
|
+
match(el) {
|
|
3623
|
+
if (!el?.closest?.('#static-bg.visible')) return null;
|
|
3624
|
+
return {
|
|
3625
|
+
kind: 'focus',
|
|
3626
|
+
focus: {
|
|
3627
|
+
tool: _activeTool,
|
|
3628
|
+
multi: _multiSelectMode,
|
|
3629
|
+
canUndo: _undoStack.length > 0,
|
|
3630
|
+
canRedo: _redoStack.length > 0,
|
|
3631
|
+
hasElements: _elements.length > 0,
|
|
3632
|
+
canEdit: !(isGuest() && !hasPermission('whiteboard')),
|
|
3633
|
+
},
|
|
3634
|
+
act: {
|
|
3635
|
+
setTool: (name) => pressToolbar(`.wb-tool[data-tool="${name}"]`),
|
|
3636
|
+
toggleMulti: () => pressToolbar('#wb-multiselect'),
|
|
3637
|
+
undo: () => pressToolbar('#wb-undo'),
|
|
3638
|
+
redo: () => pressToolbar('#wb-redo'),
|
|
3639
|
+
selectAll: () => { const ids = _elements.map(e => e.id); if (ids.length) selectMultiple(ids); },
|
|
3640
|
+
exitFocus: () => $('titlebar-viz-toggle')?.click(), // the navbar's button, as the V key presses it
|
|
3641
|
+
},
|
|
3642
|
+
};
|
|
3643
|
+
},
|
|
3644
|
+
};
|
|
3645
|
+
|
|
3403
3646
|
// ═══════════════════════════════════════════
|
|
3404
3647
|
// INIT
|
|
3405
3648
|
// ═══════════════════════════════════════════
|
|
@@ -3487,8 +3730,10 @@ export function initWhiteboard() {
|
|
|
3487
3730
|
// Logo-adjacent ruleset reminder
|
|
3488
3731
|
initRulesetAlert();
|
|
3489
3732
|
|
|
3490
|
-
//
|
|
3491
|
-
|
|
3733
|
+
// Right-click: the board has no menu of its own, so it leaves the event to the app's menu
|
|
3734
|
+
// (ui-context-menu.js), which adds the board's section from focusMenuArea. A card being
|
|
3735
|
+
// edited keeps the browser's menu (spelling, paste), as every field does.
|
|
3736
|
+
registerContextMenuArea(focusMenuArea);
|
|
3492
3737
|
|
|
3493
3738
|
// Listen for workspace events
|
|
3494
3739
|
on('whiteboard:restore', restoreWhiteboardSnapshot);
|
|
@@ -3618,27 +3863,31 @@ function _cssPx(name) {
|
|
|
3618
3863
|
}
|
|
3619
3864
|
|
|
3620
3865
|
/** Usable whiteboard viewport in CSS px relative to #wb-root's top-left: the
|
|
3621
|
-
*
|
|
3622
|
-
*
|
|
3623
|
-
*
|
|
3624
|
-
*
|
|
3625
|
-
*
|
|
3866
|
+
* part of the root inside the app frame (below the navbar, right of the
|
|
3867
|
+
* explorers, the gutter around it: app-frame.js), minus the left tool rail
|
|
3868
|
+
* and the docked right-side agent panel (its reserved width is published as
|
|
3869
|
+
* --right-panel-width by ui-sidepanel-layout.js; 0 when hidden or floating).
|
|
3870
|
+
* #wb-root already ends above a docked terminal (--terminal-height). Sent to
|
|
3871
|
+
* the server so MCP coordinate math and screenshots describe what the user
|
|
3872
|
+
* can actually see. */
|
|
3626
3873
|
function _computeViewport() {
|
|
3627
3874
|
const rect = _root.getBoundingClientRect();
|
|
3628
|
-
const titleBar = document.getElementById('title-bar');
|
|
3629
3875
|
const toolbar = document.getElementById('wb-toolbar');
|
|
3630
|
-
const navH = titleBar ? titleBar.getBoundingClientRect().height : _cssPx('--navbar-height');
|
|
3631
|
-
const tbW = toolbar ? Math.max(0, toolbar.getBoundingClientRect().right - rect.left + 12) : 60;
|
|
3632
3876
|
const rightW = Math.max(0, _cssPx('--right-panel-width') || _cssPx('--claude-panel-width'));
|
|
3877
|
+
const f = frameRect();
|
|
3878
|
+
const top = Math.max(rect.top, f.top);
|
|
3879
|
+
const left = Math.max(rect.left, f.left, toolbar ? toolbar.getBoundingClientRect().right + 12 : rect.left + 60);
|
|
3880
|
+
const right = Math.min(rect.right, f.right, window.innerWidth - rightW);
|
|
3881
|
+
const bottom = Math.min(rect.bottom, f.bottom);
|
|
3633
3882
|
return {
|
|
3634
3883
|
type: 'viewport',
|
|
3635
3884
|
clientId: _clientId,
|
|
3636
3885
|
focusActive: isWhiteboardActive(),
|
|
3637
3886
|
visible: document.visibilityState === 'visible',
|
|
3638
|
-
width: Math.max(0, Math.round(
|
|
3639
|
-
height: Math.max(0, Math.round(
|
|
3640
|
-
xOffset: Math.round(
|
|
3641
|
-
yOffset: Math.round(
|
|
3887
|
+
width: Math.max(0, Math.round(right - left)),
|
|
3888
|
+
height: Math.max(0, Math.round(bottom - top)),
|
|
3889
|
+
xOffset: Math.round(left - rect.left),
|
|
3890
|
+
yOffset: Math.round(top - rect.top),
|
|
3642
3891
|
rootWidth: Math.round(rect.width),
|
|
3643
3892
|
rootHeight: Math.round(rect.height),
|
|
3644
3893
|
rightPanelWidth: Math.round(rightW),
|
|
@@ -3695,6 +3944,7 @@ function _handleExternalCommand(msg) {
|
|
|
3695
3944
|
const before = { ...el, points: el.points ? el.points.map(p => [...p]) : undefined };
|
|
3696
3945
|
for (const [key, value] of Object.entries(msg.updates || {})) {
|
|
3697
3946
|
if (key === 'id' || key === 'type') continue;
|
|
3947
|
+
if (key === 'levels' && value == null) { delete el.levels; continue; } // the list went flat
|
|
3698
3948
|
el[key] = value;
|
|
3699
3949
|
}
|
|
3700
3950
|
pushUndo('edit', before, { ...el, points: el.points ? el.points.map(p => [...p]) : undefined });
|
|
@@ -3756,6 +4006,7 @@ function _handleExternalCommand(msg) {
|
|
|
3756
4006
|
befores.push(copyEl(el));
|
|
3757
4007
|
for (const [key, value] of Object.entries(op.updates || {})) {
|
|
3758
4008
|
if (key === 'id' || key === 'type') continue;
|
|
4009
|
+
if (key === 'levels' && value == null) { delete el.levels; continue; } // the list went flat
|
|
3759
4010
|
el[key] = value;
|
|
3760
4011
|
}
|
|
3761
4012
|
afters.push(copyEl(el));
|
|
@@ -3980,20 +4231,22 @@ async function _captureScreenshot(requestId, { crop } = {}) {
|
|
|
3980
4231
|
lineTop += lineH;
|
|
3981
4232
|
}
|
|
3982
4233
|
} else if (el.type === 'list') {
|
|
3983
|
-
// .wb-list: same box, line-height 1.5, <ul>/<ol> padding-left 1.4em
|
|
4234
|
+
// .wb-list: same box, line-height 1.5, <ul>/<ol> padding-left 1.4em, plus 1.4em per nesting level;
|
|
4235
|
+
// each level's bullet / numbering as styles.css draws it (whiteboard-list.js listMarkers)
|
|
3984
4236
|
const fs = el.fontSize || 18;
|
|
3985
4237
|
ctx.font = `400 ${fs}px ${mono}`;
|
|
3986
4238
|
ctx.fillStyle = el.color || 'rgba(255,255,255,0.85)';
|
|
3987
4239
|
ctx.textBaseline = 'alphabetic';
|
|
3988
4240
|
const lineH = fs * 1.5;
|
|
3989
4241
|
const baseOff = _lineBaseline(ctx, fs, lineH);
|
|
3990
|
-
const items = el.items && el.items.length ? el.items : [''];
|
|
3991
|
-
const
|
|
4242
|
+
const { items, levels } = readListItems(el.items && el.items.length ? el.items : [''], el.levels);
|
|
4243
|
+
const markers = listMarkers(levels, !!el.ordered);
|
|
3992
4244
|
let lineTop = el.y + 11;
|
|
3993
4245
|
items.forEach((item, i) => {
|
|
3994
4246
|
const baseline = lineTop + baseOff;
|
|
4247
|
+
const contentLeft = el.x + 15 + 1.4 * fs * (1 + levels[i]);
|
|
3995
4248
|
ctx.textAlign = 'right';
|
|
3996
|
-
ctx.fillText(
|
|
4249
|
+
ctx.fillText(markers[i], contentLeft - 0.6 * fs, baseline);
|
|
3997
4250
|
ctx.textAlign = 'left';
|
|
3998
4251
|
if (item) ctx.fillText(item, contentLeft, baseline);
|
|
3999
4252
|
lineTop += lineH;
|
|
@@ -34,6 +34,10 @@ let _wired = false;
|
|
|
34
34
|
|
|
35
35
|
function newer(next, current) {
|
|
36
36
|
if (!current) return true;
|
|
37
|
+
// A run resumed after a server restart (`resumes` only grows) is newer whatever its version: a crash can lose the
|
|
38
|
+
// last saved versions of a run a page already saw.
|
|
39
|
+
const resumed = (Number(next?.resumes) || 0) - (Number(current?.resumes) || 0);
|
|
40
|
+
if (resumed) return resumed > 0;
|
|
37
41
|
const a = Number(next?.version) || 0;
|
|
38
42
|
const b = Number(current?.version) || 0;
|
|
39
43
|
if (a !== b) return a > b;
|
|
@@ -24,6 +24,7 @@ import { storage } from './storage.js';
|
|
|
24
24
|
import { t } from './i18n.js';
|
|
25
25
|
import { posStore } from './node-positions-store.js';
|
|
26
26
|
import { getVariant } from './registry.js';
|
|
27
|
+
import { normalizeWorkspaceVariant } from './view-variant.js';
|
|
27
28
|
import {
|
|
28
29
|
getOpenCardsSnapshot,
|
|
29
30
|
closeAllCards,
|
|
@@ -51,14 +52,14 @@ let _isOpen = false;
|
|
|
51
52
|
|
|
52
53
|
function getWorkspaces() {
|
|
53
54
|
try {
|
|
54
|
-
return JSON.parse(storage.getItem(KEYS.WORKSPACES) || '[]');
|
|
55
|
+
return JSON.parse(storage.getItem(KEYS.WORKSPACES) || '[]').map(normalizeWorkspaceVariant);
|
|
55
56
|
} catch {
|
|
56
57
|
return [];
|
|
57
58
|
}
|
|
58
59
|
}
|
|
59
60
|
|
|
60
61
|
function saveWorkspacesToStorage(list) {
|
|
61
|
-
storage.setItem(KEYS.WORKSPACES, JSON.stringify(list));
|
|
62
|
+
storage.setItem(KEYS.WORKSPACES, JSON.stringify(list.map(normalizeWorkspaceVariant)));
|
|
62
63
|
}
|
|
63
64
|
|
|
64
65
|
function _readStoredPositions(key) {
|