synabun 2.0.5 → 2.0.9
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 +78 -0
- package/README.md +2 -1
- package/THIRD-PARTY-LICENSES.md +13 -0
- package/docs/whatsapp.md +151 -16
- package/hooks/claude-code/post-plan.mjs +6 -30
- package/hooks/claude-code/session-start.mjs +1 -4
- package/hooks/claude-code/shared.mjs +112 -75
- package/lib/child-path.js +303 -0
- package/lib/claude-hooks.js +2 -1
- package/lib/data-home-migration.js +2 -0
- package/lib/desktop-app.js +265 -0
- package/lib/external-tools.js +7 -3
- package/lib/log-redact.js +102 -0
- package/lib/package-processes.js +670 -0
- package/lib/packaged-runtime.js +138 -11
- package/lib/runtime-dependencies.js +225 -0
- package/mcp-server/dist/http.d.ts +3 -1
- package/mcp-server/dist/http.d.ts.map +1 -1
- package/mcp-server/dist/http.js +15 -4
- package/mcp-server/dist/http.js.map +1 -1
- package/mcp-server/dist/index.d.ts.map +1 -1
- package/mcp-server/dist/index.js +9 -6
- package/mcp-server/dist/index.js.map +1 -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/budget-judgments.d.ts +108 -0
- package/mcp-server/dist/services/budget-judgments.d.ts.map +1 -0
- package/mcp-server/dist/services/budget-judgments.js +153 -0
- package/mcp-server/dist/services/budget-judgments.js.map +1 -0
- 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/codex-config-heal.d.ts +5 -1
- package/mcp-server/dist/services/codex-config-heal.d.ts.map +1 -1
- package/mcp-server/dist/services/codex-config-heal.js +41 -4
- package/mcp-server/dist/services/codex-config-heal.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/services/identity.d.ts +11 -0
- package/mcp-server/dist/services/identity.d.ts.map +1 -1
- package/mcp-server/dist/services/identity.js +24 -0
- package/mcp-server/dist/services/identity.js.map +1 -1
- package/mcp-server/dist/services/neural-interface.d.ts +13 -0
- package/mcp-server/dist/services/neural-interface.d.ts.map +1 -1
- package/mcp-server/dist/services/neural-interface.js +34 -2
- package/mcp-server/dist/services/neural-interface.js.map +1 -1
- package/mcp-server/dist/services/typesafe-config.d.ts +1 -1
- package/mcp-server/dist/services/typesafe-config.d.ts.map +1 -1
- package/mcp-server/dist/services/typesafe-config.js +4 -1
- package/mcp-server/dist/services/typesafe-config.js.map +1 -1
- package/mcp-server/dist/tools/agents.d.ts +11 -8
- package/mcp-server/dist/tools/agents.d.ts.map +1 -1
- package/mcp-server/dist/tools/agents.js +35 -14
- 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 +17 -0
- package/mcp-server/dist/tools/whatsapp-send-file.d.ts.map +1 -0
- package/mcp-server/dist/tools/whatsapp-send-file.js +44 -0
- package/mcp-server/dist/tools/whatsapp-send-file.js.map +1 -0
- 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 +18 -5
- package/mcp-server/dist/tui.js.map +1 -1
- package/mcp-server/dist/version.d.ts +5 -0
- package/mcp-server/dist/version.d.ts.map +1 -0
- package/mcp-server/dist/version.js +21 -0
- package/mcp-server/dist/version.js.map +1 -0
- package/mcp-server/package-lock.json +3 -3
- package/mcp-server/package.json +3 -3
- package/mcp-server/run.mjs +15 -9
- package/mcp-server/src/http.ts +18 -3
- package/mcp-server/src/index.ts +9 -6
- package/mcp-server/src/services/browser-bench.ts +1 -1
- package/mcp-server/src/services/budget-judgments.ts +183 -0
- package/mcp-server/src/services/categories.ts +77 -32
- package/mcp-server/src/services/codex-config-heal.ts +35 -4
- package/mcp-server/src/services/desktop-bench.ts +1 -1
- package/mcp-server/src/services/identity.ts +27 -0
- package/mcp-server/src/services/neural-interface.ts +34 -2
- package/mcp-server/src/services/typesafe-config.ts +4 -1
- package/mcp-server/src/tools/agents.ts +34 -15
- 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 +51 -0
- 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 +24 -10
- package/mcp-server/src/version.ts +22 -0
- package/neural-interface/i18n/en.json +645 -57
- package/neural-interface/i18n/pt-BR.json +644 -56
- package/neural-interface/lib/account-usage.js +163 -0
- package/neural-interface/lib/agent-sandbox-elevation.js +102 -0
- package/neural-interface/lib/agent-sandbox-setting.js +57 -0
- package/neural-interface/lib/assistant-api.js +274 -52
- package/neural-interface/lib/assistant-brains/claude.js +15 -2
- package/neural-interface/lib/assistant-brains/codex.js +3 -0
- package/neural-interface/lib/assistant-brains/opencode.js +17 -4
- package/neural-interface/lib/assistant-budget-planner.js +390 -0
- package/neural-interface/lib/assistant-budget-split.js +408 -0
- package/neural-interface/lib/assistant-budget.js +94 -95
- package/neural-interface/lib/assistant-config.js +62 -14
- package/neural-interface/lib/assistant-dispatch.js +432 -125
- package/neural-interface/lib/assistant-jev.js +26 -3
- package/neural-interface/lib/assistant-persona.js +68 -11
- package/neural-interface/lib/assistant-plan-permissions.js +8 -1
- package/neural-interface/lib/assistant-route-accounts.js +14 -0
- package/neural-interface/lib/assistant-route-gate-codex.js +3 -1
- package/neural-interface/lib/assistant-route-gate.js +19 -3
- package/neural-interface/lib/assistant-router.js +373 -42
- package/neural-interface/lib/assistant-runtime.js +312 -48
- package/neural-interface/lib/assistant-user-writer.js +4 -2
- package/neural-interface/lib/augmented-path.js +317 -53
- package/neural-interface/lib/claude-accounts.js +342 -31
- package/neural-interface/lib/claude-agent-bridge.js +90 -3
- package/neural-interface/lib/claude-executable.js +6 -1
- package/neural-interface/lib/claude-model-catalog.js +3 -3
- package/neural-interface/lib/claude-panel-auth.js +221 -0
- package/neural-interface/lib/claude-profile-api-key.js +62 -0
- package/neural-interface/lib/cli-install-api.js +137 -0
- package/neural-interface/lib/cli-install.js +797 -0
- package/neural-interface/lib/cli-path.js +130 -0
- package/neural-interface/lib/cli-resolver.js +744 -0
- package/neural-interface/lib/cli-update-planner.js +143 -7
- package/neural-interface/lib/codex-account-auth.js +180 -0
- package/neural-interface/lib/codex-accounts.js +502 -0
- package/neural-interface/lib/codex-capabilities.js +2 -1
- package/neural-interface/lib/codex-compat-rpc.js +8 -2
- package/neural-interface/lib/codex-mcp-approvals.js +5 -1
- package/neural-interface/lib/codex-panel-auth.js +62 -0
- package/neural-interface/lib/codex-session-settings.js +1 -1
- package/neural-interface/lib/codex-settings-schema.js +122 -0
- package/neural-interface/lib/desktop/config.js +3 -0
- package/neural-interface/lib/exec-loop-command.js +3 -1
- package/neural-interface/lib/git-bash/bzip2.js +339 -0
- package/neural-interface/lib/git-bash/claude-settings.js +73 -0
- package/neural-interface/lib/git-bash/extract-worker.js +34 -0
- package/neural-interface/lib/git-bash/install.js +529 -0
- package/neural-interface/lib/git-bash/licenses/COPYING +360 -0
- package/neural-interface/lib/git-bash/licenses/LGPL-2.1 +511 -0
- package/neural-interface/lib/git-bash/manager.js +268 -0
- package/neural-interface/lib/git-bash/pins.js +206 -0
- package/neural-interface/lib/git-bash/pins.json +34 -0
- package/neural-interface/lib/git-bash/resolver.js +194 -0
- package/neural-interface/lib/git-read.js +34 -2
- package/neural-interface/lib/install-details.js +163 -0
- package/neural-interface/lib/mcp-installer.js +14 -2
- package/neural-interface/lib/native-loop-providers.js +22 -4
- package/neural-interface/lib/native-loop-runtime.js +9 -4
- package/neural-interface/lib/os-path.js +319 -0
- package/neural-interface/lib/provider-panel-api-key-auth.js +92 -0
- package/neural-interface/lib/provider-panel-terminal-auth.js +96 -0
- package/neural-interface/lib/pty-host/core.js +2 -1
- package/neural-interface/lib/pty-host/manager.js +27 -2
- package/neural-interface/lib/remote-policy.js +20 -11
- package/neural-interface/lib/session-indexer.js +3 -1
- package/neural-interface/lib/session-title-generator.js +6 -3
- package/neural-interface/lib/system-about.js +340 -0
- package/neural-interface/lib/system-activity.js +66 -0
- package/neural-interface/lib/terminal-env.js +11 -3
- package/neural-interface/lib/terminal-path.js +55 -0
- package/neural-interface/lib/virtual-socket.js +12 -1
- package/neural-interface/lib/whatsapp/baileys-adapter.js +165 -45
- package/neural-interface/lib/whatsapp/bridge.js +78 -34
- package/neural-interface/lib/whatsapp/commands.js +13 -14
- package/neural-interface/lib/whatsapp/config.js +134 -20
- package/neural-interface/lib/whatsapp/fake-baileys.js +99 -14
- package/neural-interface/lib/whatsapp/host-core.js +168 -35
- package/neural-interface/lib/whatsapp/inbound.js +5 -3
- package/neural-interface/lib/whatsapp/local-file.js +524 -0
- package/neural-interface/lib/whatsapp/manager.js +92 -32
- package/neural-interface/lib/whatsapp/paths.js +11 -1
- package/neural-interface/lib/whatsapp/protocol.js +65 -5
- package/neural-interface/lib/whatsapp/redact.js +63 -0
- package/neural-interface/lib/whatsapp/service.js +343 -91
- package/neural-interface/lib/whatsapp/transcription.js +463 -0
- package/neural-interface/package-lock.json +2 -2
- package/neural-interface/package.json +1 -1
- package/neural-interface/public/index.html +2 -0
- package/neural-interface/public/index2d.html +2 -0
- package/neural-interface/public/offline.html +16 -1
- package/neural-interface/public/onboarding.html +96 -2
- package/neural-interface/public/shared/account-i18n.js +7 -0
- package/neural-interface/public/shared/account-menu.js +542 -0
- package/neural-interface/public/shared/account-usage.js +79 -0
- package/neural-interface/public/shared/account-view.js +336 -0
- package/neural-interface/public/shared/agent-runtime-options.js +28 -8
- package/neural-interface/public/shared/assistant/asst-account-menu.js +298 -0
- package/neural-interface/public/shared/assistant/asst-accounts.js +182 -184
- package/neural-interface/public/shared/assistant/asst-api.js +39 -2
- package/neural-interface/public/shared/assistant/asst-brain-picker.js +288 -32
- package/neural-interface/public/shared/assistant/asst-budget.js +259 -303
- package/neural-interface/public/shared/assistant/asst-mascot.js +126 -7
- package/neural-interface/public/shared/assistant/asst-menu.js +219 -80
- package/neural-interface/public/shared/assistant/asst-model-chip.js +135 -0
- package/neural-interface/public/shared/assistant/asst-panel.js +87 -16
- package/neural-interface/public/shared/assistant/asst-render.js +38 -7
- package/neural-interface/public/shared/assistant/asst-route.js +72 -14
- package/neural-interface/public/shared/assistant/asst-routes-editor.js +45 -6
- package/neural-interface/public/shared/assistant/asst-slash.js +1 -1
- package/neural-interface/public/shared/assistant/asst-state.js +1 -1
- package/neural-interface/public/shared/assistant/asst-styles.js +160 -29
- package/neural-interface/public/shared/cdx/cdx-commands.js +4 -1
- package/neural-interface/public/shared/cdx/cdx-features.js +22 -14
- package/neural-interface/public/shared/cdx/cdx-icons.js +2 -0
- package/neural-interface/public/shared/cdx/cdx-panel.js +49 -108
- package/neural-interface/public/shared/cdx/cdx-settings.js +231 -0
- package/neural-interface/public/shared/cdx/cdx-styles.js +172 -111
- package/neural-interface/public/shared/cdx/cdx-tabs.js +264 -664
- package/neural-interface/public/shared/cdx/cdx-ws.js +2 -0
- package/neural-interface/public/shared/claude-account-view.js +3 -0
- package/neural-interface/public/shared/cli-install-view.js +392 -0
- package/neural-interface/public/shared/cli-status.js +282 -22
- package/neural-interface/public/shared/cp/cp-events.js +2 -1
- package/neural-interface/public/shared/cp/cp-styles.js +46 -0
- package/neural-interface/public/shared/desktop-bridge.js +39 -0
- package/neural-interface/public/shared/desktop-titlebar-page.js +59 -0
- package/neural-interface/public/shared/desktop-titlebar-view.js +122 -0
- package/neural-interface/public/shared/desktop-titlebar.js +153 -0
- package/neural-interface/public/shared/desktop-update-view.js +462 -0
- package/neural-interface/public/shared/editor/editor-panel.js +1 -1
- package/neural-interface/public/shared/explorer/fx-main.js +1 -1
- package/neural-interface/public/shared/git-bash-view.js +145 -0
- package/neural-interface/public/shared/html-shell.js +1 -0
- package/neural-interface/public/shared/i18n.js +19 -1
- package/neural-interface/public/shared/install-details-view.js +86 -0
- package/neural-interface/public/shared/ocp-v2/ocp-v2-childpanel.js +4 -1
- package/neural-interface/public/shared/ocp-v2/ocp-v2-panel.js +36 -1
- package/neural-interface/public/shared/ocp-v2/ocp-v2-send.js +17 -0
- package/neural-interface/public/shared/ocp-v2/ocp-v2-styles.js +169 -2
- package/neural-interface/public/shared/pwa-install-page.js +3 -1
- package/neural-interface/public/shared/settings/settings-about-pane.js +271 -0
- package/neural-interface/public/shared/settings/settings-about.js +254 -0
- package/neural-interface/public/shared/settings/settings-ia.js +15 -0
- package/neural-interface/public/shared/showcase-privacy.js +490 -0
- package/neural-interface/public/shared/storage.js +30 -3
- package/neural-interface/public/shared/styles.css +281 -32
- package/neural-interface/public/shared/synabun-mascot.js +48 -2
- package/neural-interface/public/shared/ui-claude-panel.js +262 -217
- package/neural-interface/public/shared/ui-cli-banner.js +355 -0
- package/neural-interface/public/shared/ui-explorer.js +2 -0
- package/neural-interface/public/shared/ui-git-bash.js +124 -0
- package/neural-interface/public/shared/ui-judgments.js +10 -1
- package/neural-interface/public/shared/ui-loading.js +30 -6
- package/neural-interface/public/shared/ui-menubar.js +13 -3
- package/neural-interface/public/shared/ui-navbar.js +3 -1
- package/neural-interface/public/shared/ui-provider-auth.js +104 -0
- package/neural-interface/public/shared/ui-provider-terminal-auth.js +223 -0
- package/neural-interface/public/shared/ui-sessions.js +9 -3
- package/neural-interface/public/shared/ui-settings.js +55 -3
- package/neural-interface/public/shared/ui-sidepanel-sessions.js +3 -1
- package/neural-interface/public/shared/ui-sync.js +8 -0
- package/neural-interface/public/shared/ui-terminal.js +17 -0
- package/neural-interface/public/shared/ui-tool-updates.js +5 -0
- package/neural-interface/public/shared/ui-update.js +598 -7
- package/neural-interface/public/shared/ui-whatsapp.js +21 -6
- package/neural-interface/public/shared/whatsapp/wa-view.js +51 -8
- package/neural-interface/public/shared/worker-end.js +2 -0
- package/neural-interface/public/sw.js +1 -1
- package/neural-interface/public/variant/2d/main.js +10 -1
- package/neural-interface/public/variant/3d/main.js +10 -1
- package/neural-interface/scripts/rebuild-pty.js +14 -2
- package/neural-interface/server.js +1384 -679
- package/npm-shrinkwrap.json +4424 -0
- package/package.json +57 -2
- package/postinstall.js +61 -47
- package/preuninstall.js +8 -35
- package/setup.js +80 -47
- package/skills/synabun/modules/idea.md +1 -1
- package/skills/synabun/modules/schedule.md +1 -1
- package/updater.cmd +2 -2
- package/updater.mjs +97 -3
|
@@ -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,15 @@
|
|
|
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
|
+
// `render(button, { checked })` on an item draws its content instead of label/desc/tag.
|
|
35
|
+
// `className` on an item or an info row adds its own class.
|
|
36
|
+
// `keepOpen` on an item leaves the menu open when it is chosen.
|
|
24
37
|
// `restoreFocus` on an item or input sends focus back to the anchor once it is chosen.
|
|
25
38
|
// Keys: ↑/↓ move, Home/End jump, Enter/Space activate, Esc closes (focus back
|
|
26
39
|
// to the anchor), Tab closes. The optional filter hides non-matching items and
|
|
27
|
-
// any group left empty.
|
|
40
|
+
// any group left empty. The handle's update({ title, items, footer, focus }) redraws
|
|
41
|
+
// an open menu in place (focus: the data-id to land on).
|
|
28
42
|
|
|
29
43
|
let _open = null;
|
|
30
44
|
let _uid = 0;
|
|
@@ -87,11 +101,64 @@ export function currentMenu() {
|
|
|
87
101
|
return _open?.menu || null;
|
|
88
102
|
}
|
|
89
103
|
|
|
104
|
+
function itemButton(item, kind, { role, list, onActivate }) {
|
|
105
|
+
const btn = el('button', `asst-dd-item${item.danger ? ' danger' : ''}${item.className ? ` ${item.className}` : ''}`);
|
|
106
|
+
btn.type = 'button';
|
|
107
|
+
btn.tabIndex = -1;
|
|
108
|
+
btn.dataset.kind = kind;
|
|
109
|
+
if (item.id != null) btn.dataset.id = String(item.id);
|
|
110
|
+
btn.dataset.search = `${item.label || ''} ${item.desc || ''} ${item.keywords || ''}`.toLowerCase();
|
|
111
|
+
const checked = kind === 'radio' ? !!item.selected : kind === 'check' ? !!item.checked : false;
|
|
112
|
+
if (role === 'listbox') {
|
|
113
|
+
btn.setAttribute('role', 'option');
|
|
114
|
+
btn.setAttribute('aria-selected', checked ? 'true' : 'false');
|
|
115
|
+
} else if (kind === 'radio') {
|
|
116
|
+
btn.setAttribute('role', 'menuitemradio');
|
|
117
|
+
btn.setAttribute('aria-checked', checked ? 'true' : 'false');
|
|
118
|
+
} else if (kind === 'check') {
|
|
119
|
+
btn.setAttribute('role', 'menuitemcheckbox');
|
|
120
|
+
btn.setAttribute('aria-checked', checked ? 'true' : 'false');
|
|
121
|
+
} else {
|
|
122
|
+
btn.setAttribute('role', role === 'listbox' ? 'option' : 'menuitem');
|
|
123
|
+
}
|
|
124
|
+
if (item.disabled) btn.disabled = true;
|
|
125
|
+
if (typeof item.render === 'function') {
|
|
126
|
+
item.render(btn, { checked });
|
|
127
|
+
} else {
|
|
128
|
+
const lead = kind === 'radio' || kind === 'check'
|
|
129
|
+
? `<span class="asst-dd-check" aria-hidden="true">${checked ? ICON_CHECK : ''}</span>`
|
|
130
|
+
: '';
|
|
131
|
+
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>` : ''}`;
|
|
132
|
+
}
|
|
133
|
+
if (item.title) btn.title = item.title;
|
|
134
|
+
if (item.ariaLabel) btn.setAttribute('aria-label', item.ariaLabel);
|
|
135
|
+
btn.addEventListener('click', (e) => { e.stopPropagation(); if (!btn.disabled) onActivate(item, btn); });
|
|
136
|
+
btn.addEventListener('mousemove', () => {
|
|
137
|
+
if (btn.classList.contains('focused') || btn.disabled) return;
|
|
138
|
+
const menu = list.closest('.asst-dd-menu');
|
|
139
|
+
menu?.querySelectorAll('.asst-dd-item.focused').forEach(n => n.classList.remove('focused'));
|
|
140
|
+
btn.classList.add('focused');
|
|
141
|
+
// Hover never steals focus from the filter / inline input.
|
|
142
|
+
if (!document.activeElement?.classList?.contains('asst-dd-input')) { try { btn.focus({ preventScroll: true }); } catch { /* ignore */ } }
|
|
143
|
+
});
|
|
144
|
+
if (checked) btn.dataset.checked = '1';
|
|
145
|
+
return btn;
|
|
146
|
+
}
|
|
147
|
+
|
|
90
148
|
function buildItems(list, items, { role, onActivate }) {
|
|
91
|
-
const itemRole = role === 'listbox' ? 'option' : 'menuitem';
|
|
92
149
|
for (const item of items || []) {
|
|
93
150
|
const kind = itemKind(item);
|
|
94
151
|
if (kind === 'separator') { list.appendChild(el('div', 'asst-dd-sep')).setAttribute('role', 'separator'); continue; }
|
|
152
|
+
if (kind === 'buttons') {
|
|
153
|
+
const row = el('div', 'asst-dd-btnrow');
|
|
154
|
+
row.setAttribute('role', 'group');
|
|
155
|
+
if (item.id != null) row.dataset.id = String(item.id);
|
|
156
|
+
if (item.ariaLabel || item.label) row.setAttribute('aria-label', item.ariaLabel || item.label);
|
|
157
|
+
if (item.label) row.appendChild(el('span', 'asst-dd-btnrow-label', item.label)).setAttribute('aria-hidden', 'true');
|
|
158
|
+
for (const button of item.buttons || []) row.appendChild(itemButton(button, itemKind(button), { role, list, onActivate }));
|
|
159
|
+
list.appendChild(row);
|
|
160
|
+
continue;
|
|
161
|
+
}
|
|
95
162
|
if (kind === 'header') {
|
|
96
163
|
const h = el('div', 'asst-dd-group');
|
|
97
164
|
h.setAttribute('role', 'presentation');
|
|
@@ -100,7 +167,7 @@ function buildItems(list, items, { role, onActivate }) {
|
|
|
100
167
|
continue;
|
|
101
168
|
}
|
|
102
169
|
if (kind === 'info' || kind === 'tag') {
|
|
103
|
-
const row = el('div', kind === 'tag' ? 'asst-dd-tagrow' : 'asst-dd-info');
|
|
170
|
+
const row = el('div', `${kind === 'tag' ? 'asst-dd-tagrow' : 'asst-dd-info'}${item.className ? ` ${item.className}` : ''}`);
|
|
104
171
|
row.setAttribute('role', 'presentation');
|
|
105
172
|
row.innerHTML = kind === 'tag'
|
|
106
173
|
? `<span class="asst-dd-tag">${esc(item.label)}</span>`
|
|
@@ -121,42 +188,7 @@ function buildItems(list, items, { role, onActivate }) {
|
|
|
121
188
|
list.appendChild(input);
|
|
122
189
|
continue;
|
|
123
190
|
}
|
|
124
|
-
|
|
125
|
-
btn.type = 'button';
|
|
126
|
-
btn.tabIndex = -1;
|
|
127
|
-
btn.dataset.kind = kind;
|
|
128
|
-
if (item.id != null) btn.dataset.id = String(item.id);
|
|
129
|
-
btn.dataset.search = `${item.label || ''} ${item.desc || ''} ${item.keywords || ''}`.toLowerCase();
|
|
130
|
-
const checked = kind === 'radio' ? !!item.selected : kind === 'check' ? !!item.checked : false;
|
|
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);
|
|
142
|
-
}
|
|
143
|
-
if (item.disabled) btn.disabled = true;
|
|
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);
|
|
191
|
+
list.appendChild(itemButton(item, kind, { role, list, onActivate }));
|
|
160
192
|
}
|
|
161
193
|
if (!list.querySelector('.asst-dd-item, .asst-dd-input, .asst-dd-info, .asst-dd-tagrow')) list.appendChild(el('div', 'asst-dd-empty', '—'));
|
|
162
194
|
}
|
|
@@ -167,6 +199,8 @@ export function applyMenuFilter(list, query, emptyLabel = 'No matches') {
|
|
|
167
199
|
const rows = [...list.children];
|
|
168
200
|
for (const row of rows) {
|
|
169
201
|
if (row.classList.contains('asst-dd-item')) row.hidden = !!q && !row.dataset.search.includes(q);
|
|
202
|
+
// A button row stays whole: shown while one of its buttons matches.
|
|
203
|
+
else if (row.classList.contains('asst-dd-btnrow')) row.hidden = !!q && ![...row.querySelectorAll('.asst-dd-item')].some(b => b.dataset.search.includes(q));
|
|
170
204
|
else if (row.classList.contains('asst-dd-info') || row.classList.contains('asst-dd-tagrow')) row.hidden = !!q;
|
|
171
205
|
}
|
|
172
206
|
// Group headers: visible only when an item until the next header/separator is visible.
|
|
@@ -192,7 +226,7 @@ export function applyMenuFilter(list, query, emptyLabel = 'No matches') {
|
|
|
192
226
|
const last = visible[visible.length - 1];
|
|
193
227
|
if (last?.classList.contains('asst-dd-sep')) last.hidden = true;
|
|
194
228
|
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')));
|
|
229
|
+
const hasItem = rows.some(r => !r.hidden && (r.classList.contains('asst-dd-item') || r.classList.contains('asst-dd-btnrow') || r.classList.contains('asst-dd-input')));
|
|
196
230
|
if (q && !hasItem) {
|
|
197
231
|
if (!empty) { empty = el('div', 'asst-dd-empty asst-dd-noresults', emptyLabel); list.appendChild(empty); }
|
|
198
232
|
empty.hidden = false;
|
|
@@ -204,7 +238,8 @@ const PREFER_BELOW_MIN = 220;
|
|
|
204
238
|
|
|
205
239
|
/**
|
|
206
240
|
* Whether a menu opens above its anchor (pure; sizes in px). `above` / `below`:
|
|
207
|
-
* the room on each side of the anchor
|
|
241
|
+
* the room on each side of the anchor; `anchorTop` / `viewportHeight`: the anchor's
|
|
242
|
+
* top in the box the menu stays inside, and that box's height.
|
|
208
243
|
*/
|
|
209
244
|
export function menuOpensAbove(placement, { anchorTop, anchorHeight, menuHeight, above, below, viewportHeight }) {
|
|
210
245
|
if (placement === 'above') return menuHeight <= above || above >= below;
|
|
@@ -214,37 +249,101 @@ export function menuOpensAbove(placement, { anchorTop, anchorHeight, menuHeight,
|
|
|
214
249
|
return !(menuHeight <= below || below >= above);
|
|
215
250
|
}
|
|
216
251
|
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
252
|
+
// A menu: never narrower than this while the window allows it; never shorter than these
|
|
253
|
+
// (a bounded menu holds a title and a footer around its list).
|
|
254
|
+
const BOUNDED_MIN_WIDTH = 248;
|
|
255
|
+
const MENU_MIN_HEIGHT = 96;
|
|
256
|
+
const BOUNDED_MIN_HEIGHT = 140;
|
|
257
|
+
|
|
258
|
+
/** The box a menu stays inside: its bounds clipped to the window (the window when it has none). */
|
|
259
|
+
function boundsBox(bounds) {
|
|
220
260
|
const vw = window.innerWidth;
|
|
221
261
|
const vh = window.innerHeight;
|
|
222
|
-
|
|
223
|
-
|
|
262
|
+
let r = null;
|
|
263
|
+
try { r = typeof bounds === 'function' ? bounds() : bounds?.getBoundingClientRect?.(); } catch { r = null; }
|
|
264
|
+
if (!r || !(r.width > 0) || !(r.height > 0)) return { left: 0, top: 0, right: vw, bottom: vh };
|
|
265
|
+
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) };
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
/** Where a menu without `bounds` stays: the Assistant around its anchor (the sidepanel, or a tab's own root). */
|
|
269
|
+
function assistantOf(anchor) {
|
|
270
|
+
return anchor.closest?.('.assistant-panel') || anchor.closest?.('.asst-root') || null;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
/** The widest a menu may be in its box (pure; px): the box less the margins, never under the minimum the window allows. */
|
|
274
|
+
export function menuRoomWidth({ box, viewportWidth, margin = 8 }) {
|
|
275
|
+
return Math.round(Math.max(box.right - box.left - margin * 2, Math.min(BOUNDED_MIN_WIDTH, viewportWidth - margin * 2)));
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
/** A bounded menu's width (pure; px): `width` at most, its box less the margins, never under the minimum the window allows. */
|
|
279
|
+
export function boundedMenuWidth({ box, viewportWidth, width, margin = 8 }) {
|
|
280
|
+
return Math.round(Math.max(Math.min(width || 320, box.right - box.left - margin * 2), Math.min(BOUNDED_MIN_WIDTH, viewportWidth - margin * 2)));
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
/**
|
|
284
|
+
* Where a menu goes (pure; px; rects as getBoundingClientRect gives them): under the anchor or
|
|
285
|
+
* above it as `placement` decides from the room inside the box (menuOpensAbove), as tall as that
|
|
286
|
+
* room; right-aligned with `alignX` (default: the anchor's right edge), or starting at the anchor's
|
|
287
|
+
* left edge with `alignLeft`; always inside the box and the window.
|
|
288
|
+
* → { left, top, maxHeight (null: its own height fits), up }.
|
|
289
|
+
*/
|
|
290
|
+
export function boundedMenuPlacement({ anchor, box, viewportWidth, viewportHeight, width, menuHeight, placement = 'below', alignX = null, alignLeft = false, minHeight = BOUNDED_MIN_HEIGHT, margin = 8, gap = 6 }) {
|
|
291
|
+
const below = box.bottom - margin - (anchor.bottom + gap);
|
|
292
|
+
const above = anchor.top - gap - (box.top + margin);
|
|
293
|
+
const up = menuOpensAbove(placement, { anchorTop: anchor.top - box.top, anchorHeight: anchor.bottom - anchor.top, menuHeight, above, below, viewportHeight: box.bottom - box.top });
|
|
294
|
+
const room = Math.max(minHeight, Math.floor(up ? above : below));
|
|
295
|
+
const height = Math.min(menuHeight, room);
|
|
296
|
+
let top = up ? anchor.top - gap - height : anchor.bottom + gap;
|
|
297
|
+
top = Math.max(margin, Math.min(top, viewportHeight - margin - height));
|
|
298
|
+
let left = alignLeft ? anchor.left : (alignX ?? anchor.right) - width;
|
|
299
|
+
left = Math.min(left, box.right - margin - width);
|
|
300
|
+
left = Math.max(left, box.left + margin);
|
|
301
|
+
left = Math.max(margin, Math.min(left, viewportWidth - margin - width));
|
|
302
|
+
return { left: Math.round(left), top: Math.round(top), maxHeight: menuHeight > room ? room : null, up };
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
/**
|
|
306
|
+
* Place a menu inside its box: its width is final before it is measured (the CSS caps read the
|
|
307
|
+
* box's room through --asst-dd-room; a bounded menu is set to its width), so what is shown is what
|
|
308
|
+
* was placed; then boundedMenuPlacement. The list scrolls inside, title and footer stay.
|
|
309
|
+
*/
|
|
310
|
+
function positionMenu(menu, anchor, { placement, bounds, width }) {
|
|
311
|
+
const viewportWidth = window.innerWidth;
|
|
312
|
+
const viewportHeight = window.innerHeight;
|
|
313
|
+
const box = boundsBox(bounds || assistantOf(anchor));
|
|
314
|
+
if (bounds) {
|
|
315
|
+
menu.style.minWidth = '0';
|
|
316
|
+
menu.style.maxWidth = 'none';
|
|
317
|
+
menu.style.width = `${boundedMenuWidth({ box, viewportWidth, width })}px`;
|
|
318
|
+
menu.style.maxHeight = 'none';
|
|
319
|
+
} else {
|
|
320
|
+
menu.style.setProperty('--asst-dd-room', `${menuRoomWidth({ box, viewportWidth })}px`);
|
|
321
|
+
menu.style.maxHeight = '';
|
|
322
|
+
}
|
|
224
323
|
menu.style.left = '0px';
|
|
225
324
|
menu.style.top = '0px';
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
const
|
|
229
|
-
const
|
|
230
|
-
const
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
if (
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
325
|
+
// The sidepanel hugs the right screen edge: its menus grow leftward from the click point (or the
|
|
326
|
+
// anchor's right edge for keyboard opens), as a bounded menu does; elsewhere a menu starts at its anchor.
|
|
327
|
+
const inSidepanel = !!anchor.closest?.('.assistant-panel');
|
|
328
|
+
const clicked = inSidepanel && _lastDown && anchor.contains(_lastDown.target) && performance.now() - _lastDown.at < 1500;
|
|
329
|
+
const spot = boundedMenuPlacement({
|
|
330
|
+
anchor: anchor.getBoundingClientRect(), box, viewportWidth, viewportHeight,
|
|
331
|
+
width: menu.offsetWidth, menuHeight: menu.offsetHeight, placement,
|
|
332
|
+
alignX: clicked ? _lastDown.x : null, alignLeft: !bounds && !inSidepanel,
|
|
333
|
+
minHeight: bounds ? BOUNDED_MIN_HEIGHT : MENU_MIN_HEIGHT,
|
|
334
|
+
});
|
|
335
|
+
if (spot.maxHeight != null) menu.style.maxHeight = `${spot.maxHeight}px`;
|
|
336
|
+
menu.style.left = `${spot.left}px`;
|
|
337
|
+
menu.style.top = `${spot.top}px`;
|
|
338
|
+
menu.dataset.placement = spot.up ? 'above' : 'below';
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
/** A row its menu's width cuts short (one-line label or description) keeps its words in a tooltip. */
|
|
342
|
+
function titleCutRows(menu) {
|
|
343
|
+
for (const btn of menu.querySelectorAll('.asst-dd-item:not([title])')) {
|
|
344
|
+
const parts = [...btn.querySelectorAll('.asst-dd-item-label, .asst-dd-item-desc')];
|
|
345
|
+
if (parts.some(n => n.scrollWidth > n.clientWidth + 1)) btn.title = parts.map(n => n.textContent).filter(Boolean).join(' · ');
|
|
241
346
|
}
|
|
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
347
|
}
|
|
249
348
|
|
|
250
349
|
/**
|
|
@@ -262,8 +361,9 @@ export function openMenu(anchor, opts = {}) {
|
|
|
262
361
|
menu.setAttribute('role', role);
|
|
263
362
|
menu.id = `asst-menu-${++_uid}`;
|
|
264
363
|
menu.tabIndex = -1;
|
|
364
|
+
let heading = null;
|
|
265
365
|
if (title) {
|
|
266
|
-
|
|
366
|
+
heading = el('div', 'asst-dd-menu-title', title);
|
|
267
367
|
heading.id = `${menu.id}-title`;
|
|
268
368
|
menu.setAttribute('aria-labelledby', heading.id);
|
|
269
369
|
menu.appendChild(heading);
|
|
@@ -288,14 +388,27 @@ export function openMenu(anchor, opts = {}) {
|
|
|
288
388
|
closeMenu({ restoreFocus: !!item.restoreFocus });
|
|
289
389
|
item.onSelect?.(item, node);
|
|
290
390
|
};
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
391
|
+
if (opts.content) list.appendChild(opts.content);
|
|
392
|
+
else buildItems(list, items, { role, onActivate: activate });
|
|
393
|
+
// The footer stays under the list: only the list scrolls.
|
|
394
|
+
let foot = null;
|
|
395
|
+
const fillFooter = (footer) => {
|
|
396
|
+
if (!foot) { foot = el('div', 'asst-dd-foot'); menu.appendChild(foot); }
|
|
397
|
+
foot.replaceChildren();
|
|
398
|
+
if (Array.isArray(footer) && footer.length) buildItems(foot, footer, { role, onActivate: activate });
|
|
399
|
+
foot.hidden = !(Array.isArray(footer) && footer.length);
|
|
400
|
+
};
|
|
401
|
+
if (Array.isArray(opts.footer)) fillFooter(opts.footer);
|
|
402
|
+
// `width`: at least that wide (exactly, with repositionOnResize), never wider than the room positionMenu sets.
|
|
403
|
+
const capped = `min(${width}px, var(--asst-dd-room, calc(100vw - 16px)))`;
|
|
404
|
+
if (width) menu.style.minWidth = capped;
|
|
405
|
+
if (width && opts.repositionOnResize) menu.style.width = capped;
|
|
294
406
|
document.body.appendChild(menu);
|
|
295
|
-
positionMenu(menu, anchor, placement);
|
|
407
|
+
const place = () => { positionMenu(menu, anchor, { placement, bounds: opts.bounds || null, width }); titleCutRows(menu); };
|
|
408
|
+
place();
|
|
296
409
|
|
|
297
410
|
if (filterInput) {
|
|
298
|
-
filterInput.addEventListener('input', () => { applyMenuFilter(list, filterInput.value, opts.filterEmptyLabel);
|
|
411
|
+
filterInput.addEventListener('input', () => { applyMenuFilter(list, filterInput.value, opts.filterEmptyLabel); place(); });
|
|
299
412
|
}
|
|
300
413
|
|
|
301
414
|
const onDocDown = (e) => {
|
|
@@ -310,7 +423,21 @@ export function openMenu(anchor, opts = {}) {
|
|
|
310
423
|
const inInput = target?.classList?.contains('asst-dd-input');
|
|
311
424
|
const nodes = focusables(menu);
|
|
312
425
|
const idx = nodes.indexOf(document.activeElement);
|
|
313
|
-
if (e.key === 'Escape') {
|
|
426
|
+
if (e.key === 'Escape') {
|
|
427
|
+
e.preventDefault(); e.stopPropagation();
|
|
428
|
+
if (typeof opts.onEscape === 'function' && opts.onEscape() === true) return;
|
|
429
|
+
closeMenu({ restoreFocus: true });
|
|
430
|
+
return;
|
|
431
|
+
}
|
|
432
|
+
if (role === 'dialog') {
|
|
433
|
+
if (e.key === 'Tab') {
|
|
434
|
+
const fields = [...menu.querySelectorAll('button, input')].filter(node => !node.disabled && !node.hidden);
|
|
435
|
+
const index = fields.indexOf(document.activeElement);
|
|
436
|
+
if (e.shiftKey && index <= 0) { e.preventDefault(); fields.at(-1)?.focus(); }
|
|
437
|
+
else if (!e.shiftKey && (index < 0 || index === fields.length - 1)) { e.preventDefault(); fields[0]?.focus(); }
|
|
438
|
+
}
|
|
439
|
+
return; // native number/switch keys and Enter belong to the form
|
|
440
|
+
}
|
|
314
441
|
if (e.key === 'Tab') { closeMenu(); return; }
|
|
315
442
|
if (e.key === 'ArrowDown' || e.key === 'ArrowUp') {
|
|
316
443
|
e.preventDefault(); e.stopPropagation();
|
|
@@ -344,15 +471,16 @@ export function openMenu(anchor, opts = {}) {
|
|
|
344
471
|
// Keep single-letter global keybinds out of an open menu.
|
|
345
472
|
e.stopPropagation();
|
|
346
473
|
};
|
|
347
|
-
const onResize = () => opts.repositionOnResize ?
|
|
474
|
+
const onResize = () => (opts.repositionOnResize || opts.bounds ? place() : closeMenu());
|
|
348
475
|
const onBlur = () => closeMenu();
|
|
349
476
|
document.addEventListener('mousedown', onDocDown, true);
|
|
350
|
-
|
|
477
|
+
const keyTarget = role === 'dialog' ? window : document;
|
|
478
|
+
keyTarget.addEventListener('keydown', onKey, true);
|
|
351
479
|
window.addEventListener('resize', onResize);
|
|
352
480
|
window.addEventListener('blur', onBlur);
|
|
353
481
|
const cleanup = () => {
|
|
354
482
|
document.removeEventListener('mousedown', onDocDown, true);
|
|
355
|
-
|
|
483
|
+
keyTarget.removeEventListener('keydown', onKey, true);
|
|
356
484
|
window.removeEventListener('resize', onResize);
|
|
357
485
|
window.removeEventListener('blur', onBlur);
|
|
358
486
|
};
|
|
@@ -360,6 +488,7 @@ export function openMenu(anchor, opts = {}) {
|
|
|
360
488
|
_open = { menu, anchor, onClose, cleanup };
|
|
361
489
|
if (anchor.getAttribute('aria-haspopup')) anchor.setAttribute('aria-expanded', 'true');
|
|
362
490
|
anchor.setAttribute('aria-controls', menu.id);
|
|
491
|
+
opts.onOpen?.(menu);
|
|
363
492
|
|
|
364
493
|
const selected = list.querySelector('.asst-dd-item[data-checked="1"]:not([disabled])');
|
|
365
494
|
const first = focusables(menu).find(n => n.classList.contains('asst-dd-item')) || focusables(menu)[0];
|
|
@@ -369,7 +498,17 @@ export function openMenu(anchor, opts = {}) {
|
|
|
369
498
|
} else {
|
|
370
499
|
setFocused(menu, selected || first || null);
|
|
371
500
|
}
|
|
372
|
-
|
|
501
|
+
// Redraw in place (another view of the same menu): the anchor, the listeners and the focus stay in it.
|
|
502
|
+
const update = (next = {}) => {
|
|
503
|
+
if (!_open || _open.menu !== menu) return;
|
|
504
|
+
if (typeof next.title === 'string' && heading) heading.textContent = next.title;
|
|
505
|
+
if (Array.isArray(next.items)) { list.replaceChildren(); buildItems(list, next.items, { role, onActivate: activate }); list.scrollTop = 0; }
|
|
506
|
+
if ('footer' in next) fillFooter(next.footer);
|
|
507
|
+
place();
|
|
508
|
+
const wanted = next.focus != null ? [...menu.querySelectorAll('.asst-dd-item')].find(n => n.dataset.id === String(next.focus) && !n.disabled) : null;
|
|
509
|
+
setFocused(menu, wanted || list.querySelector('.asst-dd-item[data-checked="1"]:not([disabled])') || focusables(menu)[0] || null);
|
|
510
|
+
};
|
|
511
|
+
return { el: menu, list, close: closeMenu, reposition: place, update };
|
|
373
512
|
}
|
|
374
513
|
|
|
375
514
|
/** Toggle helper for anchor buttons: closes when this anchor's menu is open. */
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
// ═══════════════════════════════════════════
|
|
2
|
+
// SynaBun Assistant — what the composer's model chip says
|
|
3
|
+
// ═══════════════════════════════════════════
|
|
4
|
+
// The model as a short human name from its catalog label ("Opus 5.5 (1M
|
|
5
|
+
// context)" → "Opus 5.5", "Default (recommended) — Opus 5.5" → "Opus 5.5"),
|
|
6
|
+
// a context tag for a "[1m]" / "[extended]" variant ("1M"), and the forms the
|
|
7
|
+
// chip falls back to when the composer row has no room, longest first: the tag
|
|
8
|
+
// goes first, then the account cue, then the API badge, then the name shortens
|
|
9
|
+
// by whole words, to a form that still names this model alone when there is one
|
|
10
|
+
// ("Opus 5.5" → "Opus", "GPT-6-Luna" → "Luna"). Never a cut word.
|
|
11
|
+
// asst-brain-picker.js draws the first form that fits. DOM-free: node --test loads it.
|
|
12
|
+
|
|
13
|
+
const VARIANT = /\[(1m|extended)\]$/i;
|
|
14
|
+
const VERSION = /^v?\d+(?:\.\d+)*[a-z]?$/i;
|
|
15
|
+
|
|
16
|
+
function str(v) {
|
|
17
|
+
return v == null ? '' : String(v).trim();
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/** The id without its provider ("anthropic/…"), legacy ":<window>" and variant suffix. */
|
|
21
|
+
function bareId(id) {
|
|
22
|
+
const tail = str(id).split('/').pop() || '';
|
|
23
|
+
return tail.replace(/:\d+$/, '').replace(VARIANT, '');
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* The model's short name: { name, raw }. The label's own words without what the
|
|
28
|
+
* chip says otherwise (parentheses: the context, "recommended"); after a dash, the
|
|
29
|
+
* model it runs as. No label (or the id itself): the bare id, `raw` (shown mono).
|
|
30
|
+
*/
|
|
31
|
+
export function modelChipName(label, id) {
|
|
32
|
+
let text = str(label);
|
|
33
|
+
if (text && text !== str(id)) {
|
|
34
|
+
const runsAs = text.split(/\s+[—–]\s+/).pop();
|
|
35
|
+
text = str(runsAs.replace(/\s*\([^)]*\)/g, '').replace(/\s+/g, ' '));
|
|
36
|
+
if (text) return { name: text, raw: false };
|
|
37
|
+
}
|
|
38
|
+
const name = bareId(id) || str(id);
|
|
39
|
+
return { name, raw: true };
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/** "1M", "400K": a context window in tokens, never rounded up. */
|
|
43
|
+
function compactTokens(n) {
|
|
44
|
+
const v = Number(n);
|
|
45
|
+
if (!Number.isFinite(v) || v <= 0) return '';
|
|
46
|
+
if (v >= 1e6) return `${Math.floor(v / 1e5) / 10}M`;
|
|
47
|
+
if (v >= 1e3) return `${Math.floor(v / 1e3)}K`;
|
|
48
|
+
return String(v);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* What tells a context variant from its model ("opus[1m]" → "1M"): its window when
|
|
53
|
+
* the catalog knows it, else "1M" for "[1m]", else the label's own word ("extended").
|
|
54
|
+
* '' for a model that is not a variant.
|
|
55
|
+
*/
|
|
56
|
+
export function modelChipTag(id, { contextWindow = null, label = '' } = {}) {
|
|
57
|
+
const m = str(id).match(VARIANT);
|
|
58
|
+
if (!m) return '';
|
|
59
|
+
const tokens = compactTokens(contextWindow);
|
|
60
|
+
if (tokens) return tokens;
|
|
61
|
+
if (m[1].toLowerCase() === '1m') return '1M';
|
|
62
|
+
const note = str(label).match(/\(([^)]*)\)\s*$/);
|
|
63
|
+
return note ? str(note[1].replace(/\bcontext\b/i, '')) : '';
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// "GPT-6.1-Sol" → ['GPT', '6.1', 'Sol'] with the separators kept: whole words in, whole words out.
|
|
67
|
+
function parts(name) {
|
|
68
|
+
return str(name).match(/[^\s-]+|[\s-]+/g) || [];
|
|
69
|
+
}
|
|
70
|
+
const isSep = (p) => /^[\s-]+$/.test(p);
|
|
71
|
+
const wordsOf = (name) => parts(name).filter(p => !isSep(p)).map(w => w.toLowerCase());
|
|
72
|
+
|
|
73
|
+
/** Whether `words` run in that order, whole, somewhere in `other` ("opus" in "Opus 5.5"). */
|
|
74
|
+
function within(words, other) {
|
|
75
|
+
for (let i = 0; i + words.length <= other.length; i += 1) {
|
|
76
|
+
if (words.every((w, j) => other[i + j] === w)) return true;
|
|
77
|
+
}
|
|
78
|
+
return false;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* The name's shorter forms, whole words only, longest first: `unique` still names this
|
|
83
|
+
* model alone among `names` (the short names of every model the provider lists: "Opus"
|
|
84
|
+
* for "Opus 5.5", "GPT-6.1" for "GPT-6.1-Sol", "Luna" for "GPT-6-Luna" when every
|
|
85
|
+
* "GPT-6" is another model too), `other` drops trailing words whatever they lose
|
|
86
|
+
* ("Sonnet" for "Sonnet 5.5" beside "Sonnet 5"; "GPT-6"). A form never starts with a
|
|
87
|
+
* version ("5.5"), and a suffix is tried only when no shorter prefix is unique.
|
|
88
|
+
*/
|
|
89
|
+
export function modelChipShortNames(name, names = []) {
|
|
90
|
+
const all = parts(name);
|
|
91
|
+
const own = wordsOf(name);
|
|
92
|
+
const others = [...new Set((Array.isArray(names) ? names : []).map(str).filter(n => n && n.toLowerCase() !== str(name).toLowerCase()))].map(wordsOf);
|
|
93
|
+
const unique = (form) => { const w = wordsOf(form); return !others.some(o => within(w, o)); };
|
|
94
|
+
const prefixes = [];
|
|
95
|
+
const suffixes = [];
|
|
96
|
+
for (let n = own.length - 1; n >= 1; n -= 1) {
|
|
97
|
+
let seen = 0;
|
|
98
|
+
const cut = all.findIndex(p => !isSep(p) && ++seen > n);
|
|
99
|
+
prefixes.push(all.slice(0, cut).join('').replace(/[\s-]+$/, ''));
|
|
100
|
+
let skip = own.length - n;
|
|
101
|
+
const from = all.findIndex(p => !isSep(p) && skip-- === 0);
|
|
102
|
+
if (!VERSION.test(all[from])) suffixes.push(all.slice(from).join(''));
|
|
103
|
+
}
|
|
104
|
+
const lossless = prefixes.filter(unique);
|
|
105
|
+
const picked = lossless.length ? lossless : suffixes.filter(unique);
|
|
106
|
+
return { unique: picked, other: prefixes.filter(p => !picked.includes(p)) };
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* The chip's forms, longest first: [{ name, tag, cue, api }] (tag / cue / api: shown
|
|
111
|
+
* or not). `tag`, `cue` and `api` say whether the chip has one at all; `names` are the
|
|
112
|
+
* provider's models (modelChipShortNames). What goes, in order: the tag, the account
|
|
113
|
+
* cue, the API badge, then the name. A shorter name that still names this model alone
|
|
114
|
+
* loses nothing, so it is tried before the cue and before the badge go; one that
|
|
115
|
+
* loses something only after.
|
|
116
|
+
*/
|
|
117
|
+
export function modelChipForms({ name, names = [], tag = false, cue = false, api = false } = {}) {
|
|
118
|
+
const full = str(name);
|
|
119
|
+
const out = [];
|
|
120
|
+
const seen = new Set();
|
|
121
|
+
const add = (n, t, c, a) => {
|
|
122
|
+
const form = { name: n, tag: !!(t && tag), cue: !!(c && cue), api: !!(a && api) };
|
|
123
|
+
const key = JSON.stringify(form);
|
|
124
|
+
if (!seen.has(key)) { seen.add(key); out.push(form); }
|
|
125
|
+
};
|
|
126
|
+
const short = full ? modelChipShortNames(full, names) : { unique: [], other: [] };
|
|
127
|
+
add(full, true, true, true);
|
|
128
|
+
add(full, false, true, true);
|
|
129
|
+
for (const u of short.unique) add(u, false, true, true);
|
|
130
|
+
add(full, false, false, true);
|
|
131
|
+
for (const u of short.unique) add(u, false, false, true);
|
|
132
|
+
add(full, false, false, false);
|
|
133
|
+
for (const n of [...short.unique, ...short.other]) add(n, false, false, false);
|
|
134
|
+
return out;
|
|
135
|
+
}
|