synabun 2.0.7 → 2.0.10
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 +67 -0
- package/README.md +1 -1
- package/THIRD-PARTY-LICENSES.md +46 -4
- package/hooks/claude-code/post-plan.mjs +6 -30
- package/hooks/claude-code/session-start.mjs +1 -4
- package/hooks/claude-code/shared.mjs +106 -72
- package/launcher.mjs +9 -3
- package/lib/package-processes.js +670 -0
- package/lib/runtime-dependencies.js +225 -0
- package/lib/start-launcher.js +6 -1
- package/mcp-server/dist/services/browser-bench.d.ts +1 -1
- package/mcp-server/dist/services/browser-bench.d.ts.map +1 -1
- package/mcp-server/dist/services/browser-bench.js +1 -1
- package/mcp-server/dist/services/browser-bench.js.map +1 -1
- package/mcp-server/dist/services/categories.d.ts +35 -2
- package/mcp-server/dist/services/categories.d.ts.map +1 -1
- package/mcp-server/dist/services/categories.js +77 -33
- package/mcp-server/dist/services/categories.js.map +1 -1
- package/mcp-server/dist/services/desktop-bench.d.ts +7 -7
- package/mcp-server/dist/services/desktop-bench.d.ts.map +1 -1
- package/mcp-server/dist/services/desktop-bench.js +1 -1
- package/mcp-server/dist/services/desktop-bench.js.map +1 -1
- package/mcp-server/dist/tools/agents.d.ts +1 -1
- package/mcp-server/dist/tools/agents.d.ts.map +1 -1
- package/mcp-server/dist/tools/agents.js +1 -1
- package/mcp-server/dist/tools/agents.js.map +1 -1
- package/mcp-server/dist/tools/bluesky.js +1 -1
- package/mcp-server/dist/tools/bluesky.js.map +1 -1
- package/mcp-server/dist/tools/browser-advanced.d.ts +1 -1
- package/mcp-server/dist/tools/browser-advanced.d.ts.map +1 -1
- package/mcp-server/dist/tools/browser-advanced.js +1 -1
- package/mcp-server/dist/tools/browser-advanced.js.map +1 -1
- package/mcp-server/dist/tools/browser-batch.d.ts +1 -1
- package/mcp-server/dist/tools/browser-batch.d.ts.map +1 -1
- package/mcp-server/dist/tools/browser-batch.js +1 -1
- package/mcp-server/dist/tools/browser-batch.js.map +1 -1
- package/mcp-server/dist/tools/browser-cheatsheet.d.ts +1 -1
- package/mcp-server/dist/tools/browser-cheatsheet.d.ts.map +1 -1
- package/mcp-server/dist/tools/browser-cheatsheet.js +1 -1
- package/mcp-server/dist/tools/browser-cheatsheet.js.map +1 -1
- package/mcp-server/dist/tools/browser-interact.d.ts +1 -1
- package/mcp-server/dist/tools/browser-interact.d.ts.map +1 -1
- package/mcp-server/dist/tools/browser-interact.js +1 -1
- package/mcp-server/dist/tools/browser-interact.js.map +1 -1
- package/mcp-server/dist/tools/browser-navigate.d.ts +1 -1
- package/mcp-server/dist/tools/browser-navigate.d.ts.map +1 -1
- package/mcp-server/dist/tools/browser-navigate.js +1 -1
- package/mcp-server/dist/tools/browser-navigate.js.map +1 -1
- package/mcp-server/dist/tools/browser-observe.d.ts +1 -1
- package/mcp-server/dist/tools/browser-observe.d.ts.map +1 -1
- package/mcp-server/dist/tools/browser-observe.js +1 -1
- package/mcp-server/dist/tools/browser-observe.js.map +1 -1
- package/mcp-server/dist/tools/card-tools.d.ts +1 -1
- package/mcp-server/dist/tools/card-tools.d.ts.map +1 -1
- package/mcp-server/dist/tools/card-tools.js +1 -1
- package/mcp-server/dist/tools/card-tools.js.map +1 -1
- package/mcp-server/dist/tools/category.d.ts +1 -1
- package/mcp-server/dist/tools/category.d.ts.map +1 -1
- package/mcp-server/dist/tools/category.js +5 -1
- package/mcp-server/dist/tools/category.js.map +1 -1
- package/mcp-server/dist/tools/choice.d.ts +1 -1
- package/mcp-server/dist/tools/choice.d.ts.map +1 -1
- package/mcp-server/dist/tools/choice.js +1 -1
- package/mcp-server/dist/tools/choice.js.map +1 -1
- package/mcp-server/dist/tools/computer.d.ts +1 -1
- package/mcp-server/dist/tools/computer.d.ts.map +1 -1
- package/mcp-server/dist/tools/computer.js +1 -1
- package/mcp-server/dist/tools/computer.js.map +1 -1
- package/mcp-server/dist/tools/discord-channel.d.ts +1 -1
- package/mcp-server/dist/tools/discord-channel.d.ts.map +1 -1
- package/mcp-server/dist/tools/discord-channel.js +1 -1
- package/mcp-server/dist/tools/discord-channel.js.map +1 -1
- package/mcp-server/dist/tools/discord-guild.d.ts +1 -1
- package/mcp-server/dist/tools/discord-guild.d.ts.map +1 -1
- package/mcp-server/dist/tools/discord-guild.js +1 -1
- package/mcp-server/dist/tools/discord-guild.js.map +1 -1
- package/mcp-server/dist/tools/discord-member.d.ts +1 -1
- package/mcp-server/dist/tools/discord-member.d.ts.map +1 -1
- package/mcp-server/dist/tools/discord-member.js +1 -1
- package/mcp-server/dist/tools/discord-member.js.map +1 -1
- package/mcp-server/dist/tools/discord-message.d.ts +1 -1
- package/mcp-server/dist/tools/discord-message.d.ts.map +1 -1
- package/mcp-server/dist/tools/discord-message.js +1 -1
- package/mcp-server/dist/tools/discord-message.js.map +1 -1
- package/mcp-server/dist/tools/discord-onboarding.d.ts +1 -1
- package/mcp-server/dist/tools/discord-onboarding.d.ts.map +1 -1
- package/mcp-server/dist/tools/discord-onboarding.js +1 -1
- package/mcp-server/dist/tools/discord-onboarding.js.map +1 -1
- package/mcp-server/dist/tools/discord-role.d.ts +1 -1
- package/mcp-server/dist/tools/discord-role.d.ts.map +1 -1
- package/mcp-server/dist/tools/discord-role.js +1 -1
- package/mcp-server/dist/tools/discord-role.js.map +1 -1
- package/mcp-server/dist/tools/discord-thread.d.ts +1 -1
- package/mcp-server/dist/tools/discord-thread.d.ts.map +1 -1
- package/mcp-server/dist/tools/discord-thread.js +1 -1
- package/mcp-server/dist/tools/discord-thread.js.map +1 -1
- package/mcp-server/dist/tools/discord-webhook.d.ts +1 -1
- package/mcp-server/dist/tools/discord-webhook.d.ts.map +1 -1
- package/mcp-server/dist/tools/discord-webhook.js +1 -1
- package/mcp-server/dist/tools/discord-webhook.js.map +1 -1
- package/mcp-server/dist/tools/fb-groups.d.ts +1 -1
- package/mcp-server/dist/tools/fb-groups.d.ts.map +1 -1
- package/mcp-server/dist/tools/fb-groups.js +1 -1
- package/mcp-server/dist/tools/fb-groups.js.map +1 -1
- package/mcp-server/dist/tools/forget.d.ts +1 -1
- package/mcp-server/dist/tools/forget.d.ts.map +1 -1
- package/mcp-server/dist/tools/forget.js +1 -1
- package/mcp-server/dist/tools/forget.js.map +1 -1
- package/mcp-server/dist/tools/git-tools.d.ts +1 -1
- package/mcp-server/dist/tools/git-tools.d.ts.map +1 -1
- package/mcp-server/dist/tools/git-tools.js +1 -1
- package/mcp-server/dist/tools/git-tools.js.map +1 -1
- package/mcp-server/dist/tools/gsc/experience.d.ts +1 -1
- package/mcp-server/dist/tools/gsc/experience.d.ts.map +1 -1
- package/mcp-server/dist/tools/gsc/experience.js +1 -1
- package/mcp-server/dist/tools/gsc/experience.js.map +1 -1
- package/mcp-server/dist/tools/gsc/indexing.d.ts +1 -1
- package/mcp-server/dist/tools/gsc/indexing.d.ts.map +1 -1
- package/mcp-server/dist/tools/gsc/indexing.js +1 -1
- package/mcp-server/dist/tools/gsc/indexing.js.map +1 -1
- package/mcp-server/dist/tools/gsc/inspect.d.ts +1 -1
- package/mcp-server/dist/tools/gsc/inspect.d.ts.map +1 -1
- package/mcp-server/dist/tools/gsc/inspect.js +1 -1
- package/mcp-server/dist/tools/gsc/inspect.js.map +1 -1
- package/mcp-server/dist/tools/gsc/navigate.d.ts +1 -1
- package/mcp-server/dist/tools/gsc/navigate.d.ts.map +1 -1
- package/mcp-server/dist/tools/gsc/navigate.js +1 -1
- package/mcp-server/dist/tools/gsc/navigate.js.map +1 -1
- package/mcp-server/dist/tools/gsc/performance.d.ts +1 -1
- package/mcp-server/dist/tools/gsc/performance.d.ts.map +1 -1
- package/mcp-server/dist/tools/gsc/performance.js +1 -1
- package/mcp-server/dist/tools/gsc/performance.js.map +1 -1
- package/mcp-server/dist/tools/gsc/settings.d.ts +1 -1
- package/mcp-server/dist/tools/gsc/settings.d.ts.map +1 -1
- package/mcp-server/dist/tools/gsc/settings.js +1 -1
- package/mcp-server/dist/tools/gsc/settings.js.map +1 -1
- package/mcp-server/dist/tools/image-tools.d.ts +1 -1
- package/mcp-server/dist/tools/image-tools.d.ts.map +1 -1
- package/mcp-server/dist/tools/image-tools.js +1 -1
- package/mcp-server/dist/tools/image-tools.js.map +1 -1
- package/mcp-server/dist/tools/leonardo-browser-tools.d.ts +1 -1
- package/mcp-server/dist/tools/leonardo-browser-tools.d.ts.map +1 -1
- package/mcp-server/dist/tools/leonardo-browser-tools.js +1 -1
- package/mcp-server/dist/tools/leonardo-browser-tools.js.map +1 -1
- package/mcp-server/dist/tools/loop.d.ts +1 -1
- package/mcp-server/dist/tools/loop.d.ts.map +1 -1
- package/mcp-server/dist/tools/loop.js +1 -1
- package/mcp-server/dist/tools/loop.js.map +1 -1
- package/mcp-server/dist/tools/memories.d.ts +1 -1
- package/mcp-server/dist/tools/memories.d.ts.map +1 -1
- package/mcp-server/dist/tools/memories.js +1 -1
- package/mcp-server/dist/tools/memories.js.map +1 -1
- package/mcp-server/dist/tools/morelogin.d.ts +1 -1
- package/mcp-server/dist/tools/morelogin.d.ts.map +1 -1
- package/mcp-server/dist/tools/morelogin.js +1 -1
- package/mcp-server/dist/tools/morelogin.js.map +1 -1
- package/mcp-server/dist/tools/profile.d.ts +1 -1
- package/mcp-server/dist/tools/profile.d.ts.map +1 -1
- package/mcp-server/dist/tools/profile.js +1 -1
- package/mcp-server/dist/tools/profile.js.map +1 -1
- package/mcp-server/dist/tools/recall.d.ts +1 -1
- package/mcp-server/dist/tools/recall.d.ts.map +1 -1
- package/mcp-server/dist/tools/recall.js +1 -1
- package/mcp-server/dist/tools/recall.js.map +1 -1
- package/mcp-server/dist/tools/reflect.d.ts +1 -1
- package/mcp-server/dist/tools/reflect.d.ts.map +1 -1
- package/mcp-server/dist/tools/reflect.js +1 -1
- package/mcp-server/dist/tools/reflect.js.map +1 -1
- package/mcp-server/dist/tools/remember.d.ts +1 -1
- package/mcp-server/dist/tools/remember.d.ts.map +1 -1
- package/mcp-server/dist/tools/remember.js +13 -11
- package/mcp-server/dist/tools/remember.js.map +1 -1
- package/mcp-server/dist/tools/restore.d.ts +1 -1
- package/mcp-server/dist/tools/restore.d.ts.map +1 -1
- package/mcp-server/dist/tools/restore.js +1 -1
- package/mcp-server/dist/tools/restore.js.map +1 -1
- package/mcp-server/dist/tools/style-guide-tools.d.ts +1 -1
- package/mcp-server/dist/tools/style-guide-tools.d.ts.map +1 -1
- package/mcp-server/dist/tools/style-guide-tools.js +1 -1
- package/mcp-server/dist/tools/style-guide-tools.js.map +1 -1
- package/mcp-server/dist/tools/sync.d.ts +1 -1
- package/mcp-server/dist/tools/sync.d.ts.map +1 -1
- package/mcp-server/dist/tools/sync.js +1 -1
- package/mcp-server/dist/tools/sync.js.map +1 -1
- package/mcp-server/dist/tools/tictactoe-tools.d.ts +1 -1
- package/mcp-server/dist/tools/tictactoe-tools.d.ts.map +1 -1
- package/mcp-server/dist/tools/tictactoe-tools.js +1 -1
- package/mcp-server/dist/tools/tictactoe-tools.js.map +1 -1
- package/mcp-server/dist/tools/utils.d.ts +1 -1
- package/mcp-server/dist/tools/utils.d.ts.map +1 -1
- package/mcp-server/dist/tools/utils.js +1 -1
- package/mcp-server/dist/tools/utils.js.map +1 -1
- package/mcp-server/dist/tools/whatsapp-send-file.d.ts +1 -1
- package/mcp-server/dist/tools/whatsapp-send-file.d.ts.map +1 -1
- package/mcp-server/dist/tools/whatsapp-send-file.js +1 -1
- package/mcp-server/dist/tools/whatsapp-send-file.js.map +1 -1
- package/mcp-server/dist/tools/whiteboard-tools.d.ts +3 -3
- package/mcp-server/dist/tools/whiteboard-tools.d.ts.map +1 -1
- package/mcp-server/dist/tools/whiteboard-tools.js +1 -1
- package/mcp-server/dist/tools/whiteboard-tools.js.map +1 -1
- package/mcp-server/dist/tools/youtube/analytics.d.ts +1 -1
- package/mcp-server/dist/tools/youtube/analytics.d.ts.map +1 -1
- package/mcp-server/dist/tools/youtube/analytics.js +1 -1
- package/mcp-server/dist/tools/youtube/analytics.js.map +1 -1
- package/mcp-server/dist/tools/youtube/download.d.ts +1 -1
- package/mcp-server/dist/tools/youtube/download.d.ts.map +1 -1
- package/mcp-server/dist/tools/youtube/download.js +1 -1
- package/mcp-server/dist/tools/youtube/download.js.map +1 -1
- package/mcp-server/dist/tools/youtube/media.d.ts +1 -1
- package/mcp-server/dist/tools/youtube/media.d.ts.map +1 -1
- package/mcp-server/dist/tools/youtube/media.js +1 -1
- package/mcp-server/dist/tools/youtube/media.js.map +1 -1
- package/mcp-server/dist/tools/youtube/metadata.d.ts +1 -1
- package/mcp-server/dist/tools/youtube/metadata.d.ts.map +1 -1
- package/mcp-server/dist/tools/youtube/metadata.js +1 -1
- package/mcp-server/dist/tools/youtube/metadata.js.map +1 -1
- package/mcp-server/dist/tools/youtube/navigate.d.ts +1 -1
- package/mcp-server/dist/tools/youtube/navigate.d.ts.map +1 -1
- package/mcp-server/dist/tools/youtube/navigate.js +1 -1
- package/mcp-server/dist/tools/youtube/navigate.js.map +1 -1
- package/mcp-server/dist/tools/youtube/playlist.d.ts +1 -1
- package/mcp-server/dist/tools/youtube/playlist.d.ts.map +1 -1
- package/mcp-server/dist/tools/youtube/playlist.js +1 -1
- package/mcp-server/dist/tools/youtube/playlist.js.map +1 -1
- package/mcp-server/dist/tools/youtube/schedule.d.ts +1 -1
- package/mcp-server/dist/tools/youtube/schedule.d.ts.map +1 -1
- package/mcp-server/dist/tools/youtube/schedule.js +1 -1
- package/mcp-server/dist/tools/youtube/schedule.js.map +1 -1
- package/mcp-server/dist/tools/youtube/source.d.ts +1 -1
- package/mcp-server/dist/tools/youtube/source.d.ts.map +1 -1
- package/mcp-server/dist/tools/youtube/source.js +1 -1
- package/mcp-server/dist/tools/youtube/source.js.map +1 -1
- package/mcp-server/dist/tools/youtube/upload.d.ts +1 -1
- package/mcp-server/dist/tools/youtube/upload.d.ts.map +1 -1
- package/mcp-server/dist/tools/youtube/upload.js +4 -9
- package/mcp-server/dist/tools/youtube/upload.js.map +1 -1
- package/mcp-server/dist/tui.js +16 -4
- package/mcp-server/dist/tui.js.map +1 -1
- package/mcp-server/package-lock.json +3 -3
- package/mcp-server/package.json +3 -3
- package/mcp-server/src/services/browser-bench.ts +1 -1
- package/mcp-server/src/services/categories.ts +77 -32
- package/mcp-server/src/services/desktop-bench.ts +1 -1
- package/mcp-server/src/tools/agents.ts +1 -1
- package/mcp-server/src/tools/bluesky.ts +1 -1
- package/mcp-server/src/tools/browser-advanced.ts +1 -1
- package/mcp-server/src/tools/browser-batch.ts +1 -1
- package/mcp-server/src/tools/browser-cheatsheet.ts +1 -1
- package/mcp-server/src/tools/browser-interact.ts +1 -1
- package/mcp-server/src/tools/browser-navigate.ts +1 -1
- package/mcp-server/src/tools/browser-observe.ts +1 -1
- package/mcp-server/src/tools/card-tools.ts +1 -1
- package/mcp-server/src/tools/category.ts +6 -1
- package/mcp-server/src/tools/choice.ts +1 -1
- package/mcp-server/src/tools/computer.ts +1 -1
- package/mcp-server/src/tools/discord-channel.ts +1 -1
- package/mcp-server/src/tools/discord-guild.ts +1 -1
- package/mcp-server/src/tools/discord-member.ts +1 -1
- package/mcp-server/src/tools/discord-message.ts +1 -1
- package/mcp-server/src/tools/discord-onboarding.ts +1 -1
- package/mcp-server/src/tools/discord-role.ts +1 -1
- package/mcp-server/src/tools/discord-thread.ts +1 -1
- package/mcp-server/src/tools/discord-webhook.ts +1 -1
- package/mcp-server/src/tools/fb-groups.ts +1 -1
- package/mcp-server/src/tools/forget.ts +1 -1
- package/mcp-server/src/tools/git-tools.ts +1 -1
- package/mcp-server/src/tools/gsc/experience.ts +1 -1
- package/mcp-server/src/tools/gsc/indexing.ts +1 -1
- package/mcp-server/src/tools/gsc/inspect.ts +1 -1
- package/mcp-server/src/tools/gsc/navigate.ts +1 -1
- package/mcp-server/src/tools/gsc/performance.ts +1 -1
- package/mcp-server/src/tools/gsc/settings.ts +1 -1
- package/mcp-server/src/tools/image-tools.ts +1 -1
- package/mcp-server/src/tools/leonardo-browser-tools.ts +1 -1
- package/mcp-server/src/tools/loop.ts +1 -1
- package/mcp-server/src/tools/memories.ts +1 -1
- package/mcp-server/src/tools/morelogin.ts +1 -1
- package/mcp-server/src/tools/profile.ts +1 -1
- package/mcp-server/src/tools/recall.ts +1 -1
- package/mcp-server/src/tools/reflect.ts +1 -1
- package/mcp-server/src/tools/remember.ts +13 -12
- package/mcp-server/src/tools/restore.ts +1 -1
- package/mcp-server/src/tools/style-guide-tools.ts +1 -1
- package/mcp-server/src/tools/sync.ts +1 -1
- package/mcp-server/src/tools/tictactoe-tools.ts +1 -1
- package/mcp-server/src/tools/utils.ts +1 -1
- package/mcp-server/src/tools/whatsapp-send-file.ts +1 -1
- package/mcp-server/src/tools/whiteboard-tools.ts +1 -1
- package/mcp-server/src/tools/youtube/analytics.ts +1 -1
- package/mcp-server/src/tools/youtube/download.ts +1 -1
- package/mcp-server/src/tools/youtube/media.ts +1 -1
- package/mcp-server/src/tools/youtube/metadata.ts +1 -1
- package/mcp-server/src/tools/youtube/navigate.ts +1 -1
- package/mcp-server/src/tools/youtube/playlist.ts +1 -1
- package/mcp-server/src/tools/youtube/schedule.ts +1 -1
- package/mcp-server/src/tools/youtube/source.ts +1 -1
- package/mcp-server/src/tools/youtube/upload.ts +4 -12
- package/mcp-server/src/tui.ts +22 -9
- package/neural-interface/i18n/en.json +236 -7
- package/neural-interface/i18n/pt-BR.json +238 -9
- package/neural-interface/lib/account-usage.js +5 -3
- package/neural-interface/lib/agent-sandbox-elevation.js +102 -0
- package/neural-interface/lib/agent-sandbox-setting.js +57 -0
- package/neural-interface/lib/app-controls-api.js +161 -0
- package/neural-interface/lib/assistant-api.js +40 -7
- package/neural-interface/lib/assistant-brains/opencode.js +13 -1
- package/neural-interface/lib/assistant-brains/opencode2-browser-policy/index.js +44 -0
- package/neural-interface/lib/assistant-brains/opencode2-route-gate/index.js +57 -0
- package/neural-interface/lib/assistant-catalog.js +17 -2
- package/neural-interface/lib/assistant-dispatch.js +3 -0
- package/neural-interface/lib/assistant-envelope.js +19 -1
- package/neural-interface/lib/assistant-route-gate-codex.js +3 -1
- package/neural-interface/lib/assistant-runtime.js +131 -8
- package/neural-interface/lib/claude-model-catalog.js +64 -14
- package/neural-interface/lib/codex-account-auth.js +4 -2
- package/neural-interface/lib/codex-accounts.js +14 -1
- package/neural-interface/lib/codex-settings-schema.js +111 -47
- package/neural-interface/lib/editor-completion-opencode.js +132 -35
- package/neural-interface/lib/exec-loop-command.js +11 -2
- package/neural-interface/lib/install-details.js +23 -5
- package/neural-interface/lib/launch-cache.js +293 -0
- package/neural-interface/lib/login-start.js +524 -0
- package/neural-interface/lib/native-loop-providers.js +24 -4
- package/neural-interface/lib/native-loop-runtime.js +19 -0
- package/neural-interface/lib/opencode-db-schema.js +75 -0
- package/neural-interface/lib/opencode-history.js +16 -1
- package/neural-interface/lib/opencode-protocol.js +173 -0
- package/neural-interface/lib/opencode-provider-view.js +81 -0
- package/neural-interface/lib/opencode-runtime-config.js +10 -3
- package/neural-interface/lib/opencode-serve-records.js +606 -0
- package/neural-interface/lib/opencode-v2-client.js +33 -6
- package/neural-interface/lib/opencode-v2-ws-requests.js +19 -5
- package/neural-interface/lib/opencode-worker-activity.js +229 -0
- package/neural-interface/lib/opencode2-client.js +760 -0
- package/neural-interface/lib/opencode2-proxy.js +116 -0
- package/neural-interface/lib/opencode2-translate.js +1008 -0
- package/neural-interface/lib/process-identity.js +361 -0
- package/neural-interface/lib/provider-panel-terminal-auth.js +3 -2
- package/neural-interface/lib/session-cache-builder.js +26 -5
- package/neural-interface/lib/session-indexer.js +3 -1
- package/neural-interface/lib/session-title-generator.js +20 -0
- package/neural-interface/lib/style-guide/api.js +4 -3
- package/neural-interface/lib/untrusted-file-headers.js +27 -0
- package/neural-interface/package-lock.json +1342 -44
- package/neural-interface/package.json +5 -1
- package/neural-interface/public/index.html +2 -2
- package/neural-interface/public/shared/account-menu.js +4 -1
- package/neural-interface/public/shared/account-usage.js +62 -2
- package/neural-interface/public/shared/account-view.js +2 -1
- package/neural-interface/public/shared/agent-runtime-options.js +55 -11
- package/neural-interface/public/shared/assistant/asst-account-menu.js +298 -0
- package/neural-interface/public/shared/assistant/asst-agents-dock.js +172 -17
- package/neural-interface/public/shared/assistant/asst-api.js +18 -3
- package/neural-interface/public/shared/assistant/asst-brain-picker.js +424 -152
- package/neural-interface/public/shared/assistant/asst-composer.js +1 -1
- package/neural-interface/public/shared/assistant/asst-hosts.js +49 -2
- package/neural-interface/public/shared/assistant/asst-menu.js +275 -77
- package/neural-interface/public/shared/assistant/asst-model-chip.js +135 -0
- package/neural-interface/public/shared/assistant/asst-panel.js +236 -35
- package/neural-interface/public/shared/assistant/asst-rack-styles.js +28 -10
- package/neural-interface/public/shared/assistant/asst-redact.js +186 -0
- package/neural-interface/public/shared/assistant/asst-render.js +382 -36
- package/neural-interface/public/shared/assistant/asst-run-stage.js +231 -0
- package/neural-interface/public/shared/assistant/asst-sanitize.js +193 -3
- package/neural-interface/public/shared/assistant/asst-scene-styles.js +273 -0
- package/neural-interface/public/shared/assistant/asst-scenes.js +528 -0
- package/neural-interface/public/shared/assistant/asst-sidepanel-styles.js +3 -15
- package/neural-interface/public/shared/assistant/asst-state.js +14 -0
- package/neural-interface/public/shared/assistant/asst-styles.js +109 -17
- package/neural-interface/public/shared/assistant/asst-tool-kinds.js +13 -1
- package/neural-interface/public/shared/cc-dropdown.js +480 -0
- package/neural-interface/public/shared/cdx/cdx-panel.js +14 -2
- package/neural-interface/public/shared/cdx/cdx-render.js +15 -3
- package/neural-interface/public/shared/cdx/cdx-styles.js +49 -5
- package/neural-interface/public/shared/cdx/cdx-tabs.js +152 -35
- package/neural-interface/public/shared/cp/cp-markdown.js +4 -57
- package/neural-interface/public/shared/data-cache.js +223 -0
- package/neural-interface/public/shared/desktop-bridge.js +11 -0
- package/neural-interface/public/shared/desktop-titlebar-view.js +78 -1
- package/neural-interface/public/shared/desktop-titlebar.js +214 -28
- package/neural-interface/public/shared/desktop-update-view.js +15 -5
- package/neural-interface/public/shared/file-links.js +73 -8
- package/neural-interface/public/shared/html-shell.js +74 -80
- package/neural-interface/public/shared/launch-prefetch.js +59 -0
- package/neural-interface/public/shared/ocp-v2/ocp-v2-caps.js +3 -2
- package/neural-interface/public/shared/ocp-v2/ocp-v2-childpanel.js +11 -3
- package/neural-interface/public/shared/ocp-v2/ocp-v2-modelpicker.js +54 -17
- package/neural-interface/public/shared/ocp-v2/ocp-v2-panel.js +97 -3
- package/neural-interface/public/shared/ocp-v2/ocp-v2-projectbar.js +3 -2
- package/neural-interface/public/shared/ocp-v2/ocp-v2-providers.js +11 -9
- package/neural-interface/public/shared/ocp-v2/ocp-v2-rehydrate.js +15 -4
- package/neural-interface/public/shared/ocp-v2/ocp-v2-render.js +1 -1
- package/neural-interface/public/shared/ocp-v2/ocp-v2-send-logic.js +401 -0
- package/neural-interface/public/shared/ocp-v2/ocp-v2-send.js +16 -9
- package/neural-interface/public/shared/ocp-v2/ocp-v2-sessions-logic.js +60 -0
- package/neural-interface/public/shared/ocp-v2/ocp-v2-styles.js +83 -5
- package/neural-interface/public/shared/ocp-v2/ocp-v2-ws.js +195 -19
- package/neural-interface/public/shared/opencode-providers.js +39 -0
- package/neural-interface/public/shared/schedules-view.js +318 -0
- package/neural-interface/public/shared/settings/settings-app-pane.js +335 -0
- package/neural-interface/public/shared/settings/settings-app.js +243 -0
- package/neural-interface/public/shared/settings/settings-data.js +88 -0
- package/neural-interface/public/shared/settings/settings-ia.js +17 -0
- package/neural-interface/public/shared/showcase-privacy.js +2 -1
- package/neural-interface/public/shared/sidepanel-session-status.js +49 -0
- package/neural-interface/public/shared/styles.css +543 -372
- package/neural-interface/public/shared/ui-assistant.js +27 -5
- package/neural-interface/public/shared/ui-automation-studio.js +67 -27
- package/neural-interface/public/shared/ui-bookmarks.js +2 -1
- package/neural-interface/public/shared/ui-claude-panel.js +126 -12
- package/neural-interface/public/shared/ui-codex-panel.js +1 -0
- package/neural-interface/public/shared/ui-detail.js +6 -6
- package/neural-interface/public/shared/ui-explorer.js +2 -0
- package/neural-interface/public/shared/ui-file-links.js +1 -0
- package/neural-interface/public/shared/ui-image-gallery.js +6 -5
- package/neural-interface/public/shared/ui-invite.js +18 -2
- package/neural-interface/public/shared/ui-link.js +9 -9
- package/neural-interface/public/shared/ui-menubar.js +232 -33
- package/neural-interface/public/shared/ui-native-loop-router.js +2 -0
- package/neural-interface/public/shared/ui-opencode-panel-v2.js +1 -0
- package/neural-interface/public/shared/ui-schedules-studio.js +2099 -869
- package/neural-interface/public/shared/ui-sessions.js +15 -4
- package/neural-interface/public/shared/ui-settings.js +87 -36
- package/neural-interface/public/shared/ui-sidebar.js +4 -4
- package/neural-interface/public/shared/ui-sidepanel-session-entry.js +13 -0
- package/neural-interface/public/shared/ui-sidepanel-sessions.js +55 -4
- package/neural-interface/public/shared/ui-sidepanel-tray.js +84 -8
- package/neural-interface/public/shared/ui-skills.js +70 -12
- package/neural-interface/public/shared/ui-sync.js +9 -0
- package/neural-interface/public/shared/ui-terminal.js +36 -20
- package/neural-interface/public/shared/ui-trash.js +8 -1
- package/neural-interface/public/shared/ui-tutorial-steps.js +7 -3
- package/neural-interface/public/shared/ui-tutorial.js +15 -5
- package/neural-interface/public/shared/ui-update.js +87 -16
- package/neural-interface/public/shared/ui-whiteboard.js +2 -1
- package/neural-interface/public/variant/2d/main.js +4 -1
- package/neural-interface/public/variant/3d/main.js +3 -0
- package/neural-interface/public/vendor/highlight/LICENSE +29 -0
- package/neural-interface/public/vendor/highlight/github-dark-dimmed-11.12.0.min.css +9 -0
- package/neural-interface/public/vendor/highlight/highlight-11.12.0.min.js +1262 -0
- package/neural-interface/public/vendor/highlight/manifest.json +5 -0
- package/neural-interface/public/vendor/html2canvas/LICENSE +22 -0
- package/neural-interface/public/vendor/html2canvas/html2canvas-1.4.1.min.js +20 -0
- package/neural-interface/public/vendor/html2canvas/manifest.json +4 -0
- package/neural-interface/public/vendor/marked/LICENSE.md +44 -0
- package/neural-interface/public/vendor/marked/manifest.json +4 -0
- package/neural-interface/public/vendor/marked/marked-14.1.4.esm.js +2502 -0
- package/neural-interface/public/vendor/mermaid/LICENSE +21 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/abnfDiagram-PAR5IXJI.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/architecture-7GRP2DOG-M7G3SWCO.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/architectureDiagram-2PNWDENW.mjs +36 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/blockDiagram-PUWGUBOA.mjs +129 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/c4Diagram-3NLNHZP5.mjs +38 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-2NMSUPJH.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-32MCUW2P.mjs +206 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-3NPNU5GP.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-3SMGFRGC.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-4CAJECRU.mjs +14 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-4OADUZ5Q.mjs +10 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-5IMINLNL.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-5IQCK5RY.mjs +3 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-5MVFYWFU.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-AQ6EADP3.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-AZZRMDJM.mjs +15 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-CLS4B6BI.mjs +168 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-DZP67EKU.mjs +70 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-EICP7K5X.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-GDY2UAB4.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-H7VHZCWX.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-IJBDOHL6.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-JFFVMJIA.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-JQRUD6KW.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-JZGDI5JK.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-KD6SPIPN.mjs +2 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-NF366YI6.mjs +231 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-NUDPKBEF.mjs +32 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-OJINMHTS.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-Q6NGPIAH.mjs +2 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-QJSWEUOL.mjs +132 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-R7JDMUKV.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-RCXAM2GK.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-RT4MAWR3.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-SFMINX4W.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-SLDQ463B.mjs +88 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-STOV2HOB.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-T5OCTHI4.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-TCEHX7R3.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-VQC2EELJ.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-VRZKL3EH.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-VTHDQ7BV.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-VY2OSTJR.mjs +2 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-WUBWJARI.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-YBMQ5JGJ.mjs +2 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-YDMLL4PJ.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-Z6V44ARX.mjs +62 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-ZJJ7VQJP.mjs +339 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-ZNLK7HRR.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-ZQN7UHIL.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-ZS6VBONO.mjs +174 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-ZUNWM646.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/classDiagram-FGAMPOII.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/classDiagram-v2-AI5RJEK4.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/cose-bilkent-Q72C2OJ3.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/cynefin-OW5HDTMX-6OCTWOGG.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/cynefinDiagram-2OOWUY4M.mjs +62 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/dagre-MPVFI544.mjs +4 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/diagram-AE4NWXFW.mjs +24 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/diagram-BUJID5UG.mjs +41 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/diagram-C44CZ5NI.mjs +24 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/diagram-EYOLEXXS.mjs +30 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/diagram-P7B6PWOJ.mjs +3 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/ebnfDiagram-FTPZ3PV6.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/erDiagram-H3ERR26V.mjs +99 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/eventmodeling-NTZA5JFV-WKSWQHFS.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/flowDiagram-YHGXBVSY.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/ganttDiagram-RWFTB7ED.mjs +292 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/gitGraph-4MIJSDKK-JH2HKYUO.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/gitGraphDiagram-MAR6GKOP.mjs +106 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/info-A6RAGUB7-3TX3OFWW.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/infoDiagram-LV547AFH.mjs +2 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/ishikawaDiagram-PUJQGNDS.mjs +70 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/journeyDiagram-EW5QJT4W.mjs +139 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/kanban-definition-JSQERAZB.mjs +89 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/katex-CZ4GXH2S.mjs +257 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/mindmap-definition-VBJCLQLM.mjs +96 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/packet-AYTQ26CC-MKII6P3Y.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/pegDiagram-7WILOQTJ.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/pie-WAS4IAKB-6B4OPMJ5.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/pieDiagram-EFA7CBSB.mjs +39 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/quadrantDiagram-WARVYA7P.mjs +7 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/radar-RG4KPBEZ-UPTR3XUL.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/railroad-74A4TZTK-MEFUPFMS.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/railroad-abnf-HS5TGJTU-R5YG2S5D.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/railroad-ebnf-LZEXJU2U-G5QEX2L6.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/railroad-peg-WCYAUIDC-H5I2ZRY2.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/railroadDiagram-5NIQVNLQ.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/requirementDiagram-SXREGOKZ.mjs +84 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/sankeyDiagram-VV74M2VP.mjs +40 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/sequenceDiagram-52ZFIFDL.mjs +162 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/sizeCapture-FGA5AGYP.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/stateDiagram-3QAKPIVI.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/stateDiagram-v2-HN2P4RDZ.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/swimlanes-QLVP42QD.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/swimlanesDiagram-JKPDBFCN.mjs +8 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/timeline-definition-GS45XAXW.mjs +120 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/treeView-Q6P3EWNA-ZRJGANNQ.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/treemap-WGGIJYW6-K64VN5AS.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/vennDiagram-QIQU2ZRJ.mjs +34 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/wardley-WFR3VGLG-D67OWCCH.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/wardleyDiagram-U6YNM66N.mjs +78 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/xychartDiagram-XJAZEDNM.mjs +7 -0
- package/neural-interface/public/vendor/mermaid/manifest.json +4 -0
- package/neural-interface/public/vendor/mermaid/mermaid-11.17.2.esm.min.mjs +14 -0
- package/neural-interface/scripts/build-vendor.mjs +84 -3
- package/neural-interface/scripts/rebuild-pty.js +14 -2
- package/neural-interface/server.js +1279 -372
- package/npm-shrinkwrap.json +4424 -0
- package/package.json +54 -2
- package/postinstall.js +61 -47
- package/preuninstall.js +8 -35
- package/setup.js +76 -44
- package/skills/synabun/modules/idea.md +1 -1
- package/skills/synabun/modules/schedule.md +2 -2
- package/updater.cmd +2 -2
- package/updater.mjs +97 -3
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
// ═══════════════════════════════════════════
|
|
2
|
+
// SynaBun Neural Interface — data cache
|
|
3
|
+
// ═══════════════════════════════════════════
|
|
4
|
+
// One cache for what windows, menus and dropdowns wait for when they open
|
|
5
|
+
// (model lists, provider lists, accounts, Settings' reads). In memory, per
|
|
6
|
+
// page: a kept answer is given at once and asked for again in the background
|
|
7
|
+
// once it is old (stale-while-revalidate); callers asking at the same time
|
|
8
|
+
// share one request; nothing is kept from a failed request. A change event
|
|
9
|
+
// ages an answer (markStale: still given, asked for again) or drops it
|
|
10
|
+
// (invalidate: the next read waits, `refetch` asks for it right away), and
|
|
11
|
+
// subscribers hear when a background read brought another answer, so an open
|
|
12
|
+
// window can update in place.
|
|
13
|
+
//
|
|
14
|
+
// `persist: true` also keeps the last answer in sessionStorage (this tab only,
|
|
15
|
+
// gone when it closes): a reload paints from it at once. An answer read back
|
|
16
|
+
// from there is always asked for again on its first read.
|
|
17
|
+
//
|
|
18
|
+
// No DOM, no imports: runs under node --test as well.
|
|
19
|
+
|
|
20
|
+
const STORAGE_PREFIX = 'synabun:data-cache:v1:';
|
|
21
|
+
const MAX_PERSIST_CHARS = 768 * 1024;
|
|
22
|
+
|
|
23
|
+
const registry = new Map(); // name → cache (invalidateDataCaches)
|
|
24
|
+
|
|
25
|
+
function sameJson(a, b) {
|
|
26
|
+
if (a === b) return true;
|
|
27
|
+
try { return JSON.stringify(a) === JSON.stringify(b); } catch { return false; }
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function session() {
|
|
31
|
+
try { return typeof sessionStorage !== 'undefined' ? sessionStorage : null; } catch { return null; }
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* @param {string} name
|
|
36
|
+
* @param {object} o
|
|
37
|
+
* @param {(key:string, opts:{force:boolean})=>Promise<any>} o.load the request; throw on failure
|
|
38
|
+
* @param {number} [o.ttlMs] how long an answer is fresh
|
|
39
|
+
* @param {boolean} [o.persist] keep the last answer in sessionStorage too
|
|
40
|
+
* @param {(value:any, key:string)=>boolean} [o.accept] worth keeping (a failed or empty answer is not)
|
|
41
|
+
* @param {(a:any, b:any)=>boolean} [o.equals]
|
|
42
|
+
*/
|
|
43
|
+
export function createDataCache(name, {
|
|
44
|
+
load,
|
|
45
|
+
ttlMs = 60_000,
|
|
46
|
+
persist = false,
|
|
47
|
+
accept = (value) => value !== undefined && value !== null,
|
|
48
|
+
equals = sameJson,
|
|
49
|
+
now = () => Date.now(),
|
|
50
|
+
} = {}) {
|
|
51
|
+
if (typeof load !== 'function') throw new Error(`data cache ${name}: load is required`);
|
|
52
|
+
const entries = new Map(); // key → { value, at, stale }
|
|
53
|
+
const inflight = new Map(); // key → promise
|
|
54
|
+
const generations = new Map(); // key → bumped by invalidate(): a request begun before it is not kept
|
|
55
|
+
const listeners = new Set();
|
|
56
|
+
const restored = new Set(); // keys looked up in sessionStorage already
|
|
57
|
+
|
|
58
|
+
const storageKey = (key) => `${STORAGE_PREFIX}${name}:${key}`;
|
|
59
|
+
const ok = (value, key) => { try { return accept(value, key) === true; } catch { return false; } };
|
|
60
|
+
|
|
61
|
+
function restore(key) {
|
|
62
|
+
if (!persist || restored.has(key)) return;
|
|
63
|
+
restored.add(key);
|
|
64
|
+
if (entries.has(key)) return;
|
|
65
|
+
try {
|
|
66
|
+
const raw = session()?.getItem(storageKey(key));
|
|
67
|
+
if (!raw) return;
|
|
68
|
+
const parsed = JSON.parse(raw);
|
|
69
|
+
if (parsed && parsed.value !== undefined && ok(parsed.value, key)) entries.set(key, { value: parsed.value, at: Number(parsed.at) || 0, stale: true });
|
|
70
|
+
} catch { /* unreadable: nothing kept */ }
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function save(key, entry) {
|
|
74
|
+
if (!persist) return;
|
|
75
|
+
try {
|
|
76
|
+
const text = JSON.stringify({ at: entry.at, value: entry.value });
|
|
77
|
+
if (text.length > MAX_PERSIST_CHARS) { session()?.removeItem(storageKey(key)); return; }
|
|
78
|
+
session()?.setItem(storageKey(key), text);
|
|
79
|
+
} catch { /* full or unavailable: memory still has it */ }
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function forget(key) {
|
|
83
|
+
if (!persist) return;
|
|
84
|
+
try { session()?.removeItem(storageKey(key)); } catch { /* unavailable */ }
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function notify(key, value, previous) {
|
|
88
|
+
for (const listener of [...listeners]) {
|
|
89
|
+
try { listener(key, value, previous); } catch { /* a listener's failure is its own */ }
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function store(key, value) {
|
|
94
|
+
const previous = entries.get(key);
|
|
95
|
+
const same = !!previous && equals(previous.value, value);
|
|
96
|
+
const entry = { value: same ? previous.value : value, at: now(), stale: false };
|
|
97
|
+
entries.set(key, entry);
|
|
98
|
+
save(key, entry);
|
|
99
|
+
if (!same) notify(key, entry.value, previous?.value);
|
|
100
|
+
return entry.value;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function fresh(entry) {
|
|
104
|
+
return !!entry && !entry.stale && now() - entry.at < ttlMs;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function request(key, force) {
|
|
108
|
+
if (inflight.has(key)) return inflight.get(key);
|
|
109
|
+
const generation = generations.get(key) || 0;
|
|
110
|
+
const job = (async () => {
|
|
111
|
+
try {
|
|
112
|
+
const value = await load(key, { force });
|
|
113
|
+
if (ok(value, key) && (generations.get(key) || 0) === generation) return { value: store(key, value), kept: true };
|
|
114
|
+
return { value, kept: false };
|
|
115
|
+
} finally {
|
|
116
|
+
if (inflight.get(key) === job) inflight.delete(key);
|
|
117
|
+
}
|
|
118
|
+
})();
|
|
119
|
+
inflight.set(key, job);
|
|
120
|
+
return job;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/** `{ value, cached, stale }`: a kept answer at once (asked for again when old), else the request's. */
|
|
124
|
+
async function read(key = '', { force = false } = {}) {
|
|
125
|
+
const id = String(key);
|
|
126
|
+
restore(id);
|
|
127
|
+
const entry = entries.get(id);
|
|
128
|
+
if (!force && entry) {
|
|
129
|
+
const stale = !fresh(entry);
|
|
130
|
+
if (stale) request(id, false).catch(() => {});
|
|
131
|
+
return { value: entry.value, cached: true, stale };
|
|
132
|
+
}
|
|
133
|
+
const result = await request(id, force);
|
|
134
|
+
return { value: result.value, cached: false, stale: false, kept: result.kept };
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
const cache = {
|
|
138
|
+
name,
|
|
139
|
+
read,
|
|
140
|
+
async get(key = '', options = {}) { return (await read(key, options)).value; },
|
|
141
|
+
/** The kept answer, or undefined: never waits. An old one (or one read back after a reload) is asked for again behind it. */
|
|
142
|
+
peek(key = '') {
|
|
143
|
+
const id = String(key);
|
|
144
|
+
restore(id);
|
|
145
|
+
const entry = entries.get(id);
|
|
146
|
+
if (entry && !fresh(entry)) request(id, false).catch(() => {});
|
|
147
|
+
return entry?.value;
|
|
148
|
+
},
|
|
149
|
+
has(key = '') { const id = String(key); restore(id); return entries.has(id); },
|
|
150
|
+
/** True while nothing was kept and a request is running (the one moment a spinner is right). */
|
|
151
|
+
loading(key = '') { const id = String(key); restore(id); return !entries.has(id) && inflight.has(id); },
|
|
152
|
+
/** Ask now unless a fresh answer is kept; resolves when done, never rejects. */
|
|
153
|
+
prefetch(key = '') {
|
|
154
|
+
const id = String(key);
|
|
155
|
+
restore(id);
|
|
156
|
+
if (fresh(entries.get(id))) return Promise.resolve(entries.get(id).value);
|
|
157
|
+
return request(id, false).then((result) => result.value, () => undefined);
|
|
158
|
+
},
|
|
159
|
+
/** Ask again now (one request for every caller); resolves to the answer, never rejects. */
|
|
160
|
+
refresh(key = '', { force = false } = {}) {
|
|
161
|
+
return request(String(key), force).then((result) => result.value, () => undefined);
|
|
162
|
+
},
|
|
163
|
+
/** Keep an answer obtained elsewhere (a save that returned the new state). */
|
|
164
|
+
set(key = '', value) { const id = String(key); if (ok(value, id)) store(id, value); },
|
|
165
|
+
/** The answer is due: still given, asked for again on the next read (`refetch`: now). */
|
|
166
|
+
markStale(key = null, { refetch = false } = {}) {
|
|
167
|
+
const ids = key === null ? [...entries.keys()] : [String(key)];
|
|
168
|
+
for (const id of ids) {
|
|
169
|
+
restore(id);
|
|
170
|
+
const entry = entries.get(id);
|
|
171
|
+
if (entry) entry.stale = true;
|
|
172
|
+
if (refetch) request(id, false).catch(() => {});
|
|
173
|
+
}
|
|
174
|
+
},
|
|
175
|
+
/** Drop the answer: the next read waits for a new one; a request already running is not kept (`refetch`: ask now). */
|
|
176
|
+
invalidate(key = null, { refetch = false } = {}) {
|
|
177
|
+
const ids = key === null ? [...new Set([...entries.keys(), ...inflight.keys()])] : [String(key)];
|
|
178
|
+
for (const id of ids) {
|
|
179
|
+
entries.delete(id);
|
|
180
|
+
restored.add(id);
|
|
181
|
+
forget(id);
|
|
182
|
+
generations.set(id, (generations.get(id) || 0) + 1);
|
|
183
|
+
// A request begun before this is not joined by later reads: its answer is from before the change.
|
|
184
|
+
inflight.delete(id);
|
|
185
|
+
if (refetch) request(id, false).catch(() => {});
|
|
186
|
+
}
|
|
187
|
+
},
|
|
188
|
+
/** `listener(key, value, previous)` whenever another answer is kept. Returns the function that stops it. */
|
|
189
|
+
subscribe(listener) {
|
|
190
|
+
if (typeof listener !== 'function') return () => {};
|
|
191
|
+
listeners.add(listener);
|
|
192
|
+
return () => { listeners.delete(listener); };
|
|
193
|
+
},
|
|
194
|
+
keys() { return [...entries.keys()]; },
|
|
195
|
+
};
|
|
196
|
+
registry.set(name, cache);
|
|
197
|
+
return cache;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/** The cache registered under `name`, or null. */
|
|
201
|
+
export function dataCache(name) {
|
|
202
|
+
return registry.get(name) || null;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/** Drop every answer of the named caches (or of all of them): a sign-out, a server that came back as another. */
|
|
206
|
+
export function invalidateDataCaches(names = null) {
|
|
207
|
+
for (const [name, cache] of registry) {
|
|
208
|
+
if (!names || names.includes(name)) cache.invalidate();
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Run `fn` when the page is idle (requestIdleCallback where there is one; Safari has none, so a short timer
|
|
214
|
+
* there), at most `timeout` ms later. Returns a function that cancels it.
|
|
215
|
+
*/
|
|
216
|
+
export function whenIdle(fn, { timeout = 1500, fallbackMs = 120 } = {}) {
|
|
217
|
+
if (typeof window !== 'undefined' && typeof window.requestIdleCallback === 'function') {
|
|
218
|
+
const id = window.requestIdleCallback(() => fn(), { timeout });
|
|
219
|
+
return () => { try { window.cancelIdleCallback?.(id); } catch { /* gone */ } };
|
|
220
|
+
}
|
|
221
|
+
const id = setTimeout(fn, fallbackMs);
|
|
222
|
+
return () => clearTimeout(id);
|
|
223
|
+
}
|
|
@@ -25,6 +25,17 @@ export function isDesktopApp(win = globalThis.window) {
|
|
|
25
25
|
return desktopBridge(win) !== null;
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
+
/**
|
|
29
|
+
* The desktop app's own settings (Settings > App: packaging/desktop/src/app-prefs.mjs, login-item.mjs): state(),
|
|
30
|
+
* set(key, value), openFolder('data' | 'logs'), openLoginSettings(), relaunch(), onChange(listener).
|
|
31
|
+
* Null in a browser, and in an app from before Settings > App.
|
|
32
|
+
*/
|
|
33
|
+
export function desktopApp(win = globalThis.window) {
|
|
34
|
+
const api = desktopBridge(win)?.app;
|
|
35
|
+
if (!api || typeof api !== 'object' || typeof api.state !== 'function' || typeof api.set !== 'function') return null;
|
|
36
|
+
return api;
|
|
37
|
+
}
|
|
38
|
+
|
|
28
39
|
/**
|
|
29
40
|
* The title bar of the desktop app's main window (packaging/desktop/src/titlebar.mjs): its facts
|
|
30
41
|
* ({ style, platform, height, controls, appMenu }), its state, the app's menu and the caption buttons' colours.
|
|
@@ -8,7 +8,9 @@
|
|
|
8
8
|
// its ends. desktop-titlebar.js does the page's part; this file decides, from
|
|
9
9
|
// plain values: the colours the caption buttons get, whether the bar makes room
|
|
10
10
|
// for them now, where they are when the page has no Window Controls Overlay API,
|
|
11
|
-
//
|
|
11
|
+
// whether something drawn over the bar has to get its clicks, and which changes
|
|
12
|
+
// of the page can put something there (changedClasses, styleChange,
|
|
13
|
+
// drawnUnderBar).
|
|
12
14
|
//
|
|
13
15
|
// No imports and no DOM: `node --test` loads this file as it is
|
|
14
16
|
// (tests/desktop-titlebar-view.test.mjs).
|
|
@@ -120,3 +122,78 @@ export function barCovered(samples) {
|
|
|
120
122
|
|
|
121
123
|
/** A stable key for a pair of colours: unchanged colours are never sent twice. */
|
|
122
124
|
export const colorsKey = (colors) => (colors ? `${colors.color}/${colors.symbolColor}` : '');
|
|
125
|
+
|
|
126
|
+
/** The class names one of two class attributes has and the other has not (none: the same classes, in any order). */
|
|
127
|
+
export function changedClasses(before, after) {
|
|
128
|
+
const was = new Set(String(before ?? '').split(/\s+/).filter(Boolean));
|
|
129
|
+
const is = new Set(String(after ?? '').split(/\s+/).filter(Boolean));
|
|
130
|
+
return [...[...was].filter(name => !is.has(name)), ...[...is].filter(name => !was.has(name))];
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// Inline style properties a tooltip that follows the pointer, a window being dragged or a panel being resized
|
|
134
|
+
// changes on every frame: they move, size or paint a box, and never show or hide one.
|
|
135
|
+
const MOVES_OR_PAINTS = /^(?:--.+|top|left|right|bottom|inset(?:-[a-z-]+)?|(?:min-|max-)?(?:width|height)|margin(?:-[a-z-]+)?|padding(?:-[a-z-]+)?|transform(?:-origin)?|translate|scale|rotate|opacity|color|background(?:-[a-z-]+)?|border(?:-[a-z-]+)?|box-shadow|outline(?:-[a-z-]+)?|filter|(?:-webkit-)?backdrop-filter|transition(?:-[a-z-]+)?|animation(?:-[a-z-]+)?|cursor|font(?:-[a-z-]+)?|line-height|letter-spacing|text-[a-z-]+|white-space)$/;
|
|
136
|
+
|
|
137
|
+
/** An inline style attribute as property → value. */
|
|
138
|
+
function declarations(text) {
|
|
139
|
+
const out = new Map();
|
|
140
|
+
for (const part of String(text ?? '').split(';')) {
|
|
141
|
+
const at = part.indexOf(':');
|
|
142
|
+
const name = at < 0 ? '' : part.slice(0, at).trim().toLowerCase();
|
|
143
|
+
if (name) out.set(name, part.slice(at + 1).trim());
|
|
144
|
+
}
|
|
145
|
+
return out;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* What a change of an inline style attribute (`before` → `after`) did: 'none' (the same declarations), 'move' (only
|
|
150
|
+
* properties that move, size or paint a box: MOVES_OR_PAINTS) or 'other' (display, visibility, pointer-events,
|
|
151
|
+
* z-index, position or anything else, which can show, hide or restack it).
|
|
152
|
+
*/
|
|
153
|
+
export function styleChange(before, after) {
|
|
154
|
+
const was = declarations(before);
|
|
155
|
+
const is = declarations(after);
|
|
156
|
+
let kind = 'none';
|
|
157
|
+
for (const name of new Set([...was.keys(), ...is.keys()])) {
|
|
158
|
+
if (was.get(name) === is.get(name)) continue;
|
|
159
|
+
if (!MOVES_OR_PAINTS.test(name)) return 'other';
|
|
160
|
+
kind = 'move';
|
|
161
|
+
}
|
|
162
|
+
return kind;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/** The z-index a box is stacked at from its computed style, when it is positioned and has one; null otherwise. */
|
|
166
|
+
export function stackLevel(style) {
|
|
167
|
+
const z = Number.parseInt(style?.zIndex, 10);
|
|
168
|
+
return style?.position && style.position !== 'static' && Number.isFinite(z) ? z : null;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
const isSet = (value) => !!value && value !== 'none';
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Whether nothing inside an element can be drawn over the bar, from the element's computed style, for an element
|
|
175
|
+
* that is the bar's sibling (both children of <body>, the bar stacked at `barLevel`): it draws nothing (display:
|
|
176
|
+
* none), or it is a stacking context of its own (a z-index on a positioned box, position fixed or sticky, a
|
|
177
|
+
* transform, a filter, opacity, isolation, a blend mode, paint or layout containment) stacked under the bar. Whatever
|
|
178
|
+
* its descendants' z-index, they are drawn inside it. Only the top layer escapes (a modal dialog, a popover), which
|
|
179
|
+
* the caller watches by itself; and a rule can restyle the element from what it holds (`:has()`), so the answer holds
|
|
180
|
+
* only until something inside it changes. Anything it cannot tell is false.
|
|
181
|
+
*/
|
|
182
|
+
export function drawnUnderBar(style, barLevel) {
|
|
183
|
+
if (!style || !Number.isFinite(barLevel)) return false;
|
|
184
|
+
if (style.display === 'none') return true;
|
|
185
|
+
if (style.display === 'contents') return false;
|
|
186
|
+
const z = Number.parseInt(style.zIndex, 10);
|
|
187
|
+
const positioned = !!style.position && style.position !== 'static';
|
|
188
|
+
const ownContext = (positioned && Number.isFinite(z))
|
|
189
|
+
|| style.position === 'fixed' || style.position === 'sticky'
|
|
190
|
+
|| isSet(style.transform) || isSet(style.filter) || isSet(style.backdropFilter) || isSet(style.webkitBackdropFilter)
|
|
191
|
+
|| Number.parseFloat(style.opacity) < 1
|
|
192
|
+
|| style.isolation === 'isolate'
|
|
193
|
+
|| (!!style.mixBlendMode && style.mixBlendMode !== 'normal')
|
|
194
|
+
|| /\b(?:paint|layout|strict|content)\b/.test(String(style.contain || ''));
|
|
195
|
+
if (!ownContext) return false;
|
|
196
|
+
// A z-index on a box that is not positioned counts only on a flex or grid item: read as at least 0 there.
|
|
197
|
+
const level = Number.isFinite(z) ? (positioned ? z : Math.max(z, 0)) : 0;
|
|
198
|
+
return level < barLevel;
|
|
199
|
+
}
|
|
@@ -17,7 +17,8 @@
|
|
|
17
17
|
// menu, the one the hidden menu bar had (its shortcuts still work)
|
|
18
18
|
// clicks the bar's empty parts drag the window (the system double-clicks
|
|
19
19
|
// them too); while something is drawn over the bar (a dialog, the
|
|
20
|
-
// tour), that thing gets the clicks: body.desktop-titlebar-covered
|
|
20
|
+
// tour), that thing gets the clicks: body.desktop-titlebar-covered,
|
|
21
|
+
// checked only after a change that can put something there
|
|
21
22
|
//
|
|
22
23
|
// In a browser and in the installed PWA nothing here runs: initDesktopTitleBar()
|
|
23
24
|
// returns null, and the PWA's own overlay handling (ui-navbar.js) is untouched.
|
|
@@ -26,11 +27,19 @@
|
|
|
26
27
|
|
|
27
28
|
import { desktopWindow } from './desktop-bridge.js';
|
|
28
29
|
import { colorSender, textTone } from './desktop-titlebar-page.js';
|
|
29
|
-
import { barCovered, fallbackTitlebarArea, overlayColorsFor, titleBarActive } from './desktop-titlebar-view.js';
|
|
30
|
+
import { barCovered, changedClasses, drawnUnderBar, fallbackTitlebarArea, overlayColorsFor, stackLevel, styleChange, titleBarActive } from './desktop-titlebar-view.js';
|
|
30
31
|
import { t } from './i18n.js';
|
|
31
32
|
|
|
32
33
|
const MENU_ICON = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4 7h16"/><path d="M4 12h16"/><path d="M4 17h16"/></svg>';
|
|
33
34
|
const COLOR_DELAY_MS = 120;
|
|
35
|
+
// The cover check while the page keeps changing: at most one per COVER_INTERVAL_MS (four a second).
|
|
36
|
+
const COVER_INTERVAL_MS = 250;
|
|
37
|
+
// The classes of <body> this module writes itself: never a reason to check again.
|
|
38
|
+
const OWN_BODY_CLASSES = new Set(['desktop-titlebar-covered', 'wco-active']);
|
|
39
|
+
// What a change of the page can do to the cover.
|
|
40
|
+
const IGNORE = 0;
|
|
41
|
+
const SOON = 1;
|
|
42
|
+
const NOW = 2;
|
|
34
43
|
|
|
35
44
|
/**
|
|
36
45
|
* The map pages (index.html, index2d.html): #title-bar becomes the window's title bar. Returns the controller, or
|
|
@@ -69,24 +78,146 @@ export function initDesktopTitleBar({ doc = document, view = window } = {}) {
|
|
|
69
78
|
}
|
|
70
79
|
|
|
71
80
|
// ── Covered: something over the bar takes its clicks, so the bar stops dragging the window ──
|
|
81
|
+
// Chromium gives the page no mouse events over a drag region, so this has to be right before the pointer gets
|
|
82
|
+
// there. Measuring it costs a layout and the hit tests, so it runs only after a change that can put something over
|
|
83
|
+
// the bar or take it away (pageChange below): on the next frame (coverNow) for an overlay's root coming, going,
|
|
84
|
+
// shown or hidden, at most every COVER_INTERVAL_MS (coverSoon: at once when the page was quiet, then once more after
|
|
85
|
+
// the last change) for the rest, and never for text or for what the bar holds. A change inside a part of the page
|
|
86
|
+
// drawn under the bar (a side panel's transcript, a terminal's output) only has that part's style read again, in
|
|
87
|
+
// case a rule lifted it over the bar (the side panels' `:has(.…-dropdown.open)`); the bar is measured then only if
|
|
88
|
+
// one was.
|
|
89
|
+
const body = doc.body;
|
|
90
|
+
const clock = () => (typeof view.performance?.now === 'function' ? view.performance.now() : Date.now());
|
|
72
91
|
let coverFrame = 0;
|
|
92
|
+
let coverTimer = null;
|
|
93
|
+
let measureNow = false; // the next check measures the bar
|
|
94
|
+
let soonPending = false; // work at the throttled pace is waiting: verdicts, and the bar's measure when soonMeasure
|
|
95
|
+
let soonMeasure = false;
|
|
96
|
+
let lastSoon = -Infinity; // when that work last ran
|
|
97
|
+
// Children of <body> drawn under the bar as a whole (drawnUnderBar), and those whose verdict the next check takes
|
|
98
|
+
// again: read during a check, when the page's style is current anyway, and never their layout.
|
|
99
|
+
const underBar = new Set();
|
|
100
|
+
const toJudge = new Set();
|
|
101
|
+
const judgeAgain = () => { for (const part of underBar) toJudge.add(part); };
|
|
102
|
+
/** Takes the verdicts asked for; true when a part that was drawn under the bar no longer is. */
|
|
103
|
+
const judgeParts = () => {
|
|
104
|
+
if (!toJudge.size) return false;
|
|
105
|
+
const level = bar.parentNode === body ? stackLevel(view.getComputedStyle(bar)) : null;
|
|
106
|
+
let lifted = false;
|
|
107
|
+
for (const part of toJudge) {
|
|
108
|
+
if (level !== null && part.parentNode === body && drawnUnderBar(view.getComputedStyle(part), level)) underBar.add(part);
|
|
109
|
+
else if (underBar.delete(part)) lifted = true;
|
|
110
|
+
}
|
|
111
|
+
toJudge.clear();
|
|
112
|
+
return lifted;
|
|
113
|
+
};
|
|
73
114
|
const checkCover = () => {
|
|
74
115
|
coverFrame = 0;
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
const
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
116
|
+
let measure = measureNow;
|
|
117
|
+
measureNow = false;
|
|
118
|
+
const now = clock();
|
|
119
|
+
if (soonPending && now - lastSoon >= COVER_INTERVAL_MS) {
|
|
120
|
+
soonPending = false;
|
|
121
|
+
lastSoon = now;
|
|
122
|
+
if (soonMeasure) measure = true;
|
|
123
|
+
soonMeasure = false;
|
|
124
|
+
}
|
|
125
|
+
// Reads in one go (the style, then one layout and the hit tests), then at most one write, and only of a change.
|
|
126
|
+
if (judgeParts()) measure = true;
|
|
127
|
+
if (measure) {
|
|
128
|
+
let covered = false;
|
|
129
|
+
if (body.classList.contains('wco-active')) {
|
|
130
|
+
const rect = bar.getBoundingClientRect();
|
|
131
|
+
const area = wco && wco.visible ? wco.getTitlebarAreaRect() : null;
|
|
132
|
+
const left = Math.max(rect.left, area ? area.x : rect.left) + 2;
|
|
133
|
+
const right = Math.min(rect.right, area ? area.x + area.width : rect.right) - 2;
|
|
134
|
+
if (right > left && rect.height > 0) {
|
|
135
|
+
const y = Math.round(rect.top + rect.height / 2);
|
|
136
|
+
for (let i = 0; i <= 8 && !covered; i++) {
|
|
137
|
+
const el = doc.elementFromPoint(Math.round(left + ((right - left) * i) / 8), y);
|
|
138
|
+
covered = barCovered([{ inBar: !!el && (el === bar || bar.contains(el)), page: !el || el === html || el === body }]);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
if (body.classList.contains('desktop-titlebar-covered') !== covered) body.classList.toggle('desktop-titlebar-covered', covered);
|
|
86
143
|
}
|
|
87
|
-
|
|
144
|
+
if (soonPending) armSoon();
|
|
145
|
+
};
|
|
146
|
+
/** The next check for the throttled work: the next frame once COVER_INTERVAL_MS has passed since it last ran. */
|
|
147
|
+
const armSoon = () => {
|
|
148
|
+
if (coverFrame || coverTimer) return;
|
|
149
|
+
const wait = lastSoon + COVER_INTERVAL_MS - clock();
|
|
150
|
+
if (wait <= 0) coverFrame = view.requestAnimationFrame(checkCover);
|
|
151
|
+
else coverTimer = setTimeout(() => { coverTimer = null; if (!coverFrame) coverFrame = view.requestAnimationFrame(checkCover); }, wait);
|
|
152
|
+
};
|
|
153
|
+
/** A check on the next frame; `measured` false: only the verdicts asked for, unless one changes. */
|
|
154
|
+
const coverNow = (measured = true) => {
|
|
155
|
+
if (measured) measureNow = true;
|
|
156
|
+
if (!coverFrame) coverFrame = view.requestAnimationFrame(checkCover);
|
|
157
|
+
};
|
|
158
|
+
/** A check at the throttled pace: at once after a quiet COVER_INTERVAL_MS, else when it is over (leading, trailing). */
|
|
159
|
+
const coverSoon = (measured = true) => {
|
|
160
|
+
soonPending = true;
|
|
161
|
+
if (measured) soonMeasure = true;
|
|
162
|
+
armSoon();
|
|
163
|
+
};
|
|
164
|
+
|
|
165
|
+
/** The child of <body> that holds `node` (the node itself when it is one); null for <body> or a node off the page. */
|
|
166
|
+
const partOf = (node) => {
|
|
167
|
+
for (let n = node; n; n = n.parentNode) if (n.parentNode === body) return n;
|
|
168
|
+
return null;
|
|
169
|
+
};
|
|
170
|
+
const hasElement = (nodes) => { for (const node of nodes || []) if (node?.nodeType === 1) return true; return false; };
|
|
171
|
+
// Each change → [IGNORE | SOON | NOW, whether the bar has to be measured].
|
|
172
|
+
const QUIET = [IGNORE, false];
|
|
173
|
+
const childListChange = (record) => {
|
|
174
|
+
if (!hasElement(record.addedNodes) && !hasElement(record.removedNodes)) return QUIET; // text
|
|
175
|
+
const target = record.target;
|
|
176
|
+
if (target === body) { judgeAgain(); return [NOW, true]; } // an overlay's root; a sibling selector may match anew
|
|
177
|
+
const part = partOf(target);
|
|
178
|
+
if (!part || part === bar) return QUIET; // off the page, or what the bar holds: that counts as the bar
|
|
179
|
+
toJudge.add(part);
|
|
180
|
+
if (target === part) return [NOW, true]; // an overlay's root got or lost a child (:empty, :has() can show it)
|
|
181
|
+
return [SOON, !underBar.has(part)];
|
|
182
|
+
};
|
|
183
|
+
const attributeChange = ({ target, name, before, after }) => {
|
|
184
|
+
// A modal dialog is drawn above everything, wherever it is in the page.
|
|
185
|
+
if (name === 'open' && target.nodeName === 'DIALOG') return [NOW, true];
|
|
186
|
+
if (target === body) {
|
|
187
|
+
if (name === 'class' && changedClasses(before, after).every(token => OWN_BODY_CLASSES.has(token))) return QUIET;
|
|
188
|
+
judgeAgain();
|
|
189
|
+
return [NOW, true];
|
|
190
|
+
}
|
|
191
|
+
const part = partOf(target);
|
|
192
|
+
if (!part) return QUIET;
|
|
193
|
+
if (part === bar) return target === bar ? [NOW, true] : QUIET;
|
|
194
|
+
toJudge.add(part);
|
|
195
|
+
// Shown, hidden or restacked: on the next frame; only moved, sized or repainted: at the throttled pace. Inside a
|
|
196
|
+
// part drawn under the bar only its style is read again (a class can lift the whole part).
|
|
197
|
+
const moveOnly = name === 'style' && styleChange(before, after) !== 'other';
|
|
198
|
+
return [moveOnly ? SOON : NOW, target === part || !underBar.has(part)];
|
|
199
|
+
};
|
|
200
|
+
/** What a batch of the page's changes can do to the cover; a record this cannot read counts as one that can. */
|
|
201
|
+
const pageChange = (records) => {
|
|
202
|
+
const { attributes, others } = foldRecords(records);
|
|
203
|
+
// The next frame's work and the throttled work, each with whether it measures the bar: kept apart, so that a
|
|
204
|
+
// verdict read on the next frame never takes a throttled measure along before its time.
|
|
205
|
+
let next = false;
|
|
206
|
+
let nextMeasured = false;
|
|
207
|
+
let soon = false;
|
|
208
|
+
let soonMeasured = false;
|
|
209
|
+
const add = ([level, measured]) => {
|
|
210
|
+
if (level === NOW) { next = true; if (measured) nextMeasured = true; }
|
|
211
|
+
else if (level === SOON) { soon = true; if (measured) soonMeasured = true; }
|
|
212
|
+
};
|
|
213
|
+
for (const record of others) {
|
|
214
|
+
if (record?.type === 'characterData') continue; // text
|
|
215
|
+
add(record?.type === 'childList' && record.target ? childListChange(record) : [NOW, true]);
|
|
216
|
+
}
|
|
217
|
+
for (const change of attributes) add(attributeChange(change));
|
|
218
|
+
if (next) coverNow(nextMeasured);
|
|
219
|
+
if (soon) coverSoon(soonMeasured);
|
|
88
220
|
};
|
|
89
|
-
const scheduleCover = () => { if (!coverFrame) coverFrame = view.requestAnimationFrame(checkCover); };
|
|
90
221
|
|
|
91
222
|
// ── Colours: the bar as drawn where the buttons are, over whatever is under it there ──
|
|
92
223
|
let colorTimer = null;
|
|
@@ -124,7 +255,7 @@ export function initDesktopTitleBar({ doc = document, view = window } = {}) {
|
|
|
124
255
|
html.style.setProperty('--desktop-titlebar-width', `${area.width}px`);
|
|
125
256
|
html.style.setProperty('--desktop-titlebar-height', `${area.height}px`);
|
|
126
257
|
}
|
|
127
|
-
|
|
258
|
+
coverNow();
|
|
128
259
|
scheduleColors();
|
|
129
260
|
};
|
|
130
261
|
const setState = (next) => { if (next && typeof next === 'object') state = { ...state, ...next }; layout(); };
|
|
@@ -133,21 +264,76 @@ export function initDesktopTitleBar({ doc = document, view = window } = {}) {
|
|
|
133
264
|
Promise.resolve(api.state()).then(setState).catch(() => {});
|
|
134
265
|
if (typeof api.onState === 'function') api.onState(setState);
|
|
135
266
|
wco?.addEventListener('geometrychange', layout);
|
|
136
|
-
view.addEventListener('resize', () => {
|
|
137
|
-
bar
|
|
267
|
+
view.addEventListener('resize', () => { coverNow(); scheduleColors(); });
|
|
268
|
+
// The bar's own transitions (its background, its left edge beside a sidebar); what it holds never colours the buttons.
|
|
269
|
+
bar.addEventListener('transitionend', (event) => { if (event?.target === bar) { scheduleColors(); coverSoon(); } });
|
|
270
|
+
if (typeof doc.addEventListener === 'function') {
|
|
271
|
+
// A popover is drawn above everything without a change of the page to see; an element in full screen too.
|
|
272
|
+
doc.addEventListener('toggle', (event) => { if (event?.target?.nodeName !== 'DETAILS') coverNow(); }, true);
|
|
273
|
+
doc.addEventListener('fullscreenchange', () => coverNow());
|
|
274
|
+
}
|
|
138
275
|
|
|
139
276
|
// What changes the bar's colours: its own class and style (the Memory Explorer, a sidebar), the stylesheets (a
|
|
140
|
-
// skin's <link>, which draws once it has loaded) and the interface settings (variables on <html>).
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
}
|
|
277
|
+
// skin's <link>, which draws once it has loaded) and the interface settings (variables on <html>). A class flipped
|
|
278
|
+
// and back, a <title> or a <meta> changes nothing. A stylesheet or <html> can also restack the page: the cover too
|
|
279
|
+
// (a theme's class or a stylesheet that loaded at once, the rest at the throttled pace).
|
|
280
|
+
const restyled = (now) => {
|
|
281
|
+
judgeAgain();
|
|
146
282
|
scheduleColors();
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
283
|
+
if (now) coverNow(); else coverSoon();
|
|
284
|
+
};
|
|
285
|
+
new view.MutationObserver((records) => {
|
|
286
|
+
const { attributes, others } = foldRecords(records);
|
|
287
|
+
if (attributes.length || others.length) scheduleColors();
|
|
288
|
+
}).observe(bar, { attributes: true, attributeOldValue: true, attributeFilter: ['class', 'style'] });
|
|
289
|
+
const onSheetLoad = () => restyled(true);
|
|
290
|
+
new view.MutationObserver((records) => {
|
|
291
|
+
const { attributes, others } = foldRecords(records);
|
|
292
|
+
for (const record of others) for (const node of record?.addedNodes || []) if (node.tagName === 'LINK') node.addEventListener('load', onSheetLoad, { once: true });
|
|
293
|
+
if (attributes.some(change => isStylesheet(change.target)) || others.some(sheetsChanged)) restyled(false);
|
|
294
|
+
}).observe(doc.head, { childList: true, subtree: true, attributes: true, attributeOldValue: true, attributeFilter: ['href', 'media', 'disabled'] });
|
|
295
|
+
new view.MutationObserver((records) => {
|
|
296
|
+
const { attributes, others } = foldRecords(records);
|
|
297
|
+
if (attributes.length || others.length) restyled(others.length > 0 || attributes.some(change => change.name === 'class'));
|
|
298
|
+
}).observe(html, { attributes: true, attributeOldValue: true, attributeFilter: ['style', 'class'] });
|
|
299
|
+
// What can come over the bar: anything added, shown or moved on the page (pageChange keeps what can matter).
|
|
300
|
+
new view.MutationObserver(pageChange).observe(body, { childList: true, subtree: true, attributes: true, attributeOldValue: true, attributeFilter: ['class', 'style', 'hidden', 'open'] });
|
|
151
301
|
|
|
152
302
|
return { facts, menuButton, refresh: layout, state: () => ({ ...state }) };
|
|
153
303
|
}
|
|
304
|
+
|
|
305
|
+
/**
|
|
306
|
+
* One MutationObserver batch with each attribute's records folded into its net change: { target, name, before,
|
|
307
|
+
* after } for an attribute whose value now differs from its value before the batch (a class removed and added back
|
|
308
|
+
* in the same frame is no change), and the other records as they came.
|
|
309
|
+
*/
|
|
310
|
+
function foldRecords(records) {
|
|
311
|
+
const first = new Map();
|
|
312
|
+
const others = [];
|
|
313
|
+
for (const record of records || []) {
|
|
314
|
+
if (record?.type !== 'attributes' || !record.target || !record.attributeName) { others.push(record); continue; }
|
|
315
|
+
let names = first.get(record.target);
|
|
316
|
+
if (!names) first.set(record.target, (names = new Map()));
|
|
317
|
+
if (!names.has(record.attributeName)) names.set(record.attributeName, record.oldValue ?? null);
|
|
318
|
+
}
|
|
319
|
+
const attributes = [];
|
|
320
|
+
for (const [target, names] of first) {
|
|
321
|
+
for (const [name, before] of names) {
|
|
322
|
+
const after = target.getAttribute(name);
|
|
323
|
+
if (after !== before) attributes.push({ target, name, before, after });
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
return { attributes, others };
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
const isStylesheet = (node) => node?.nodeName === 'LINK' || node?.nodeName === 'STYLE';
|
|
330
|
+
|
|
331
|
+
/** Whether a record of <head> added or removed a stylesheet or changed one's text; a record this cannot read did. */
|
|
332
|
+
function sheetsChanged(record) {
|
|
333
|
+
if (record?.type !== 'childList') return true;
|
|
334
|
+
if (isStylesheet(record.target)) return true;
|
|
335
|
+
for (const nodes of [record.addedNodes, record.removedNodes]) {
|
|
336
|
+
for (const node of nodes || []) if (isStylesheet(node) || node?.querySelector?.('link, style')) return true;
|
|
337
|
+
}
|
|
338
|
+
return false;
|
|
339
|
+
}
|