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,480 @@
|
|
|
1
|
+
// The app's custom dropdown (DESIGN.md "Settings custom dropdown", `.cc-dropdown`): a hidden
|
|
2
|
+
// <input id> holds the value, a trigger button shows the choice, a menu lists the choices.
|
|
3
|
+
// settings-kit.js dropdown() writes the same markup for Settings; this module writes it with its
|
|
4
|
+
// ARIA and drives every dropdown under a root from one set of listeners, so markup drawn again
|
|
5
|
+
// with innerHTML needs no wiring of its own.
|
|
6
|
+
//
|
|
7
|
+
// Every dropdown in SynaBun is this one: a native <select> is not allowed (tests/no-native-select.test.mjs).
|
|
8
|
+
// Adding one, as the Schedules window (ui-schedules-studio.js) does:
|
|
9
|
+
// 1. Markup: dropdownHTML(id, [{ value, label, selected }], { labelledBy }); in Settings, settings-kit.js dropdown().
|
|
10
|
+
// 2. Wiring: wireDropdowns(container, { signal }) once; markup drawn later needs nothing more.
|
|
11
|
+
// 3. Reading: the hidden input's `.value`, and its `change` event (it bubbles), as with a <select>.
|
|
12
|
+
// 4. Changing: setDropdownItems(input, entries), setDropdownValue(input, value), setDropdownDisabled(input, on).
|
|
13
|
+
// 5. Focusing it or marking it invalid: controlOf(input) is its trigger.
|
|
14
|
+
//
|
|
15
|
+
// To the code around it, it reads like a <select>: the input's `.value` is the choice, and a
|
|
16
|
+
// choice the user makes fires `input` and `change` on the input (bubbling). Choices are
|
|
17
|
+
// `{ value, label, selected, disabled }` (labels are text), or `{ group, options }` for a heading.
|
|
18
|
+
// The chosen one is the last marked `selected`, else the first that is not disabled: a <select>'s
|
|
19
|
+
// rule, so a list rebuilt from saved data shows what was saved. setDropdownItems() is
|
|
20
|
+
// `select.innerHTML = …`, setDropdownValue() is `select.value = …`; neither fires an event.
|
|
21
|
+
//
|
|
22
|
+
// Keyboard and ARIA are the WAI-ARIA select-only combobox: the focus stays on the trigger and the
|
|
23
|
+
// highlighted option is its aria-activedescendant. Enter, Space or an arrow opens; arrows, Home,
|
|
24
|
+
// End, Page Up/Down and typing move; Enter or Space chooses; Escape closes without a change; Tab
|
|
25
|
+
// closes and moves on, the choice as it was. An open menu is placed `fixed` against its trigger,
|
|
26
|
+
// so a scrolling pane never cuts it off, and kept inside the box that clips it (a window).
|
|
27
|
+
|
|
28
|
+
const ARROW = '<svg class="cc-dropdown-arrow" viewBox="0 0 24 24" aria-hidden="true"><polyline points="6 9 12 15 18 9"/></svg>';
|
|
29
|
+
const GAP = 4; // between the trigger and its menu
|
|
30
|
+
const EDGE = 6; // the least room left between a menu and the edge of what clips it
|
|
31
|
+
const PAGE = 10; // options a Page Up/Down moves
|
|
32
|
+
const TYPE_PAUSE = 500; // ms between keys that still extend a type-ahead search
|
|
33
|
+
|
|
34
|
+
const esc = (value) => String(value ?? '').replace(/[&<>"']/g, (c) => ({
|
|
35
|
+
'&': '&', '<': '<', '>': '>', '"': '"', "'": ''',
|
|
36
|
+
}[c]));
|
|
37
|
+
|
|
38
|
+
// ── What decides (no DOM) ──
|
|
39
|
+
|
|
40
|
+
/** The options of a list of entries, headings left out, in their order. */
|
|
41
|
+
export function flatOptions(entries) {
|
|
42
|
+
const out = [];
|
|
43
|
+
for (const entry of entries || []) {
|
|
44
|
+
if (entry && Array.isArray(entry.options)) out.push(...entry.options.filter(Boolean));
|
|
45
|
+
else if (entry) out.push(entry);
|
|
46
|
+
}
|
|
47
|
+
return out;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/** The chosen option as a <select> picks it: the last marked selected, else the first enabled; -1 for none. */
|
|
51
|
+
export function chosenIndex(options) {
|
|
52
|
+
let last = -1;
|
|
53
|
+
options.forEach((option, i) => { if (option.selected) last = i; });
|
|
54
|
+
return last >= 0 ? last : options.findIndex(option => !option.disabled);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/** The first or last option that is not disabled; -1 for none. */
|
|
58
|
+
export function edgeIndex(options, which) {
|
|
59
|
+
if (which === 'first') return options.findIndex(option => !option.disabled);
|
|
60
|
+
for (let i = options.length - 1; i >= 0; i--) if (!options[i].disabled) return i;
|
|
61
|
+
return -1;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/** `steps` enabled options away from `from` (negative: up), stopping at the ends. From none, the first or last. */
|
|
65
|
+
export function stepIndex(options, from, steps) {
|
|
66
|
+
if (from < 0 || from >= options.length) return edgeIndex(options, steps < 0 ? 'last' : 'first');
|
|
67
|
+
const dir = Math.sign(steps) || 1;
|
|
68
|
+
let left = Math.abs(steps) || 1;
|
|
69
|
+
let found = from;
|
|
70
|
+
for (let i = from + dir; i >= 0 && i < options.length; i += dir) {
|
|
71
|
+
if (options[i].disabled) continue;
|
|
72
|
+
found = i;
|
|
73
|
+
if (--left === 0) break;
|
|
74
|
+
}
|
|
75
|
+
return found;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Type-ahead: the option whose label starts with what was typed, searching on from the highlighted
|
|
80
|
+
* one and round. One letter typed again moves on to the next option with that letter, as a <select> does.
|
|
81
|
+
*/
|
|
82
|
+
export function matchIndex(options, typed, from = -1) {
|
|
83
|
+
const query = String(typed || '').toLocaleLowerCase();
|
|
84
|
+
if (!query) return -1;
|
|
85
|
+
const repeated = [...query].every(c => c === query[0]);
|
|
86
|
+
const needle = repeated ? query[0] : query;
|
|
87
|
+
const n = options.length;
|
|
88
|
+
const start = repeated ? from + 1 : Math.max(from, 0);
|
|
89
|
+
for (let k = 0; k < n; k++) {
|
|
90
|
+
const i = (((start + k) % n) + n) % n;
|
|
91
|
+
const option = options[i];
|
|
92
|
+
if (!option.disabled && String(option.label ?? '').trim().toLocaleLowerCase().startsWith(needle)) return i;
|
|
93
|
+
}
|
|
94
|
+
return -1;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// ── Markup ──
|
|
98
|
+
|
|
99
|
+
function optionHTML(id, option, index, chosen) {
|
|
100
|
+
const label = String(option.label ?? '');
|
|
101
|
+
const active = index === chosen;
|
|
102
|
+
return `<div class="cc-dropdown-item${active ? ' active' : ''}" id="${esc(id)}-opt-${index}" role="option" aria-selected="${active}"`
|
|
103
|
+
+ `${option.disabled ? ' aria-disabled="true"' : ''} data-value="${esc(option.value ?? '')}" data-index="${index}" title="${esc(label)}">${esc(label)}</div>`;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/** The menu's options (and headings) for `entries`, the `chosen` index marked. */
|
|
107
|
+
export function menuHTML(id, entries, chosen = chosenIndex(flatOptions(entries))) {
|
|
108
|
+
let index = 0;
|
|
109
|
+
let group = 0;
|
|
110
|
+
return (entries || []).filter(Boolean).map((entry) => {
|
|
111
|
+
if (!Array.isArray(entry.options)) return optionHTML(id, entry, index++, chosen);
|
|
112
|
+
const headId = `${id}-grp-${group++}`;
|
|
113
|
+
const options = entry.options.filter(Boolean).map(option => optionHTML(id, option, index++, chosen)).join('');
|
|
114
|
+
return `<div class="cc-dropdown-optgroup" role="group" aria-labelledby="${esc(headId)}"><div class="cc-dropdown-group" id="${esc(headId)}" role="presentation">${esc(entry.group ?? '')}</div>${options}</div>`;
|
|
115
|
+
}).join('');
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* A dropdown: the hidden input (`id`, with `inputAttrs`) and the `.cc-dropdown` it belongs to.
|
|
120
|
+
* Name it with `labelledBy` (the id of its visible label) or `label`. `classes` and `attrs` go on
|
|
121
|
+
* the `.cc-dropdown`.
|
|
122
|
+
*/
|
|
123
|
+
export function dropdownHTML(id, entries, { classes = '', labelledBy = '', label = '', attrs = '', inputAttrs = '', disabled = false } = {}) {
|
|
124
|
+
const options = flatOptions(entries);
|
|
125
|
+
const chosen = chosenIndex(options);
|
|
126
|
+
const current = chosen >= 0 ? options[chosen] : null;
|
|
127
|
+
const name = labelledBy ? ` aria-labelledby="${esc(labelledBy)}"` : label ? ` aria-label="${esc(label)}"` : '';
|
|
128
|
+
const text = String(current?.label ?? '');
|
|
129
|
+
return `<input type="hidden" id="${esc(id)}" value="${esc(current?.value ?? '')}"${disabled ? ' disabled' : ''}${inputAttrs ? ` ${inputAttrs}` : ''}>`
|
|
130
|
+
+ `<div class="cc-dropdown${classes ? ` ${classes}` : ''}${disabled ? ' disabled' : ''}" data-for="${esc(id)}"${attrs ? ` ${attrs}` : ''}>`
|
|
131
|
+
+ `<button type="button" class="cc-dropdown-trigger" id="${esc(id)}-trigger" role="combobox" aria-haspopup="listbox" aria-expanded="false" aria-controls="${esc(id)}-menu"${name}${disabled ? ' disabled' : ''}>`
|
|
132
|
+
+ `<span class="cc-dropdown-value" title="${esc(text)}">${esc(text)}</span>${ARROW}</button>`
|
|
133
|
+
+ `<div class="cc-dropdown-menu" id="${esc(id)}-menu" role="listbox"${name} tabindex="-1">${menuHTML(id, entries, chosen)}</div>`
|
|
134
|
+
+ '</div>';
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// ── The DOM: finding the parts ──
|
|
138
|
+
|
|
139
|
+
const partsOf = (dd) => ({
|
|
140
|
+
trigger: dd.querySelector(':scope > .cc-dropdown-trigger'),
|
|
141
|
+
menu: dd.querySelector(':scope > .cc-dropdown-menu'),
|
|
142
|
+
label: dd.querySelector(':scope > .cc-dropdown-trigger .cc-dropdown-value'),
|
|
143
|
+
input: dd.dataset.for ? document.getElementById(dd.dataset.for) : null,
|
|
144
|
+
});
|
|
145
|
+
const itemsOf = (dd) => [...(partsOf(dd).menu?.querySelectorAll('.cc-dropdown-item') || [])];
|
|
146
|
+
const optionsOf = (items) => items.map(el => ({ value: el.dataset.value ?? '', label: el.textContent, disabled: el.getAttribute('aria-disabled') === 'true' }));
|
|
147
|
+
const highlightedOf = (items) => items.findIndex(el => el.classList.contains('highlight'));
|
|
148
|
+
const chosenOf = (items) => items.findIndex(el => el.classList.contains('active'));
|
|
149
|
+
const isOpen = (dd) => !!dd?.classList.contains('open');
|
|
150
|
+
|
|
151
|
+
/** The `.cc-dropdown` of a hidden input, or null. */
|
|
152
|
+
export function dropdownOf(input) {
|
|
153
|
+
if (!input?.id) return null;
|
|
154
|
+
const next = input.nextElementSibling;
|
|
155
|
+
if (next?.classList.contains('cc-dropdown') && next.dataset.for === input.id) return next;
|
|
156
|
+
return document.querySelector(`.cc-dropdown[data-for="${CSS.escape(input.id)}"]`);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/** What takes the focus for a field: a dropdown's trigger for its hidden input, else the element itself. */
|
|
160
|
+
export function controlOf(el) {
|
|
161
|
+
const dd = el && el.type === 'hidden' ? dropdownOf(el) : null;
|
|
162
|
+
return (dd && partsOf(dd).trigger) || el;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// Markup written by settings-kit.js dropdown() has no roles or ids: they are added when it opens.
|
|
166
|
+
function ensureAria(dd) {
|
|
167
|
+
const { trigger, menu, input } = partsOf(dd);
|
|
168
|
+
const base = input?.id || dd.dataset.for || 'cc-dropdown';
|
|
169
|
+
if (!trigger.hasAttribute('role')) trigger.setAttribute('role', 'combobox');
|
|
170
|
+
if (!menu.id) menu.id = `${base}-menu`;
|
|
171
|
+
trigger.setAttribute('aria-controls', menu.id);
|
|
172
|
+
if (!menu.hasAttribute('role')) menu.setAttribute('role', 'listbox');
|
|
173
|
+
itemsOf(dd).forEach((el, i) => {
|
|
174
|
+
if (!el.id) el.id = `${base}-opt-${i}`;
|
|
175
|
+
if (!el.hasAttribute('role')) el.setAttribute('role', 'option');
|
|
176
|
+
el.setAttribute('aria-selected', String(el.classList.contains('active')));
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// ── Choosing, opening, closing ──
|
|
181
|
+
|
|
182
|
+
function showChosen(dd, index) {
|
|
183
|
+
const { label, input } = partsOf(dd);
|
|
184
|
+
const items = itemsOf(dd);
|
|
185
|
+
items.forEach((el, i) => {
|
|
186
|
+
el.classList.toggle('active', i === index);
|
|
187
|
+
el.setAttribute('aria-selected', String(i === index));
|
|
188
|
+
});
|
|
189
|
+
const item = items[index];
|
|
190
|
+
if (input) input.value = item ? item.dataset.value ?? '' : '';
|
|
191
|
+
if (label) {
|
|
192
|
+
label.textContent = item ? item.textContent : '';
|
|
193
|
+
label.title = label.textContent;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
function highlight(dd, index) {
|
|
198
|
+
const { trigger, menu } = partsOf(dd);
|
|
199
|
+
const items = itemsOf(dd);
|
|
200
|
+
items.forEach((el, i) => el.classList.toggle('highlight', i === index));
|
|
201
|
+
const item = items[index];
|
|
202
|
+
if (!item) { trigger?.removeAttribute('aria-activedescendant'); return; }
|
|
203
|
+
trigger?.setAttribute('aria-activedescendant', item.id);
|
|
204
|
+
// The menu scrolls itself; scrollIntoView would also move the panes and the window behind it.
|
|
205
|
+
const box = menu.getBoundingClientRect();
|
|
206
|
+
const row = item.getBoundingClientRect();
|
|
207
|
+
if (row.top < box.top + menu.clientTop) menu.scrollTop -= box.top + menu.clientTop - row.top;
|
|
208
|
+
else if (row.bottom > box.top + menu.clientTop + menu.clientHeight) menu.scrollTop += row.bottom - (box.top + menu.clientTop + menu.clientHeight);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// What a fixed menu is placed against and cut off by: the nearest ancestor that is a containing
|
|
212
|
+
// block for fixed boxes (a transform, a filter, containment: a floating window), else the viewport.
|
|
213
|
+
function fixedContainer(el) {
|
|
214
|
+
for (let node = el.parentElement; node && node !== document.documentElement; node = node.parentElement) {
|
|
215
|
+
const cs = getComputedStyle(node);
|
|
216
|
+
if (cs.transform !== 'none' || cs.perspective !== 'none' || cs.filter !== 'none'
|
|
217
|
+
|| (cs.backdropFilter && cs.backdropFilter !== 'none') || (cs.webkitBackdropFilter && cs.webkitBackdropFilter !== 'none')
|
|
218
|
+
|| /paint|layout|strict|content/.test(cs.contain || '') || (cs.containerType && cs.containerType !== 'normal')
|
|
219
|
+
|| /transform|perspective|filter/.test(cs.willChange || '')) return node;
|
|
220
|
+
}
|
|
221
|
+
return null;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
function place(dd) {
|
|
225
|
+
const { trigger, menu } = partsOf(dd);
|
|
226
|
+
const style = menu.style;
|
|
227
|
+
Object.assign(style, { position: 'fixed', left: '0px', top: '0px', right: 'auto', bottom: 'auto', width: 'auto', maxHeight: 'none', minWidth: '0px', maxWidth: 'none' });
|
|
228
|
+
const origin = menu.getBoundingClientRect(); // where the containing block's (0, 0) is on screen
|
|
229
|
+
const t = trigger.getBoundingClientRect();
|
|
230
|
+
let room = { left: 0, top: 0, right: window.innerWidth, bottom: window.innerHeight };
|
|
231
|
+
const box = fixedContainer(dd);
|
|
232
|
+
if (box) {
|
|
233
|
+
const cs = getComputedStyle(box);
|
|
234
|
+
if (cs.overflowX !== 'visible' || cs.overflowY !== 'visible') {
|
|
235
|
+
const r = box.getBoundingClientRect();
|
|
236
|
+
room = { left: Math.max(room.left, r.left), top: Math.max(room.top, r.top), right: Math.min(room.right, r.right), bottom: Math.min(room.bottom, r.bottom) };
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
room = { left: room.left + EDGE, top: room.top + EDGE, right: room.right - EDGE, bottom: room.bottom - EDGE };
|
|
240
|
+
const roomWidth = Math.max(0, room.right - room.left);
|
|
241
|
+
style.maxWidth = `${roomWidth}px`;
|
|
242
|
+
style.minWidth = `${Math.min(t.width, roomWidth)}px`;
|
|
243
|
+
const height = menu.offsetHeight;
|
|
244
|
+
const below = room.bottom - t.bottom - GAP;
|
|
245
|
+
const above = t.top - GAP - room.top;
|
|
246
|
+
const down = height <= below || below >= above;
|
|
247
|
+
const space = Math.max(0, down ? below : above);
|
|
248
|
+
if (height > space) style.maxHeight = `${space}px`;
|
|
249
|
+
const shown = Math.min(height, space);
|
|
250
|
+
const width = menu.offsetWidth;
|
|
251
|
+
const left = Math.max(room.left, Math.min(t.left, room.right - width));
|
|
252
|
+
const top = down ? t.bottom + GAP : t.top - GAP - shown;
|
|
253
|
+
style.left = `${Math.round(left - origin.left)}px`;
|
|
254
|
+
style.top = `${Math.round(top - origin.top)}px`;
|
|
255
|
+
dd.classList.toggle('drop-up', !down);
|
|
256
|
+
dd._ccPlacedAt = { left: t.left, top: t.top };
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
// Has the trigger moved since its menu was placed (a pane scrolled under it)?
|
|
260
|
+
function moved(dd) {
|
|
261
|
+
const at = dd._ccPlacedAt;
|
|
262
|
+
const now = partsOf(dd).trigger?.getBoundingClientRect();
|
|
263
|
+
return !at || !now || Math.abs(now.left - at.left) > 1 || Math.abs(now.top - at.top) > 1;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
function open(dd, index = null) {
|
|
267
|
+
const { trigger } = partsOf(dd);
|
|
268
|
+
if (!trigger || trigger.disabled || dd.classList.contains('disabled')) return;
|
|
269
|
+
for (const other of document.querySelectorAll('.cc-dropdown.open')) if (other !== dd) close(other);
|
|
270
|
+
ensureAria(dd);
|
|
271
|
+
dd.classList.add('open');
|
|
272
|
+
trigger.setAttribute('aria-expanded', 'true');
|
|
273
|
+
if (document.activeElement !== trigger) trigger.focus({ preventScroll: true });
|
|
274
|
+
place(dd);
|
|
275
|
+
const items = itemsOf(dd);
|
|
276
|
+
const options = optionsOf(items);
|
|
277
|
+
const chosen = chosenOf(items);
|
|
278
|
+
highlight(dd, index ?? (chosen >= 0 && !options[chosen].disabled ? chosen : edgeIndex(options, 'first')));
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
function close(dd, { focus = false } = {}) {
|
|
282
|
+
const { trigger, menu } = partsOf(dd);
|
|
283
|
+
dd.classList.remove('open', 'drop-up');
|
|
284
|
+
trigger?.setAttribute('aria-expanded', 'false');
|
|
285
|
+
trigger?.removeAttribute('aria-activedescendant');
|
|
286
|
+
for (const el of itemsOf(dd)) el.classList.remove('highlight');
|
|
287
|
+
menu?.removeAttribute('style');
|
|
288
|
+
dd._ccPlacedAt = null;
|
|
289
|
+
if (focus && trigger?.isConnected) trigger.focus({ preventScroll: true });
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
/** The user's choice: shown, the menu closed, the focus on the trigger, then `input` and `change` when it changed. */
|
|
293
|
+
function choose(dd, index) {
|
|
294
|
+
const items = itemsOf(dd);
|
|
295
|
+
const item = items[index];
|
|
296
|
+
const { input } = partsOf(dd);
|
|
297
|
+
if (!item || item.getAttribute('aria-disabled') === 'true') { close(dd, { focus: true }); return; }
|
|
298
|
+
const changed = !!input && input.value !== (item.dataset.value ?? '');
|
|
299
|
+
showChosen(dd, index);
|
|
300
|
+
close(dd, { focus: true });
|
|
301
|
+
// After the focus is back: a listener that draws the form again finds the trigger focused.
|
|
302
|
+
if (changed) {
|
|
303
|
+
input.dispatchEvent(new Event('input', { bubbles: true }));
|
|
304
|
+
input.dispatchEvent(new Event('change', { bubbles: true }));
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
// ── What the code around it calls ──
|
|
309
|
+
|
|
310
|
+
/** `select.innerHTML = …`: new choices; the chosen one by the <select> rule; an open menu stays open. No event. */
|
|
311
|
+
export function setDropdownItems(input, entries) {
|
|
312
|
+
const dd = dropdownOf(input);
|
|
313
|
+
if (!dd) return;
|
|
314
|
+
const { menu } = partsOf(dd);
|
|
315
|
+
const wasOpen = isOpen(dd);
|
|
316
|
+
const before = wasOpen ? itemsOf(dd)[highlightedOf(itemsOf(dd))]?.dataset.value : undefined;
|
|
317
|
+
const options = flatOptions(entries);
|
|
318
|
+
const chosen = chosenIndex(options);
|
|
319
|
+
menu.innerHTML = menuHTML(input.id, entries, chosen);
|
|
320
|
+
showChosen(dd, chosen);
|
|
321
|
+
if (!wasOpen) return;
|
|
322
|
+
place(dd);
|
|
323
|
+
const items = itemsOf(dd);
|
|
324
|
+
const again = before === undefined ? -1 : items.findIndex(el => el.dataset.value === before);
|
|
325
|
+
highlight(dd, again >= 0 ? again : chosen);
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
/** `select.value = …`: the first choice with that value, or none (the value is then ''). No event. */
|
|
329
|
+
export function setDropdownValue(input, value) {
|
|
330
|
+
const dd = dropdownOf(input);
|
|
331
|
+
if (!dd) { if (input) input.value = value ?? ''; return; }
|
|
332
|
+
showChosen(dd, itemsOf(dd).findIndex(el => el.dataset.value === String(value ?? '')));
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
/** `select.disabled = …`: the input, the trigger and the look. A disabled dropdown closes. */
|
|
336
|
+
export function setDropdownDisabled(input, disabled) {
|
|
337
|
+
const dd = dropdownOf(input);
|
|
338
|
+
if (input) input.disabled = !!disabled;
|
|
339
|
+
if (!dd) return;
|
|
340
|
+
if (disabled && isOpen(dd)) close(dd);
|
|
341
|
+
dd.classList.toggle('disabled', !!disabled);
|
|
342
|
+
const { trigger } = partsOf(dd);
|
|
343
|
+
if (trigger) trigger.disabled = !!disabled;
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
/** Closes every open dropdown under `root`; true when one was open. `focus` puts the focus back on its trigger. */
|
|
347
|
+
export function closeDropdowns(root = document, { focus = false } = {}) {
|
|
348
|
+
const open = [...root.querySelectorAll('.cc-dropdown.open')];
|
|
349
|
+
for (const dd of open) close(dd, { focus });
|
|
350
|
+
return open.length > 0;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
/**
|
|
354
|
+
* Drives every `.cc-dropdown` under `root`, now and drawn later. `signal` (an AbortSignal) takes
|
|
355
|
+
* the listeners off again, the document's and the window's too.
|
|
356
|
+
*/
|
|
357
|
+
export function wireDropdowns(root, { signal } = {}) {
|
|
358
|
+
const on = (target, type, handler, options = {}) => target.addEventListener(type, handler, { ...options, signal });
|
|
359
|
+
let typed = '';
|
|
360
|
+
let typedAt = 0;
|
|
361
|
+
let swallowSpaceUp = false;
|
|
362
|
+
|
|
363
|
+
const typeAhead = (dd, char) => {
|
|
364
|
+
const now = Date.now();
|
|
365
|
+
typed = (now - typedAt > TYPE_PAUSE ? '' : typed) + char;
|
|
366
|
+
typedAt = now;
|
|
367
|
+
const items = itemsOf(dd);
|
|
368
|
+
const index = matchIndex(optionsOf(items), typed, highlightedOf(items));
|
|
369
|
+
if (index >= 0) highlight(dd, index);
|
|
370
|
+
};
|
|
371
|
+
const typing = () => !!typed && Date.now() - typedAt <= TYPE_PAUSE;
|
|
372
|
+
// Each opening starts a search of its own: letters typed before it closed do not carry over.
|
|
373
|
+
const show = (dd, index) => { typed = ''; open(dd, index); };
|
|
374
|
+
const printable = (e) => e.key.length === 1 && !e.ctrlKey && !e.metaKey && !e.altKey && e.key !== ' ';
|
|
375
|
+
|
|
376
|
+
on(root, 'click', (e) => {
|
|
377
|
+
const item = e.target.closest?.('.cc-dropdown-item');
|
|
378
|
+
if (item && root.contains(item)) {
|
|
379
|
+
const dd = item.closest('.cc-dropdown');
|
|
380
|
+
if (item.getAttribute('aria-disabled') !== 'true') choose(dd, itemsOf(dd).indexOf(item));
|
|
381
|
+
return;
|
|
382
|
+
}
|
|
383
|
+
const trigger = e.target.closest?.('.cc-dropdown-trigger');
|
|
384
|
+
if (!trigger || !root.contains(trigger)) return;
|
|
385
|
+
const dd = trigger.closest('.cc-dropdown');
|
|
386
|
+
if (isOpen(dd)) close(dd, { focus: true }); else show(dd);
|
|
387
|
+
});
|
|
388
|
+
|
|
389
|
+
// The focus stays on the trigger while a choice is clicked.
|
|
390
|
+
on(root, 'mousedown', (e) => { if (e.target.closest?.('.cc-dropdown-menu')) e.preventDefault(); });
|
|
391
|
+
on(root, 'mouseover', (e) => {
|
|
392
|
+
const item = e.target.closest?.('.cc-dropdown-item');
|
|
393
|
+
if (!item || item.getAttribute('aria-disabled') === 'true') return;
|
|
394
|
+
const dd = item.closest('.cc-dropdown');
|
|
395
|
+
if (isOpen(dd)) highlight(dd, itemsOf(dd).indexOf(item));
|
|
396
|
+
});
|
|
397
|
+
|
|
398
|
+
on(root, 'keydown', (e) => {
|
|
399
|
+
const trigger = e.target.closest?.('.cc-dropdown-trigger');
|
|
400
|
+
if (!trigger || !root.contains(trigger) || trigger.disabled) return;
|
|
401
|
+
const dd = trigger.closest('.cc-dropdown');
|
|
402
|
+
const items = itemsOf(dd);
|
|
403
|
+
const options = optionsOf(items);
|
|
404
|
+
const current = highlightedOf(items);
|
|
405
|
+
if (!isOpen(dd)) {
|
|
406
|
+
if (['ArrowDown', 'ArrowUp', 'Enter', ' '].includes(e.key)) {
|
|
407
|
+
e.preventDefault();
|
|
408
|
+
if (e.key === ' ') swallowSpaceUp = true;
|
|
409
|
+
show(dd);
|
|
410
|
+
} else if (e.key === 'Home' || e.key === 'End') {
|
|
411
|
+
e.preventDefault();
|
|
412
|
+
show(dd, edgeIndex(options, e.key === 'Home' ? 'first' : 'last'));
|
|
413
|
+
} else if (printable(e)) {
|
|
414
|
+
e.preventDefault();
|
|
415
|
+
show(dd);
|
|
416
|
+
typeAhead(dd, e.key);
|
|
417
|
+
}
|
|
418
|
+
return;
|
|
419
|
+
}
|
|
420
|
+
switch (e.key) {
|
|
421
|
+
case 'ArrowDown':
|
|
422
|
+
case 'ArrowUp':
|
|
423
|
+
e.preventDefault();
|
|
424
|
+
if (e.altKey && e.key === 'ArrowUp') choose(dd, current);
|
|
425
|
+
else highlight(dd, stepIndex(options, current, e.key === 'ArrowDown' ? 1 : -1));
|
|
426
|
+
break;
|
|
427
|
+
case 'PageDown':
|
|
428
|
+
case 'PageUp':
|
|
429
|
+
e.preventDefault();
|
|
430
|
+
highlight(dd, stepIndex(options, current, e.key === 'PageDown' ? PAGE : -PAGE));
|
|
431
|
+
break;
|
|
432
|
+
case 'Home':
|
|
433
|
+
case 'End':
|
|
434
|
+
e.preventDefault();
|
|
435
|
+
highlight(dd, edgeIndex(options, e.key === 'Home' ? 'first' : 'last'));
|
|
436
|
+
break;
|
|
437
|
+
case 'Enter':
|
|
438
|
+
e.preventDefault();
|
|
439
|
+
choose(dd, current);
|
|
440
|
+
break;
|
|
441
|
+
case ' ':
|
|
442
|
+
e.preventDefault();
|
|
443
|
+
swallowSpaceUp = true;
|
|
444
|
+
if (typing()) typeAhead(dd, ' '); else choose(dd, current);
|
|
445
|
+
break;
|
|
446
|
+
case 'Escape':
|
|
447
|
+
e.preventDefault();
|
|
448
|
+
e.stopPropagation();
|
|
449
|
+
close(dd, { focus: true });
|
|
450
|
+
break;
|
|
451
|
+
case 'Tab':
|
|
452
|
+
close(dd); // the focus moves on as usual; the choice stays as it was
|
|
453
|
+
break;
|
|
454
|
+
default:
|
|
455
|
+
if (printable(e)) { e.preventDefault(); typeAhead(dd, e.key); }
|
|
456
|
+
}
|
|
457
|
+
});
|
|
458
|
+
// A Space that opened or chose must not also click the trigger when it is let go.
|
|
459
|
+
on(root, 'keyup', (e) => {
|
|
460
|
+
if (e.key === ' ' && swallowSpaceUp) { swallowSpaceUp = false; e.preventDefault(); }
|
|
461
|
+
});
|
|
462
|
+
|
|
463
|
+
on(root, 'focusout', (e) => {
|
|
464
|
+
const dd = e.target.closest?.('.cc-dropdown');
|
|
465
|
+
if (dd && isOpen(dd) && !dd.contains(e.relatedTarget)) close(dd);
|
|
466
|
+
});
|
|
467
|
+
on(document, 'pointerdown', (e) => {
|
|
468
|
+
for (const dd of root.querySelectorAll('.cc-dropdown.open')) if (!dd.contains(e.target)) close(dd);
|
|
469
|
+
}, { capture: true });
|
|
470
|
+
// A pane that scrolls the trigger away from its menu closes the menu. A scroll event arrives a frame
|
|
471
|
+
// late, so one from before the menu opened (a click that first scrolled its trigger into view)
|
|
472
|
+
// finds the trigger where the menu was placed, and is let be.
|
|
473
|
+
on(document, 'scroll', (e) => {
|
|
474
|
+
for (const dd of root.querySelectorAll('.cc-dropdown.open')) {
|
|
475
|
+
const target = e.target === document ? document.documentElement : e.target;
|
|
476
|
+
if (!partsOf(dd).menu.contains(target) && target.contains?.(dd) && moved(dd)) close(dd);
|
|
477
|
+
}
|
|
478
|
+
}, { capture: true });
|
|
479
|
+
on(window, 'resize', () => closeDropdowns(root));
|
|
480
|
+
}
|
|
@@ -10,6 +10,7 @@ import { createStickyScrollController, getStickyScrollController } from '../ui-s
|
|
|
10
10
|
import { wireFileLinks } from '../ui-file-links.js';
|
|
11
11
|
import { trackWorkerEnd } from '../ui-worker-end.js';
|
|
12
12
|
import { isDispatchedRun } from '../worker-end.js';
|
|
13
|
+
import { runAwaitsAnswer } from '../sidepanel-session-status.js';
|
|
13
14
|
import { injectStyles } from './cdx-styles.js';
|
|
14
15
|
import { mountContextMenu } from './cdx-context-menu.js';
|
|
15
16
|
import { teardownMcpApps } from './cdx-mcp-app-host.js';
|
|
@@ -261,7 +262,7 @@ function buildPanel() {
|
|
|
261
262
|
panel.innerHTML = `
|
|
262
263
|
<div class="cxp-resize-handle"></div>
|
|
263
264
|
<div class="cxp-header">
|
|
264
|
-
<button class="cxp-session-btn" id="cxp-session-btn" type="button">
|
|
265
|
+
<button class="cxp-session-btn" id="cxp-session-btn" type="button" title="${esc(_sessionLabel)}">
|
|
265
266
|
<span class="cxp-session-label" id="cxp-session-label">${esc(_sessionLabel)}</span>
|
|
266
267
|
<span class="cxp-dd-arrow">▾</span>
|
|
267
268
|
</button>
|
|
@@ -1204,6 +1205,15 @@ export function isCodexPanelOpen() {
|
|
|
1204
1205
|
return _visible;
|
|
1205
1206
|
}
|
|
1206
1207
|
|
|
1208
|
+
/**
|
|
1209
|
+
* Boot the panel without showing it (ui-sidepanel-tray.js, when a tab saved while it worked comes back
|
|
1210
|
+
* after a reload): the tabs that were working reattach at once (restoreTabs), so their pills say what
|
|
1211
|
+
* the threads do now.
|
|
1212
|
+
*/
|
|
1213
|
+
export function bootCodexPanel() {
|
|
1214
|
+
ensurePanel();
|
|
1215
|
+
}
|
|
1216
|
+
|
|
1207
1217
|
export async function toggleCodexPanel() {
|
|
1208
1218
|
ensurePanel();
|
|
1209
1219
|
if (_visible) {
|
|
@@ -1252,10 +1262,12 @@ export async function attachCodexAutomation(run, {
|
|
|
1252
1262
|
}, { autoSwitch: false });
|
|
1253
1263
|
}
|
|
1254
1264
|
if (!tab) return { ok: false, reason: 'tab_limit' };
|
|
1255
|
-
// A task-mode worker between turns (waiting for a follow-up) is alive, not working
|
|
1265
|
+
// A task-mode worker between turns (waiting for a follow-up) is alive, not working; one whose turn ended
|
|
1266
|
+
// on a question needs you, like one with an approval card up (setRunning and renderPills repaint).
|
|
1256
1267
|
const working = running && run.turnState !== 'idle' && run.turnState !== 'terminal';
|
|
1257
1268
|
tab.automationRunId = run.runId;
|
|
1258
1269
|
tab.automationActive = running;
|
|
1270
|
+
tab.automationAwaits = runAwaitsAnswer(run);
|
|
1259
1271
|
tab.automationOwnerId = nativeLoopWindowId;
|
|
1260
1272
|
tab.threadId = threadId;
|
|
1261
1273
|
tab.project = run.cwd || tab.project;
|
|
@@ -16,7 +16,8 @@ import { bindExpandableCard, setCardExpanded } from './cdx-cards.js';
|
|
|
16
16
|
import { mountMcpAppCard } from './cdx-mcp-app-host.js';
|
|
17
17
|
import { OUTPUT_LIMITS, boundedText, jsonAnswer, prettyJsonFences, readableStructuredValue, fileChangeDiffs, parseUnifiedDiff, deduplicateTurnDiff } from './cdx-output-model.js';
|
|
18
18
|
import { renderReadableOutput, renderReadableBlock, cleanOutputBlocks, hydrateOutputControls, observeOutputBlocks, renderDiffView, restoreDiffViews } from './cdx-output.js';
|
|
19
|
-
import { fileLinkTitle, fillFileLinkCopy, isInsideRoots, parseFileLinkTarget } from '../file-links.js';
|
|
19
|
+
import { fileLinkTitle, fillFileLinkCopy, isInsideRoots, parseFileLinkTarget, readFileLink } from '../file-links.js';
|
|
20
|
+
import { sanitizeHtmlString } from '../assistant/asst-sanitize.js';
|
|
20
21
|
export { setCardExpanded } from './cdx-cards.js';
|
|
21
22
|
|
|
22
23
|
// ── Shared context (set by cdx-tabs via setRenderContext) ──
|
|
@@ -98,6 +99,11 @@ export function esc(s) {
|
|
|
98
99
|
// streaming delta tick. Memoize so identical text — unchanged messages
|
|
99
100
|
// elsewhere in the transcript that get re-rendered anyway — returns cached
|
|
100
101
|
// HTML without re-parsing.
|
|
102
|
+
//
|
|
103
|
+
// What a model writes is not ours, and marked passes raw HTML through: an
|
|
104
|
+
// `<img onerror>` in a reply would run in SynaBun's origin with no click. Every
|
|
105
|
+
// Markdown sink of the panel takes md()'s output, so it goes through the
|
|
106
|
+
// allowlist sanitiser the Assistant, Claude and OpenCode panels use, here, once.
|
|
101
107
|
const _mdCache = new Map();
|
|
102
108
|
const _MD_CACHE_LIMIT = 512;
|
|
103
109
|
let _mdRenderer = null;
|
|
@@ -134,6 +140,9 @@ export function md(text) {
|
|
|
134
140
|
try { html = parser.call(renderer, prettyJsonFences(key)); }
|
|
135
141
|
catch { html = markdownFallback(key); }
|
|
136
142
|
}
|
|
143
|
+
// A page with no DOM to sanitise in, or a sanitiser that fails, gets escaped text: never raw HTML.
|
|
144
|
+
try { html = sanitizeHtmlString(html); }
|
|
145
|
+
catch { html = markdownFallback(key); }
|
|
137
146
|
if (_mdCache.size >= _MD_CACHE_LIMIT) {
|
|
138
147
|
const firstKey = _mdCache.keys().next().value;
|
|
139
148
|
if (firstKey !== undefined) _mdCache.delete(firstKey);
|
|
@@ -409,8 +418,11 @@ function openInEditorTitle(target) {
|
|
|
409
418
|
|
|
410
419
|
export function markProjectFileAnchors(container) {
|
|
411
420
|
if (!container) return;
|
|
412
|
-
|
|
413
|
-
|
|
421
|
+
// A path the sanitiser kept as written (data-file-target) is the link's target: its href, when it has one, is
|
|
422
|
+
// that path resolved against the page.
|
|
423
|
+
container.querySelectorAll('a[href], a[data-file-target]').forEach((link) => {
|
|
424
|
+
const read = readFileLink(link);
|
|
425
|
+
const target = read && projectFileTarget(read.raw);
|
|
414
426
|
if (!target) return;
|
|
415
427
|
link.classList.add('cxp-file-link');
|
|
416
428
|
link.dataset.projectFilePath = target.path;
|
|
@@ -168,7 +168,15 @@ export function injectStyles() {
|
|
|
168
168
|
align-items: center;
|
|
169
169
|
background: var(--cxp-surface-1);
|
|
170
170
|
z-index: 3;
|
|
171
|
-
|
|
171
|
+
/* A query container measured in controls: 1em is one header button (24px
|
|
172
|
+
docked, the window's control size when detached, which follows the font
|
|
173
|
+
scale), so the thresholds at the end of this section hold at any scale.
|
|
174
|
+
Nothing in the row inherits it; the session menu takes the panel's back. */
|
|
175
|
+
container: cxp-header / inline-size;
|
|
176
|
+
font-size: 24px;
|
|
177
|
+
}
|
|
178
|
+
.sp-sidepanel.sp-floating > .cxp-header { font-size: var(--fw-header-control-size); }
|
|
179
|
+
.cxp-header > .cxp-session-menu { font-size: 1rem; }
|
|
172
180
|
.cxp-session-btn {
|
|
173
181
|
background: var(--cxp-surface-1);
|
|
174
182
|
border: none;
|
|
@@ -182,10 +190,14 @@ export function injectStyles() {
|
|
|
182
190
|
padding: 5px 10px;
|
|
183
191
|
border-radius: var(--cxp-radius-md) 0 0 var(--cxp-radius-md);
|
|
184
192
|
transition: background var(--cxp-dur-fast), color var(--cxp-dur-fast);
|
|
185
|
-
|
|
193
|
+
/* The session name keeps a readable 88px (about 8 characters, or its own
|
|
194
|
+
width if shorter) and gives way first above that: it grows only into the
|
|
195
|
+
room the pencil and the actions leave, so a long name never pushes them.
|
|
196
|
+
Narrower, the container queries let it go down to 32px (the caret). */
|
|
197
|
+
flex: 1 0 88px;
|
|
198
|
+
min-width: 32px;
|
|
199
|
+
max-width: max-content;
|
|
186
200
|
overflow: hidden;
|
|
187
|
-
flex-shrink: 1;
|
|
188
|
-
min-width: 0;
|
|
189
201
|
}
|
|
190
202
|
.cxp-session-btn:hover {
|
|
191
203
|
background: var(--cxp-surface-2);
|
|
@@ -231,6 +243,8 @@ export function injectStyles() {
|
|
|
231
243
|
cursor: pointer;
|
|
232
244
|
transition: background var(--cxp-dur-fast), color var(--cxp-dur-fast);
|
|
233
245
|
flex-shrink: 0;
|
|
246
|
+
/* The actions' own gap, kept when the name fills the row. */
|
|
247
|
+
margin-right: 3px;
|
|
234
248
|
}
|
|
235
249
|
.cxp-header-rename:hover:not(:disabled) {
|
|
236
250
|
color: var(--cxp-text);
|
|
@@ -256,8 +270,34 @@ export function injectStyles() {
|
|
|
256
270
|
margin-left: auto;
|
|
257
271
|
flex-shrink: 0;
|
|
258
272
|
}
|
|
259
|
-
/* The header account menu's trigger (account-menu.js)
|
|
273
|
+
/* The header buttons never shrink. The account menu's trigger (account-menu.js)
|
|
274
|
+
is the one piece of the actions that gives way, once the session name is at
|
|
275
|
+
its readable 88px, down to its compact 64px, and truncates. */
|
|
276
|
+
.cxp-actions > * { flex-shrink: 0; }
|
|
260
277
|
.cxp-actions:has(> .acct-menu-trigger) { flex-shrink: 1; min-width: 0; }
|
|
278
|
+
.cxp-actions > .acct-menu-trigger { flex-shrink: 1; }
|
|
279
|
+
/* Narrow headers, in order: the trigger stays at its compact 64px and the name
|
|
280
|
+
goes from 88px down to its caret; then the trigger is hidden (the cog's
|
|
281
|
+
Account row still opens its menu). Room counted in controls (em) plus px:
|
|
282
|
+
docked, the pencil and six buttons are 7em with 21px of gaps; detached, the
|
|
283
|
+
move handle makes 8em, the window's 6px gaps 39px, the name's floor is 1em.
|
|
284
|
+
Thresholds: name + 64px trigger + that row. */
|
|
285
|
+
@container cxp-header (width < calc(7em + 173px)) {
|
|
286
|
+
.codex-panel:not(.sp-floating) > .cxp-header .cxp-session-btn { flex: 1 1 0; }
|
|
287
|
+
.codex-panel:not(.sp-floating) > .cxp-header > .cxp-actions { flex-shrink: 0; }
|
|
288
|
+
.codex-panel:not(.sp-floating) > .cxp-header .acct-menu-trigger { flex: 0 0 64px; width: 64px; }
|
|
289
|
+
}
|
|
290
|
+
@container cxp-header (width < calc(7em + 117px)) {
|
|
291
|
+
.codex-panel:not(.sp-floating) > .cxp-header .acct-menu-trigger { display: none; }
|
|
292
|
+
}
|
|
293
|
+
@container cxp-header (width < calc(8em + 191px)) {
|
|
294
|
+
.codex-panel.sp-floating > .cxp-header .cxp-session-btn { flex: 1 1 0; }
|
|
295
|
+
.codex-panel.sp-floating > .cxp-header > .cxp-actions { flex-shrink: 0; }
|
|
296
|
+
.codex-panel.sp-floating > .cxp-header .acct-menu-trigger { flex: 0 0 64px; width: 64px; }
|
|
297
|
+
}
|
|
298
|
+
@container cxp-header (width < calc(9em + 103px)) {
|
|
299
|
+
.codex-panel.sp-floating > .cxp-header .acct-menu-trigger { display: none; }
|
|
300
|
+
}
|
|
261
301
|
.cxp-btn {
|
|
262
302
|
width: 24px;
|
|
263
303
|
height: 24px;
|
|
@@ -2684,6 +2724,10 @@ export function injectStyles() {
|
|
|
2684
2724
|
animation: cxp-working 1.6s ease-in-out infinite;
|
|
2685
2725
|
flex-shrink: 0;
|
|
2686
2726
|
}
|
|
2727
|
+
/* A session that waits on you (an approval or a question card): the warn hairline and mark of
|
|
2728
|
+
the Assistant's and a detached window's pills, in place of the working look (paintTrayPill). */
|
|
2729
|
+
.cxp-session-pill.cxp-pill-attention { border-color: color-mix(in srgb, var(--cxp-warning) 42%, transparent); }
|
|
2730
|
+
.cxp-session-pill.cxp-pill-attention .term-minimized-pill-icon { color: var(--cxp-warning); }
|
|
2687
2731
|
.cxp-hint {
|
|
2688
2732
|
margin-top: 8px;
|
|
2689
2733
|
font-size: var(--cxp-fs-xs);
|