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
|
@@ -204,7 +204,7 @@ export function createComposer(hostEl, hooks = {}) {
|
|
|
204
204
|
function renderAttachments() {
|
|
205
205
|
const chips = [];
|
|
206
206
|
state.images.forEach((img, i) => {
|
|
207
|
-
chips.push(`<span class="asst-chip static" data-kind="image" data-idx="${i}"><img src="data:${esc(img.mediaType)};base64,${img.base64}" alt=""><span class="asst-chip-text">${esc(img.name || 'image')}</span><button type="button" class="asst-chip-x" aria-label="${esc(t('common.remove', 'Remove'))}">${ICON_X}</button></span>`);
|
|
207
|
+
chips.push(`<span class="asst-chip static" data-kind="image" data-idx="${i}"><img src="data:${esc(img.mediaType)};base64,${esc(img.base64)}" alt=""><span class="asst-chip-text">${esc(img.name || 'image')}</span><button type="button" class="asst-chip-x" aria-label="${esc(t('common.remove', 'Remove'))}">${ICON_X}</button></span>`);
|
|
208
208
|
});
|
|
209
209
|
state.files.forEach((f, i) => {
|
|
210
210
|
chips.push(`<span class="asst-chip static" data-kind="file" data-idx="${i}"><span class="asst-chip-text">${esc(f.name)}</span><span class="asst-chip-meta">${esc(Math.ceil(f.content.length / 1024))}k</span><button type="button" class="asst-chip-x" aria-label="${esc(t('common.remove', 'Remove'))}">${ICON_X}</button></span>`);
|
|
@@ -54,7 +54,7 @@ export function describeBrainFallback(fallback) {
|
|
|
54
54
|
}
|
|
55
55
|
|
|
56
56
|
/**
|
|
57
|
-
* deps: { api: { list(), get(id), create({brain,label,fallback}) },
|
|
57
|
+
* deps: { api: { list(), get(id), create({brain,label,fallback,draft}), remove?(id) },
|
|
58
58
|
* isBlocked() → bool, toast(text), storedBrain() → brain|null,
|
|
59
59
|
* fallbackText(session.fallback) → text, defaultHost: 'terminal' }
|
|
60
60
|
*/
|
|
@@ -149,7 +149,9 @@ export function createAssistantHosts({
|
|
|
149
149
|
try {
|
|
150
150
|
// A model disabled since it was picked (Assistant → Models) must not
|
|
151
151
|
// lock the Assistant out: the server starts on its enabled stand-in.
|
|
152
|
-
|
|
152
|
+
// A draft: the server stores and lists it from its first prompt on (a prompt
|
|
153
|
+
// given here is that prompt); closed or left unused, nothing of it remains.
|
|
154
|
+
res = await api.create({ brain: b, label: label || undefined, fallback: true, draft: true });
|
|
153
155
|
} catch (err) {
|
|
154
156
|
say(`Assistant unavailable: ${err?.message || err}`);
|
|
155
157
|
return null;
|
|
@@ -245,6 +247,48 @@ export function createAssistantHosts({
|
|
|
245
247
|
return true;
|
|
246
248
|
}
|
|
247
249
|
|
|
250
|
+
/**
|
|
251
|
+
* A session nobody chatted in that the user left (they picked another one in its Sessions menu):
|
|
252
|
+
* its tab closes the way the user closes one, and the server removes it (it holds no conversation).
|
|
253
|
+
*/
|
|
254
|
+
function discard(sessionId) {
|
|
255
|
+
const owner = ownerOf(sessionId);
|
|
256
|
+
if (last?.sessionId === sessionId) last = null;
|
|
257
|
+
if (!owner) return false;
|
|
258
|
+
adapters.get(owner).close(sessionId, { closeServer: true });
|
|
259
|
+
return true;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
/**
|
|
263
|
+
* The Sessions menu's Remove: the server stops the session's running turn and workers and purges it,
|
|
264
|
+
* then its tab closes wherever it is open (other windows: the session-ended broadcast). `fresh`: it is
|
|
265
|
+
* the session showing where the user asked, and the host that has it gets a new one first, as New does
|
|
266
|
+
* (a host is never left empty in between). → { ok: true, stoppedRuns } | { ok: false, error }
|
|
267
|
+
*/
|
|
268
|
+
async function remove(sessionId, { host = null, fresh = false, brain = null } = {}) {
|
|
269
|
+
if (!sessionId || !api.remove) return { ok: false, error: 'unavailable' };
|
|
270
|
+
if (isBlocked()) return { ok: false, error: 'blocked' };
|
|
271
|
+
if (fresh) { try { await open({ host: host || ownerOf(sessionId), brain }); } catch { /* the removal goes on */ } }
|
|
272
|
+
let res = null;
|
|
273
|
+
try { res = await api.remove(sessionId); }
|
|
274
|
+
catch (err) { return { ok: false, error: err?.message || String(err) }; }
|
|
275
|
+
ended(sessionId);
|
|
276
|
+
return { ok: true, stoppedRuns: Array.isArray(res?.stoppedRuns) ? res.stoppedRuns : [] };
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
/**
|
|
280
|
+
* The server no longer has a session a host shows (a draft it dropped, a restart before its first
|
|
281
|
+
* prompt, a removal this page missed): its tab closes, and with `fresh` (it was showing and had
|
|
282
|
+
* no conversation) a new session takes its place in the host that had it, so the user never sees a dead tab.
|
|
283
|
+
*/
|
|
284
|
+
async function gone(sessionId, { host = null, fresh = false, brain = null } = {}) {
|
|
285
|
+
if (!sessionId) return null;
|
|
286
|
+
let opened = null;
|
|
287
|
+
if (fresh && !isBlocked()) { try { opened = await open({ host: host || ownerOf(sessionId), brain }); } catch { opened = null; } }
|
|
288
|
+
ended(sessionId);
|
|
289
|
+
return opened;
|
|
290
|
+
}
|
|
291
|
+
|
|
248
292
|
return {
|
|
249
293
|
register,
|
|
250
294
|
ownerOf,
|
|
@@ -253,6 +297,9 @@ export function createAssistantHosts({
|
|
|
253
297
|
toggle,
|
|
254
298
|
show,
|
|
255
299
|
ended,
|
|
300
|
+
discard,
|
|
301
|
+
remove,
|
|
302
|
+
gone,
|
|
256
303
|
noteFocus,
|
|
257
304
|
fetchLive,
|
|
258
305
|
isLive: isAssistantSessionLive,
|
|
@@ -4,14 +4,23 @@
|
|
|
4
4
|
// One body-level popover for every assistant menu (brain fields, model menu,
|
|
5
5
|
// sessions, "⋯", route mode). Appended to <body> so it escapes the viewport's
|
|
6
6
|
// paint containment; positioned with fixed coordinates next to its anchor and
|
|
7
|
-
//
|
|
7
|
+
// kept inside the Assistant that holds the anchor (the sidepanel, or a tab's own
|
|
8
|
+
// root) and the window. Only one menu is open at a time.
|
|
8
9
|
//
|
|
9
10
|
// openMenu(anchor, { title, items, placement:'auto'|'above'|'below'|'prefer-below', filter,
|
|
10
11
|
// width, role:'menu'|'listbox', label, className, onClose,
|
|
11
|
-
// zIndex (a menu opened from a layer above the panel's own, e.g. Settings)
|
|
12
|
+
// zIndex (a menu opened from a layer above the panel's own, e.g. Settings),
|
|
13
|
+
// footer, bounds, onEscape })
|
|
12
14
|
// 'prefer-below' opens under the anchor whenever a usable list fits there (the
|
|
13
15
|
// menu scrolls inside that room), even when more room is above: a menu opened
|
|
14
16
|
// from Settings must not cover the Settings header. The panel's menus use 'auto'.
|
|
17
|
+
// `footer`: items under the list that stay in view while the list scrolls.
|
|
18
|
+
// Every menu is measured at the width it is shown at, never wider than its box less the margins,
|
|
19
|
+
// and opens where that box has room (the list scrolls inside it). The box is the Assistant
|
|
20
|
+
// panel or root around the anchor, else the window.
|
|
21
|
+
// `bounds`: an element (or () => DOMRect) to use as that box instead: the menu is then exactly
|
|
22
|
+
// `width` wide (or the box's room) and follows the box on resize.
|
|
23
|
+
// `onEscape`: returns true when it handled Esc (e.g. went back a view): the menu stays open.
|
|
15
24
|
// Item kinds:
|
|
16
25
|
// item (default) { label, desc, icon, tag, disabled, danger, keywords, onSelect }
|
|
17
26
|
// radio { ...item, selected } → menuitemradio / option
|
|
@@ -21,10 +30,19 @@
|
|
|
21
30
|
// info { label, value } → non-interactive row
|
|
22
31
|
// tag { label } → non-interactive badge row
|
|
23
32
|
// input { placeholder, value, onSubmit } → inline text field
|
|
33
|
+
// buttons { label, buttons: [item…] } → one row of compact items (a group)
|
|
34
|
+
// `action: { icon, label, tooltip, id, onSelect }` on an item (or radio / check) adds one icon button at the
|
|
35
|
+
// row's end, shown on hover and keyboard focus (the Sessions menu's Remove). It is not a stop of ↑/↓: → from
|
|
36
|
+
// its row reaches it, ← goes back, Delete on the row presses it; choosing it never chooses the row and leaves
|
|
37
|
+
// the menu open (its onSelect usually redraws it with update()).
|
|
38
|
+
// `render(button, { checked })` on an item draws its content instead of label/desc/tag.
|
|
39
|
+
// `className` on an item or an info row adds its own class.
|
|
40
|
+
// `keepOpen` on an item leaves the menu open when it is chosen.
|
|
24
41
|
// `restoreFocus` on an item or input sends focus back to the anchor once it is chosen.
|
|
25
42
|
// Keys: ↑/↓ move, Home/End jump, Enter/Space activate, Esc closes (focus back
|
|
26
43
|
// to the anchor), Tab closes. The optional filter hides non-matching items and
|
|
27
|
-
// any group left empty.
|
|
44
|
+
// any group left empty. The handle's update({ title, items, footer, focus }) redraws
|
|
45
|
+
// an open menu in place (focus: the data-id to land on; the filter's text still applies).
|
|
28
46
|
|
|
29
47
|
let _open = null;
|
|
30
48
|
let _uid = 0;
|
|
@@ -87,11 +105,85 @@ export function currentMenu() {
|
|
|
87
105
|
return _open?.menu || null;
|
|
88
106
|
}
|
|
89
107
|
|
|
108
|
+
/** The element the open menu belongs to (null when none is open). */
|
|
109
|
+
export function currentMenuAnchor() {
|
|
110
|
+
return _open?.anchor || null;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function itemButton(item, kind, { role, list, onActivate }) {
|
|
114
|
+
const btn = el('button', `asst-dd-item${item.danger ? ' danger' : ''}${item.className ? ` ${item.className}` : ''}`);
|
|
115
|
+
btn.type = 'button';
|
|
116
|
+
btn.tabIndex = -1;
|
|
117
|
+
btn.dataset.kind = kind;
|
|
118
|
+
if (item.id != null) btn.dataset.id = String(item.id);
|
|
119
|
+
btn.dataset.search = `${item.label || ''} ${item.desc || ''} ${item.keywords || ''}`.toLowerCase();
|
|
120
|
+
const checked = kind === 'radio' ? !!item.selected : kind === 'check' ? !!item.checked : false;
|
|
121
|
+
if (role === 'listbox') {
|
|
122
|
+
btn.setAttribute('role', 'option');
|
|
123
|
+
btn.setAttribute('aria-selected', checked ? 'true' : 'false');
|
|
124
|
+
} else if (kind === 'radio') {
|
|
125
|
+
btn.setAttribute('role', 'menuitemradio');
|
|
126
|
+
btn.setAttribute('aria-checked', checked ? 'true' : 'false');
|
|
127
|
+
} else if (kind === 'check') {
|
|
128
|
+
btn.setAttribute('role', 'menuitemcheckbox');
|
|
129
|
+
btn.setAttribute('aria-checked', checked ? 'true' : 'false');
|
|
130
|
+
} else {
|
|
131
|
+
btn.setAttribute('role', role === 'listbox' ? 'option' : 'menuitem');
|
|
132
|
+
}
|
|
133
|
+
if (item.disabled) btn.disabled = true;
|
|
134
|
+
if (typeof item.render === 'function') {
|
|
135
|
+
item.render(btn, { checked });
|
|
136
|
+
} else {
|
|
137
|
+
const lead = kind === 'radio' || kind === 'check'
|
|
138
|
+
? `<span class="asst-dd-check" aria-hidden="true">${checked ? ICON_CHECK : ''}</span>`
|
|
139
|
+
: '';
|
|
140
|
+
btn.innerHTML = `${lead}${item.icon ? `<span class="asst-icon" aria-hidden="true"${item.color ? ` style="color:${esc(item.color)}"` : ''}>${item.icon}</span>` : ''}<span class="asst-dd-item-main"><span class="asst-dd-item-label">${esc(item.label)}</span>${item.desc ? `<span class="asst-dd-item-desc">${esc(item.desc)}</span>` : ''}</span>${item.tag ? `<span class="asst-dd-item-tag">${esc(item.tag)}</span>` : ''}${item.shortcut ? `<kbd class="asst-dd-kbd">${esc(item.shortcut)}</kbd>` : ''}`;
|
|
141
|
+
}
|
|
142
|
+
if (item.title) btn.title = item.title;
|
|
143
|
+
if (item.ariaLabel) btn.setAttribute('aria-label', item.ariaLabel);
|
|
144
|
+
btn.addEventListener('click', (e) => { e.stopPropagation(); if (!btn.disabled) onActivate(item, btn); });
|
|
145
|
+
btn.addEventListener('mousemove', () => {
|
|
146
|
+
if (btn.classList.contains('focused') || btn.disabled) return;
|
|
147
|
+
const menu = list.closest('.asst-dd-menu');
|
|
148
|
+
menu?.querySelectorAll('.asst-dd-item.focused').forEach(n => n.classList.remove('focused'));
|
|
149
|
+
btn.classList.add('focused');
|
|
150
|
+
// Hover never steals focus from the filter / inline input.
|
|
151
|
+
if (!document.activeElement?.classList?.contains('asst-dd-input')) { try { btn.focus({ preventScroll: true }); } catch { /* ignore */ } }
|
|
152
|
+
});
|
|
153
|
+
if (checked) btn.dataset.checked = '1';
|
|
154
|
+
return btn;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/** A row's own action (item.action): an icon button beside the row, outside the ↑/↓ cycle. */
|
|
158
|
+
function actionButton(action, item, { role }) {
|
|
159
|
+
const btn = el('button', `asst-iconbtn asst-dd-item-action${action.className ? ` ${action.className}` : ''}`);
|
|
160
|
+
btn.type = 'button';
|
|
161
|
+
btn.tabIndex = -1;
|
|
162
|
+
btn.setAttribute('role', role === 'listbox' ? 'option' : 'menuitem');
|
|
163
|
+
if (action.id != null) btn.dataset.id = String(action.id);
|
|
164
|
+
const label = String(action.label || '');
|
|
165
|
+
if (label) btn.setAttribute('aria-label', label);
|
|
166
|
+
if (action.tooltip || label) btn.dataset.tooltip = action.tooltip || label;
|
|
167
|
+
btn.innerHTML = action.icon || '';
|
|
168
|
+
btn.querySelector('svg')?.setAttribute('aria-hidden', 'true');
|
|
169
|
+
btn.addEventListener('click', (e) => { e.stopPropagation(); action.onSelect?.(item, btn); });
|
|
170
|
+
return btn;
|
|
171
|
+
}
|
|
172
|
+
|
|
90
173
|
function buildItems(list, items, { role, onActivate }) {
|
|
91
|
-
const itemRole = role === 'listbox' ? 'option' : 'menuitem';
|
|
92
174
|
for (const item of items || []) {
|
|
93
175
|
const kind = itemKind(item);
|
|
94
176
|
if (kind === 'separator') { list.appendChild(el('div', 'asst-dd-sep')).setAttribute('role', 'separator'); continue; }
|
|
177
|
+
if (kind === 'buttons') {
|
|
178
|
+
const row = el('div', `asst-dd-btnrow${item.className ? ` ${item.className}` : ''}`);
|
|
179
|
+
row.setAttribute('role', 'group');
|
|
180
|
+
if (item.id != null) row.dataset.id = String(item.id);
|
|
181
|
+
if (item.ariaLabel || item.label) row.setAttribute('aria-label', item.ariaLabel || item.label);
|
|
182
|
+
if (item.label) row.appendChild(el('span', 'asst-dd-btnrow-label', item.label)).setAttribute('aria-hidden', 'true');
|
|
183
|
+
for (const button of item.buttons || []) row.appendChild(itemButton(button, itemKind(button), { role, list, onActivate }));
|
|
184
|
+
list.appendChild(row);
|
|
185
|
+
continue;
|
|
186
|
+
}
|
|
95
187
|
if (kind === 'header') {
|
|
96
188
|
const h = el('div', 'asst-dd-group');
|
|
97
189
|
h.setAttribute('role', 'presentation');
|
|
@@ -100,7 +192,7 @@ function buildItems(list, items, { role, onActivate }) {
|
|
|
100
192
|
continue;
|
|
101
193
|
}
|
|
102
194
|
if (kind === 'info' || kind === 'tag') {
|
|
103
|
-
const row = el('div', kind === 'tag' ? 'asst-dd-tagrow' : 'asst-dd-info');
|
|
195
|
+
const row = el('div', `${kind === 'tag' ? 'asst-dd-tagrow' : 'asst-dd-info'}${item.className ? ` ${item.className}` : ''}`);
|
|
104
196
|
row.setAttribute('role', 'presentation');
|
|
105
197
|
row.innerHTML = kind === 'tag'
|
|
106
198
|
? `<span class="asst-dd-tag">${esc(item.label)}</span>`
|
|
@@ -121,42 +213,15 @@ function buildItems(list, items, { role, onActivate }) {
|
|
|
121
213
|
list.appendChild(input);
|
|
122
214
|
continue;
|
|
123
215
|
}
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
if (role === 'listbox') {
|
|
132
|
-
btn.setAttribute('role', 'option');
|
|
133
|
-
btn.setAttribute('aria-selected', checked ? 'true' : 'false');
|
|
134
|
-
} else if (kind === 'radio') {
|
|
135
|
-
btn.setAttribute('role', 'menuitemradio');
|
|
136
|
-
btn.setAttribute('aria-checked', checked ? 'true' : 'false');
|
|
137
|
-
} else if (kind === 'check') {
|
|
138
|
-
btn.setAttribute('role', 'menuitemcheckbox');
|
|
139
|
-
btn.setAttribute('aria-checked', checked ? 'true' : 'false');
|
|
140
|
-
} else {
|
|
141
|
-
btn.setAttribute('role', itemRole);
|
|
216
|
+
if (item.action && typeof item.action === 'object' && !item.disabled) {
|
|
217
|
+
const row = el('div', 'asst-dd-itemrow');
|
|
218
|
+
row.setAttribute('role', 'none');
|
|
219
|
+
row.appendChild(itemButton(item, kind, { role, list, onActivate }));
|
|
220
|
+
row.appendChild(actionButton(item.action, item, { role }));
|
|
221
|
+
list.appendChild(row);
|
|
222
|
+
continue;
|
|
142
223
|
}
|
|
143
|
-
|
|
144
|
-
const lead = kind === 'radio' || kind === 'check'
|
|
145
|
-
? `<span class="asst-dd-check" aria-hidden="true">${checked ? ICON_CHECK : ''}</span>`
|
|
146
|
-
: '';
|
|
147
|
-
btn.innerHTML = `${lead}${item.icon ? `<span class="asst-icon" aria-hidden="true"${item.color ? ` style="color:${esc(item.color)}"` : ''}>${item.icon}</span>` : ''}<span class="asst-dd-item-main"><span class="asst-dd-item-label">${esc(item.label)}</span>${item.desc ? `<span class="asst-dd-item-desc">${esc(item.desc)}</span>` : ''}</span>${item.tag ? `<span class="asst-dd-item-tag">${esc(item.tag)}</span>` : ''}${item.shortcut ? `<kbd class="asst-dd-kbd">${esc(item.shortcut)}</kbd>` : ''}`;
|
|
148
|
-
if (item.title) btn.title = item.title;
|
|
149
|
-
btn.addEventListener('click', (e) => { e.stopPropagation(); if (!btn.disabled) onActivate(item, btn); });
|
|
150
|
-
btn.addEventListener('mousemove', () => {
|
|
151
|
-
if (btn.classList.contains('focused') || btn.disabled) return;
|
|
152
|
-
const menu = list.closest('.asst-dd-menu');
|
|
153
|
-
menu?.querySelectorAll('.asst-dd-item.focused').forEach(n => n.classList.remove('focused'));
|
|
154
|
-
btn.classList.add('focused');
|
|
155
|
-
// Hover never steals focus from the filter / inline input.
|
|
156
|
-
if (!document.activeElement?.classList?.contains('asst-dd-input')) { try { btn.focus({ preventScroll: true }); } catch { /* ignore */ } }
|
|
157
|
-
});
|
|
158
|
-
if (checked) btn.dataset.checked = '1';
|
|
159
|
-
list.appendChild(btn);
|
|
224
|
+
list.appendChild(itemButton(item, kind, { role, list, onActivate }));
|
|
160
225
|
}
|
|
161
226
|
if (!list.querySelector('.asst-dd-item, .asst-dd-input, .asst-dd-info, .asst-dd-tagrow')) list.appendChild(el('div', 'asst-dd-empty', '—'));
|
|
162
227
|
}
|
|
@@ -167,6 +232,10 @@ export function applyMenuFilter(list, query, emptyLabel = 'No matches') {
|
|
|
167
232
|
const rows = [...list.children];
|
|
168
233
|
for (const row of rows) {
|
|
169
234
|
if (row.classList.contains('asst-dd-item')) row.hidden = !!q && !row.dataset.search.includes(q);
|
|
235
|
+
// A row with its own action: shown while its item matches.
|
|
236
|
+
else if (row.classList.contains('asst-dd-itemrow')) row.hidden = !!q && !(row.querySelector('.asst-dd-item')?.dataset.search || '').includes(q);
|
|
237
|
+
// A button row stays whole: shown while one of its buttons matches.
|
|
238
|
+
else if (row.classList.contains('asst-dd-btnrow')) row.hidden = !!q && ![...row.querySelectorAll('.asst-dd-item')].some(b => b.dataset.search.includes(q));
|
|
170
239
|
else if (row.classList.contains('asst-dd-info') || row.classList.contains('asst-dd-tagrow')) row.hidden = !!q;
|
|
171
240
|
}
|
|
172
241
|
// Group headers: visible only when an item until the next header/separator is visible.
|
|
@@ -192,7 +261,7 @@ export function applyMenuFilter(list, query, emptyLabel = 'No matches') {
|
|
|
192
261
|
const last = visible[visible.length - 1];
|
|
193
262
|
if (last?.classList.contains('asst-dd-sep')) last.hidden = true;
|
|
194
263
|
let empty = list.querySelector('.asst-dd-empty.asst-dd-noresults');
|
|
195
|
-
const hasItem = rows.some(r => !r.hidden && (r.classList.contains('asst-dd-item') || r.classList.contains('asst-dd-input')));
|
|
264
|
+
const hasItem = rows.some(r => !r.hidden && (r.classList.contains('asst-dd-item') || r.classList.contains('asst-dd-itemrow') || r.classList.contains('asst-dd-btnrow') || r.classList.contains('asst-dd-input')));
|
|
196
265
|
if (q && !hasItem) {
|
|
197
266
|
if (!empty) { empty = el('div', 'asst-dd-empty asst-dd-noresults', emptyLabel); list.appendChild(empty); }
|
|
198
267
|
empty.hidden = false;
|
|
@@ -204,7 +273,8 @@ const PREFER_BELOW_MIN = 220;
|
|
|
204
273
|
|
|
205
274
|
/**
|
|
206
275
|
* Whether a menu opens above its anchor (pure; sizes in px). `above` / `below`:
|
|
207
|
-
* the room on each side of the anchor
|
|
276
|
+
* the room on each side of the anchor; `anchorTop` / `viewportHeight`: the anchor's
|
|
277
|
+
* top in the box the menu stays inside, and that box's height.
|
|
208
278
|
*/
|
|
209
279
|
export function menuOpensAbove(placement, { anchorTop, anchorHeight, menuHeight, above, below, viewportHeight }) {
|
|
210
280
|
if (placement === 'above') return menuHeight <= above || above >= below;
|
|
@@ -214,37 +284,101 @@ export function menuOpensAbove(placement, { anchorTop, anchorHeight, menuHeight,
|
|
|
214
284
|
return !(menuHeight <= below || below >= above);
|
|
215
285
|
}
|
|
216
286
|
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
287
|
+
// A menu: never narrower than this while the window allows it; never shorter than these
|
|
288
|
+
// (a bounded menu holds a title and a footer around its list).
|
|
289
|
+
const BOUNDED_MIN_WIDTH = 248;
|
|
290
|
+
const MENU_MIN_HEIGHT = 96;
|
|
291
|
+
const BOUNDED_MIN_HEIGHT = 140;
|
|
292
|
+
|
|
293
|
+
/** The box a menu stays inside: its bounds clipped to the window (the window when it has none). */
|
|
294
|
+
function boundsBox(bounds) {
|
|
220
295
|
const vw = window.innerWidth;
|
|
221
296
|
const vh = window.innerHeight;
|
|
222
|
-
|
|
223
|
-
|
|
297
|
+
let r = null;
|
|
298
|
+
try { r = typeof bounds === 'function' ? bounds() : bounds?.getBoundingClientRect?.(); } catch { r = null; }
|
|
299
|
+
if (!r || !(r.width > 0) || !(r.height > 0)) return { left: 0, top: 0, right: vw, bottom: vh };
|
|
300
|
+
return { left: Math.max(0, r.left), top: Math.max(0, r.top), right: Math.min(vw, r.right), bottom: Math.min(vh, r.bottom) };
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
/** Where a menu without `bounds` stays: the Assistant around its anchor (the sidepanel, or a tab's own root). */
|
|
304
|
+
function assistantOf(anchor) {
|
|
305
|
+
return anchor.closest?.('.assistant-panel') || anchor.closest?.('.asst-root') || null;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
/** The widest a menu may be in its box (pure; px): the box less the margins, never under the minimum the window allows. */
|
|
309
|
+
export function menuRoomWidth({ box, viewportWidth, margin = 8 }) {
|
|
310
|
+
return Math.round(Math.max(box.right - box.left - margin * 2, Math.min(BOUNDED_MIN_WIDTH, viewportWidth - margin * 2)));
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
/** A bounded menu's width (pure; px): `width` at most, its box less the margins, never under the minimum the window allows. */
|
|
314
|
+
export function boundedMenuWidth({ box, viewportWidth, width, margin = 8 }) {
|
|
315
|
+
return Math.round(Math.max(Math.min(width || 320, box.right - box.left - margin * 2), Math.min(BOUNDED_MIN_WIDTH, viewportWidth - margin * 2)));
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
/**
|
|
319
|
+
* Where a menu goes (pure; px; rects as getBoundingClientRect gives them): under the anchor or
|
|
320
|
+
* above it as `placement` decides from the room inside the box (menuOpensAbove), as tall as that
|
|
321
|
+
* room; right-aligned with `alignX` (default: the anchor's right edge), or starting at the anchor's
|
|
322
|
+
* left edge with `alignLeft`; always inside the box and the window.
|
|
323
|
+
* → { left, top, maxHeight (null: its own height fits), up }.
|
|
324
|
+
*/
|
|
325
|
+
export function boundedMenuPlacement({ anchor, box, viewportWidth, viewportHeight, width, menuHeight, placement = 'below', alignX = null, alignLeft = false, minHeight = BOUNDED_MIN_HEIGHT, margin = 8, gap = 6 }) {
|
|
326
|
+
const below = box.bottom - margin - (anchor.bottom + gap);
|
|
327
|
+
const above = anchor.top - gap - (box.top + margin);
|
|
328
|
+
const up = menuOpensAbove(placement, { anchorTop: anchor.top - box.top, anchorHeight: anchor.bottom - anchor.top, menuHeight, above, below, viewportHeight: box.bottom - box.top });
|
|
329
|
+
const room = Math.max(minHeight, Math.floor(up ? above : below));
|
|
330
|
+
const height = Math.min(menuHeight, room);
|
|
331
|
+
let top = up ? anchor.top - gap - height : anchor.bottom + gap;
|
|
332
|
+
top = Math.max(margin, Math.min(top, viewportHeight - margin - height));
|
|
333
|
+
let left = alignLeft ? anchor.left : (alignX ?? anchor.right) - width;
|
|
334
|
+
left = Math.min(left, box.right - margin - width);
|
|
335
|
+
left = Math.max(left, box.left + margin);
|
|
336
|
+
left = Math.max(margin, Math.min(left, viewportWidth - margin - width));
|
|
337
|
+
return { left: Math.round(left), top: Math.round(top), maxHeight: menuHeight > room ? room : null, up };
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
/**
|
|
341
|
+
* Place a menu inside its box: its width is final before it is measured (the CSS caps read the
|
|
342
|
+
* box's room through --asst-dd-room; a bounded menu is set to its width), so what is shown is what
|
|
343
|
+
* was placed; then boundedMenuPlacement. The list scrolls inside, title and footer stay.
|
|
344
|
+
*/
|
|
345
|
+
function positionMenu(menu, anchor, { placement, bounds, width }) {
|
|
346
|
+
const viewportWidth = window.innerWidth;
|
|
347
|
+
const viewportHeight = window.innerHeight;
|
|
348
|
+
const box = boundsBox(bounds || assistantOf(anchor));
|
|
349
|
+
if (bounds) {
|
|
350
|
+
menu.style.minWidth = '0';
|
|
351
|
+
menu.style.maxWidth = 'none';
|
|
352
|
+
menu.style.width = `${boundedMenuWidth({ box, viewportWidth, width })}px`;
|
|
353
|
+
menu.style.maxHeight = 'none';
|
|
354
|
+
} else {
|
|
355
|
+
menu.style.setProperty('--asst-dd-room', `${menuRoomWidth({ box, viewportWidth })}px`);
|
|
356
|
+
menu.style.maxHeight = '';
|
|
357
|
+
}
|
|
224
358
|
menu.style.left = '0px';
|
|
225
359
|
menu.style.top = '0px';
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
const
|
|
229
|
-
const
|
|
230
|
-
const
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
if (
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
360
|
+
// The sidepanel hugs the right screen edge: its menus grow leftward from the click point (or the
|
|
361
|
+
// anchor's right edge for keyboard opens), as a bounded menu does; elsewhere a menu starts at its anchor.
|
|
362
|
+
const inSidepanel = !!anchor.closest?.('.assistant-panel');
|
|
363
|
+
const clicked = inSidepanel && _lastDown && anchor.contains(_lastDown.target) && performance.now() - _lastDown.at < 1500;
|
|
364
|
+
const spot = boundedMenuPlacement({
|
|
365
|
+
anchor: anchor.getBoundingClientRect(), box, viewportWidth, viewportHeight,
|
|
366
|
+
width: menu.offsetWidth, menuHeight: menu.offsetHeight, placement,
|
|
367
|
+
alignX: clicked ? _lastDown.x : null, alignLeft: !bounds && !inSidepanel,
|
|
368
|
+
minHeight: bounds ? BOUNDED_MIN_HEIGHT : MENU_MIN_HEIGHT,
|
|
369
|
+
});
|
|
370
|
+
if (spot.maxHeight != null) menu.style.maxHeight = `${spot.maxHeight}px`;
|
|
371
|
+
menu.style.left = `${spot.left}px`;
|
|
372
|
+
menu.style.top = `${spot.top}px`;
|
|
373
|
+
menu.dataset.placement = spot.up ? 'above' : 'below';
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
/** A row its menu's width cuts short (one-line label or description) keeps its words in a tooltip. */
|
|
377
|
+
function titleCutRows(menu) {
|
|
378
|
+
for (const btn of menu.querySelectorAll('.asst-dd-item:not([title])')) {
|
|
379
|
+
const parts = [...btn.querySelectorAll('.asst-dd-item-label, .asst-dd-item-desc')];
|
|
380
|
+
if (parts.some(n => n.scrollWidth > n.clientWidth + 1)) btn.title = parts.map(n => n.textContent).filter(Boolean).join(' · ');
|
|
241
381
|
}
|
|
242
|
-
if (left + mw > vw - margin) left = vw - margin - mw;
|
|
243
|
-
left = Math.max(margin, left);
|
|
244
|
-
menu.style.left = `${Math.round(left)}px`;
|
|
245
|
-
menu.style.top = `${Math.round(top)}px`;
|
|
246
|
-
menu.style.maxWidth = `${vw - margin * 2}px`;
|
|
247
|
-
menu.dataset.placement = up ? 'above' : 'below';
|
|
248
382
|
}
|
|
249
383
|
|
|
250
384
|
/**
|
|
@@ -262,8 +396,9 @@ export function openMenu(anchor, opts = {}) {
|
|
|
262
396
|
menu.setAttribute('role', role);
|
|
263
397
|
menu.id = `asst-menu-${++_uid}`;
|
|
264
398
|
menu.tabIndex = -1;
|
|
399
|
+
let heading = null;
|
|
265
400
|
if (title) {
|
|
266
|
-
|
|
401
|
+
heading = el('div', 'asst-dd-menu-title', title);
|
|
267
402
|
heading.id = `${menu.id}-title`;
|
|
268
403
|
menu.setAttribute('aria-labelledby', heading.id);
|
|
269
404
|
menu.appendChild(heading);
|
|
@@ -290,13 +425,25 @@ export function openMenu(anchor, opts = {}) {
|
|
|
290
425
|
};
|
|
291
426
|
if (opts.content) list.appendChild(opts.content);
|
|
292
427
|
else buildItems(list, items, { role, onActivate: activate });
|
|
293
|
-
|
|
294
|
-
|
|
428
|
+
// The footer stays under the list: only the list scrolls.
|
|
429
|
+
let foot = null;
|
|
430
|
+
const fillFooter = (footer) => {
|
|
431
|
+
if (!foot) { foot = el('div', 'asst-dd-foot'); menu.appendChild(foot); }
|
|
432
|
+
foot.replaceChildren();
|
|
433
|
+
if (Array.isArray(footer) && footer.length) buildItems(foot, footer, { role, onActivate: activate });
|
|
434
|
+
foot.hidden = !(Array.isArray(footer) && footer.length);
|
|
435
|
+
};
|
|
436
|
+
if (Array.isArray(opts.footer)) fillFooter(opts.footer);
|
|
437
|
+
// `width`: at least that wide (exactly, with repositionOnResize), never wider than the room positionMenu sets.
|
|
438
|
+
const capped = `min(${width}px, var(--asst-dd-room, calc(100vw - 16px)))`;
|
|
439
|
+
if (width) menu.style.minWidth = capped;
|
|
440
|
+
if (width && opts.repositionOnResize) menu.style.width = capped;
|
|
295
441
|
document.body.appendChild(menu);
|
|
296
|
-
positionMenu(menu, anchor, placement);
|
|
442
|
+
const place = () => { positionMenu(menu, anchor, { placement, bounds: opts.bounds || null, width }); titleCutRows(menu); };
|
|
443
|
+
place();
|
|
297
444
|
|
|
298
445
|
if (filterInput) {
|
|
299
|
-
filterInput.addEventListener('input', () => { applyMenuFilter(list, filterInput.value, opts.filterEmptyLabel);
|
|
446
|
+
filterInput.addEventListener('input', () => { applyMenuFilter(list, filterInput.value, opts.filterEmptyLabel); place(); });
|
|
300
447
|
}
|
|
301
448
|
|
|
302
449
|
const onDocDown = (e) => {
|
|
@@ -310,8 +457,15 @@ export function openMenu(anchor, opts = {}) {
|
|
|
310
457
|
const target = e.target;
|
|
311
458
|
const inInput = target?.classList?.contains('asst-dd-input');
|
|
312
459
|
const nodes = focusables(menu);
|
|
313
|
-
|
|
314
|
-
|
|
460
|
+
// On a row's action (item.action) the keys move from its row.
|
|
461
|
+
const onAction = document.activeElement?.classList?.contains('asst-dd-item-action') && menu.contains(document.activeElement) ? document.activeElement : null;
|
|
462
|
+
const idx = nodes.indexOf(onAction ? onAction.previousElementSibling : document.activeElement);
|
|
463
|
+
if (e.key === 'Escape') {
|
|
464
|
+
e.preventDefault(); e.stopPropagation();
|
|
465
|
+
if (typeof opts.onEscape === 'function' && opts.onEscape() === true) return;
|
|
466
|
+
closeMenu({ restoreFocus: true });
|
|
467
|
+
return;
|
|
468
|
+
}
|
|
315
469
|
if (role === 'dialog') {
|
|
316
470
|
if (e.key === 'Tab') {
|
|
317
471
|
const fields = [...menu.querySelectorAll('button, input')].filter(node => !node.disabled && !node.hidden);
|
|
@@ -330,6 +484,18 @@ export function openMenu(anchor, opts = {}) {
|
|
|
330
484
|
setFocused(menu, nodes[next]);
|
|
331
485
|
return;
|
|
332
486
|
}
|
|
487
|
+
if (!inInput && (e.key === 'ArrowRight' || e.key === 'ArrowLeft' || e.key === 'Delete')) {
|
|
488
|
+
const row = onAction ? null : document.activeElement?.closest?.('.asst-dd-item');
|
|
489
|
+
const action = onAction || (row?.parentElement?.classList.contains('asst-dd-itemrow') ? row.nextElementSibling : null);
|
|
490
|
+
if (action?.classList.contains('asst-dd-item-action')) {
|
|
491
|
+
e.preventDefault(); e.stopPropagation();
|
|
492
|
+
const main = action.previousElementSibling;
|
|
493
|
+
if (e.key === 'ArrowLeft') { if (onAction) setFocused(menu, main); return; }
|
|
494
|
+
if (!onAction) { setFocused(menu, main); try { action.focus({ preventScroll: true }); } catch { action.focus(); } }
|
|
495
|
+
if (e.key === 'Delete') action.click();
|
|
496
|
+
return;
|
|
497
|
+
}
|
|
498
|
+
}
|
|
333
499
|
if ((e.key === 'Home' || e.key === 'End') && !inInput) {
|
|
334
500
|
e.preventDefault(); e.stopPropagation();
|
|
335
501
|
if (nodes.length) setFocused(menu, nodes[e.key === 'Home' ? 0 : nodes.length - 1]);
|
|
@@ -347,6 +513,7 @@ export function openMenu(anchor, opts = {}) {
|
|
|
347
513
|
return;
|
|
348
514
|
}
|
|
349
515
|
if ((e.key === 'Enter' || e.key === ' ') && !inInput) {
|
|
516
|
+
if (onAction) { e.preventDefault(); e.stopPropagation(); onAction.click(); return; }
|
|
350
517
|
const current = document.activeElement?.closest?.('.asst-dd-item');
|
|
351
518
|
if (current && menu.contains(current)) { e.preventDefault(); e.stopPropagation(); current.click(); }
|
|
352
519
|
return;
|
|
@@ -354,7 +521,7 @@ export function openMenu(anchor, opts = {}) {
|
|
|
354
521
|
// Keep single-letter global keybinds out of an open menu.
|
|
355
522
|
e.stopPropagation();
|
|
356
523
|
};
|
|
357
|
-
const onResize = () => opts.repositionOnResize ?
|
|
524
|
+
const onResize = () => (opts.repositionOnResize || opts.bounds ? place() : closeMenu());
|
|
358
525
|
const onBlur = () => closeMenu();
|
|
359
526
|
document.addEventListener('mousedown', onDocDown, true);
|
|
360
527
|
const keyTarget = role === 'dialog' ? window : document;
|
|
@@ -381,7 +548,38 @@ export function openMenu(anchor, opts = {}) {
|
|
|
381
548
|
} else {
|
|
382
549
|
setFocused(menu, selected || first || null);
|
|
383
550
|
}
|
|
384
|
-
|
|
551
|
+
// Redraw in place (another view of the same menu): the anchor, the listeners and the focus stay in it.
|
|
552
|
+
// `keepPlace` (fresher data for the same view, read in the background): the scroll position, the row the
|
|
553
|
+
// keyboard is on and the filter's text and focus are left where the user had them.
|
|
554
|
+
const update = (next = {}) => {
|
|
555
|
+
if (!_open || _open.menu !== menu) return;
|
|
556
|
+
const keep = next.keepPlace === true;
|
|
557
|
+
const scrollBefore = list.scrollTop;
|
|
558
|
+
const active = document.activeElement;
|
|
559
|
+
const typing = !!active && active.classList?.contains('asst-dd-input') && menu.contains(active);
|
|
560
|
+
const onRowId = !typing ? (menu.querySelector('.asst-dd-item.focused')?.dataset?.id ?? null) : null;
|
|
561
|
+
if (typeof next.title === 'string' && heading) heading.textContent = next.title;
|
|
562
|
+
if (Array.isArray(next.items)) {
|
|
563
|
+
list.replaceChildren();
|
|
564
|
+
buildItems(list, next.items, { role, onActivate: activate });
|
|
565
|
+
if (filterInput?.value) applyMenuFilter(list, filterInput.value, opts.filterEmptyLabel);
|
|
566
|
+
list.scrollTop = keep ? scrollBefore : 0;
|
|
567
|
+
}
|
|
568
|
+
if ('footer' in next) fillFooter(next.footer);
|
|
569
|
+
place();
|
|
570
|
+
if (keep && typing) {
|
|
571
|
+
// The input keeps the caret; the row highlight follows the selection as it did when it opened.
|
|
572
|
+
menu.querySelectorAll('.asst-dd-item.focused').forEach(n => n.classList.remove('focused'));
|
|
573
|
+
list.querySelector('.asst-dd-item[data-checked="1"]:not([disabled])')?.classList.add('focused');
|
|
574
|
+
return;
|
|
575
|
+
}
|
|
576
|
+
const keptRow = keep && onRowId != null ? [...menu.querySelectorAll('.asst-dd-item')].find(n => n.dataset.id === onRowId && !n.disabled) : null;
|
|
577
|
+
const wanted = keptRow || (next.focus != null ? [...menu.querySelectorAll('.asst-dd-item')].find(n => n.dataset.id === String(next.focus) && !n.disabled) : null);
|
|
578
|
+
if (keep && !keptRow && !menu.contains(document.activeElement)) return; // focus is elsewhere: not taken back
|
|
579
|
+
setFocused(menu, wanted || list.querySelector('.asst-dd-item[data-checked="1"]:not([disabled])') || focusables(menu)[0] || null);
|
|
580
|
+
if (keep) list.scrollTop = scrollBefore;
|
|
581
|
+
};
|
|
582
|
+
return { el: menu, list, close: closeMenu, reposition: place, update };
|
|
385
583
|
}
|
|
386
584
|
|
|
387
585
|
/** Toggle helper for anchor buttons: closes when this anchor's menu is open. */
|