synabun 2.0.7 → 2.0.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +67 -0
- package/README.md +1 -1
- package/THIRD-PARTY-LICENSES.md +46 -4
- package/hooks/claude-code/post-plan.mjs +6 -30
- package/hooks/claude-code/session-start.mjs +1 -4
- package/hooks/claude-code/shared.mjs +106 -72
- package/launcher.mjs +9 -3
- package/lib/package-processes.js +670 -0
- package/lib/runtime-dependencies.js +225 -0
- package/lib/start-launcher.js +6 -1
- package/mcp-server/dist/services/browser-bench.d.ts +1 -1
- package/mcp-server/dist/services/browser-bench.d.ts.map +1 -1
- package/mcp-server/dist/services/browser-bench.js +1 -1
- package/mcp-server/dist/services/browser-bench.js.map +1 -1
- package/mcp-server/dist/services/categories.d.ts +35 -2
- package/mcp-server/dist/services/categories.d.ts.map +1 -1
- package/mcp-server/dist/services/categories.js +77 -33
- package/mcp-server/dist/services/categories.js.map +1 -1
- package/mcp-server/dist/services/desktop-bench.d.ts +7 -7
- package/mcp-server/dist/services/desktop-bench.d.ts.map +1 -1
- package/mcp-server/dist/services/desktop-bench.js +1 -1
- package/mcp-server/dist/services/desktop-bench.js.map +1 -1
- package/mcp-server/dist/tools/agents.d.ts +1 -1
- package/mcp-server/dist/tools/agents.d.ts.map +1 -1
- package/mcp-server/dist/tools/agents.js +1 -1
- package/mcp-server/dist/tools/agents.js.map +1 -1
- package/mcp-server/dist/tools/bluesky.js +1 -1
- package/mcp-server/dist/tools/bluesky.js.map +1 -1
- package/mcp-server/dist/tools/browser-advanced.d.ts +1 -1
- package/mcp-server/dist/tools/browser-advanced.d.ts.map +1 -1
- package/mcp-server/dist/tools/browser-advanced.js +1 -1
- package/mcp-server/dist/tools/browser-advanced.js.map +1 -1
- package/mcp-server/dist/tools/browser-batch.d.ts +1 -1
- package/mcp-server/dist/tools/browser-batch.d.ts.map +1 -1
- package/mcp-server/dist/tools/browser-batch.js +1 -1
- package/mcp-server/dist/tools/browser-batch.js.map +1 -1
- package/mcp-server/dist/tools/browser-cheatsheet.d.ts +1 -1
- package/mcp-server/dist/tools/browser-cheatsheet.d.ts.map +1 -1
- package/mcp-server/dist/tools/browser-cheatsheet.js +1 -1
- package/mcp-server/dist/tools/browser-cheatsheet.js.map +1 -1
- package/mcp-server/dist/tools/browser-interact.d.ts +1 -1
- package/mcp-server/dist/tools/browser-interact.d.ts.map +1 -1
- package/mcp-server/dist/tools/browser-interact.js +1 -1
- package/mcp-server/dist/tools/browser-interact.js.map +1 -1
- package/mcp-server/dist/tools/browser-navigate.d.ts +1 -1
- package/mcp-server/dist/tools/browser-navigate.d.ts.map +1 -1
- package/mcp-server/dist/tools/browser-navigate.js +1 -1
- package/mcp-server/dist/tools/browser-navigate.js.map +1 -1
- package/mcp-server/dist/tools/browser-observe.d.ts +1 -1
- package/mcp-server/dist/tools/browser-observe.d.ts.map +1 -1
- package/mcp-server/dist/tools/browser-observe.js +1 -1
- package/mcp-server/dist/tools/browser-observe.js.map +1 -1
- package/mcp-server/dist/tools/card-tools.d.ts +1 -1
- package/mcp-server/dist/tools/card-tools.d.ts.map +1 -1
- package/mcp-server/dist/tools/card-tools.js +1 -1
- package/mcp-server/dist/tools/card-tools.js.map +1 -1
- package/mcp-server/dist/tools/category.d.ts +1 -1
- package/mcp-server/dist/tools/category.d.ts.map +1 -1
- package/mcp-server/dist/tools/category.js +5 -1
- package/mcp-server/dist/tools/category.js.map +1 -1
- package/mcp-server/dist/tools/choice.d.ts +1 -1
- package/mcp-server/dist/tools/choice.d.ts.map +1 -1
- package/mcp-server/dist/tools/choice.js +1 -1
- package/mcp-server/dist/tools/choice.js.map +1 -1
- package/mcp-server/dist/tools/computer.d.ts +1 -1
- package/mcp-server/dist/tools/computer.d.ts.map +1 -1
- package/mcp-server/dist/tools/computer.js +1 -1
- package/mcp-server/dist/tools/computer.js.map +1 -1
- package/mcp-server/dist/tools/discord-channel.d.ts +1 -1
- package/mcp-server/dist/tools/discord-channel.d.ts.map +1 -1
- package/mcp-server/dist/tools/discord-channel.js +1 -1
- package/mcp-server/dist/tools/discord-channel.js.map +1 -1
- package/mcp-server/dist/tools/discord-guild.d.ts +1 -1
- package/mcp-server/dist/tools/discord-guild.d.ts.map +1 -1
- package/mcp-server/dist/tools/discord-guild.js +1 -1
- package/mcp-server/dist/tools/discord-guild.js.map +1 -1
- package/mcp-server/dist/tools/discord-member.d.ts +1 -1
- package/mcp-server/dist/tools/discord-member.d.ts.map +1 -1
- package/mcp-server/dist/tools/discord-member.js +1 -1
- package/mcp-server/dist/tools/discord-member.js.map +1 -1
- package/mcp-server/dist/tools/discord-message.d.ts +1 -1
- package/mcp-server/dist/tools/discord-message.d.ts.map +1 -1
- package/mcp-server/dist/tools/discord-message.js +1 -1
- package/mcp-server/dist/tools/discord-message.js.map +1 -1
- package/mcp-server/dist/tools/discord-onboarding.d.ts +1 -1
- package/mcp-server/dist/tools/discord-onboarding.d.ts.map +1 -1
- package/mcp-server/dist/tools/discord-onboarding.js +1 -1
- package/mcp-server/dist/tools/discord-onboarding.js.map +1 -1
- package/mcp-server/dist/tools/discord-role.d.ts +1 -1
- package/mcp-server/dist/tools/discord-role.d.ts.map +1 -1
- package/mcp-server/dist/tools/discord-role.js +1 -1
- package/mcp-server/dist/tools/discord-role.js.map +1 -1
- package/mcp-server/dist/tools/discord-thread.d.ts +1 -1
- package/mcp-server/dist/tools/discord-thread.d.ts.map +1 -1
- package/mcp-server/dist/tools/discord-thread.js +1 -1
- package/mcp-server/dist/tools/discord-thread.js.map +1 -1
- package/mcp-server/dist/tools/discord-webhook.d.ts +1 -1
- package/mcp-server/dist/tools/discord-webhook.d.ts.map +1 -1
- package/mcp-server/dist/tools/discord-webhook.js +1 -1
- package/mcp-server/dist/tools/discord-webhook.js.map +1 -1
- package/mcp-server/dist/tools/fb-groups.d.ts +1 -1
- package/mcp-server/dist/tools/fb-groups.d.ts.map +1 -1
- package/mcp-server/dist/tools/fb-groups.js +1 -1
- package/mcp-server/dist/tools/fb-groups.js.map +1 -1
- package/mcp-server/dist/tools/forget.d.ts +1 -1
- package/mcp-server/dist/tools/forget.d.ts.map +1 -1
- package/mcp-server/dist/tools/forget.js +1 -1
- package/mcp-server/dist/tools/forget.js.map +1 -1
- package/mcp-server/dist/tools/git-tools.d.ts +1 -1
- package/mcp-server/dist/tools/git-tools.d.ts.map +1 -1
- package/mcp-server/dist/tools/git-tools.js +1 -1
- package/mcp-server/dist/tools/git-tools.js.map +1 -1
- package/mcp-server/dist/tools/gsc/experience.d.ts +1 -1
- package/mcp-server/dist/tools/gsc/experience.d.ts.map +1 -1
- package/mcp-server/dist/tools/gsc/experience.js +1 -1
- package/mcp-server/dist/tools/gsc/experience.js.map +1 -1
- package/mcp-server/dist/tools/gsc/indexing.d.ts +1 -1
- package/mcp-server/dist/tools/gsc/indexing.d.ts.map +1 -1
- package/mcp-server/dist/tools/gsc/indexing.js +1 -1
- package/mcp-server/dist/tools/gsc/indexing.js.map +1 -1
- package/mcp-server/dist/tools/gsc/inspect.d.ts +1 -1
- package/mcp-server/dist/tools/gsc/inspect.d.ts.map +1 -1
- package/mcp-server/dist/tools/gsc/inspect.js +1 -1
- package/mcp-server/dist/tools/gsc/inspect.js.map +1 -1
- package/mcp-server/dist/tools/gsc/navigate.d.ts +1 -1
- package/mcp-server/dist/tools/gsc/navigate.d.ts.map +1 -1
- package/mcp-server/dist/tools/gsc/navigate.js +1 -1
- package/mcp-server/dist/tools/gsc/navigate.js.map +1 -1
- package/mcp-server/dist/tools/gsc/performance.d.ts +1 -1
- package/mcp-server/dist/tools/gsc/performance.d.ts.map +1 -1
- package/mcp-server/dist/tools/gsc/performance.js +1 -1
- package/mcp-server/dist/tools/gsc/performance.js.map +1 -1
- package/mcp-server/dist/tools/gsc/settings.d.ts +1 -1
- package/mcp-server/dist/tools/gsc/settings.d.ts.map +1 -1
- package/mcp-server/dist/tools/gsc/settings.js +1 -1
- package/mcp-server/dist/tools/gsc/settings.js.map +1 -1
- package/mcp-server/dist/tools/image-tools.d.ts +1 -1
- package/mcp-server/dist/tools/image-tools.d.ts.map +1 -1
- package/mcp-server/dist/tools/image-tools.js +1 -1
- package/mcp-server/dist/tools/image-tools.js.map +1 -1
- package/mcp-server/dist/tools/leonardo-browser-tools.d.ts +1 -1
- package/mcp-server/dist/tools/leonardo-browser-tools.d.ts.map +1 -1
- package/mcp-server/dist/tools/leonardo-browser-tools.js +1 -1
- package/mcp-server/dist/tools/leonardo-browser-tools.js.map +1 -1
- package/mcp-server/dist/tools/loop.d.ts +1 -1
- package/mcp-server/dist/tools/loop.d.ts.map +1 -1
- package/mcp-server/dist/tools/loop.js +1 -1
- package/mcp-server/dist/tools/loop.js.map +1 -1
- package/mcp-server/dist/tools/memories.d.ts +1 -1
- package/mcp-server/dist/tools/memories.d.ts.map +1 -1
- package/mcp-server/dist/tools/memories.js +1 -1
- package/mcp-server/dist/tools/memories.js.map +1 -1
- package/mcp-server/dist/tools/morelogin.d.ts +1 -1
- package/mcp-server/dist/tools/morelogin.d.ts.map +1 -1
- package/mcp-server/dist/tools/morelogin.js +1 -1
- package/mcp-server/dist/tools/morelogin.js.map +1 -1
- package/mcp-server/dist/tools/profile.d.ts +1 -1
- package/mcp-server/dist/tools/profile.d.ts.map +1 -1
- package/mcp-server/dist/tools/profile.js +1 -1
- package/mcp-server/dist/tools/profile.js.map +1 -1
- package/mcp-server/dist/tools/recall.d.ts +1 -1
- package/mcp-server/dist/tools/recall.d.ts.map +1 -1
- package/mcp-server/dist/tools/recall.js +1 -1
- package/mcp-server/dist/tools/recall.js.map +1 -1
- package/mcp-server/dist/tools/reflect.d.ts +1 -1
- package/mcp-server/dist/tools/reflect.d.ts.map +1 -1
- package/mcp-server/dist/tools/reflect.js +1 -1
- package/mcp-server/dist/tools/reflect.js.map +1 -1
- package/mcp-server/dist/tools/remember.d.ts +1 -1
- package/mcp-server/dist/tools/remember.d.ts.map +1 -1
- package/mcp-server/dist/tools/remember.js +13 -11
- package/mcp-server/dist/tools/remember.js.map +1 -1
- package/mcp-server/dist/tools/restore.d.ts +1 -1
- package/mcp-server/dist/tools/restore.d.ts.map +1 -1
- package/mcp-server/dist/tools/restore.js +1 -1
- package/mcp-server/dist/tools/restore.js.map +1 -1
- package/mcp-server/dist/tools/style-guide-tools.d.ts +1 -1
- package/mcp-server/dist/tools/style-guide-tools.d.ts.map +1 -1
- package/mcp-server/dist/tools/style-guide-tools.js +1 -1
- package/mcp-server/dist/tools/style-guide-tools.js.map +1 -1
- package/mcp-server/dist/tools/sync.d.ts +1 -1
- package/mcp-server/dist/tools/sync.d.ts.map +1 -1
- package/mcp-server/dist/tools/sync.js +1 -1
- package/mcp-server/dist/tools/sync.js.map +1 -1
- package/mcp-server/dist/tools/tictactoe-tools.d.ts +1 -1
- package/mcp-server/dist/tools/tictactoe-tools.d.ts.map +1 -1
- package/mcp-server/dist/tools/tictactoe-tools.js +1 -1
- package/mcp-server/dist/tools/tictactoe-tools.js.map +1 -1
- package/mcp-server/dist/tools/utils.d.ts +1 -1
- package/mcp-server/dist/tools/utils.d.ts.map +1 -1
- package/mcp-server/dist/tools/utils.js +1 -1
- package/mcp-server/dist/tools/utils.js.map +1 -1
- package/mcp-server/dist/tools/whatsapp-send-file.d.ts +1 -1
- package/mcp-server/dist/tools/whatsapp-send-file.d.ts.map +1 -1
- package/mcp-server/dist/tools/whatsapp-send-file.js +1 -1
- package/mcp-server/dist/tools/whatsapp-send-file.js.map +1 -1
- package/mcp-server/dist/tools/whiteboard-tools.d.ts +3 -3
- package/mcp-server/dist/tools/whiteboard-tools.d.ts.map +1 -1
- package/mcp-server/dist/tools/whiteboard-tools.js +1 -1
- package/mcp-server/dist/tools/whiteboard-tools.js.map +1 -1
- package/mcp-server/dist/tools/youtube/analytics.d.ts +1 -1
- package/mcp-server/dist/tools/youtube/analytics.d.ts.map +1 -1
- package/mcp-server/dist/tools/youtube/analytics.js +1 -1
- package/mcp-server/dist/tools/youtube/analytics.js.map +1 -1
- package/mcp-server/dist/tools/youtube/download.d.ts +1 -1
- package/mcp-server/dist/tools/youtube/download.d.ts.map +1 -1
- package/mcp-server/dist/tools/youtube/download.js +1 -1
- package/mcp-server/dist/tools/youtube/download.js.map +1 -1
- package/mcp-server/dist/tools/youtube/media.d.ts +1 -1
- package/mcp-server/dist/tools/youtube/media.d.ts.map +1 -1
- package/mcp-server/dist/tools/youtube/media.js +1 -1
- package/mcp-server/dist/tools/youtube/media.js.map +1 -1
- package/mcp-server/dist/tools/youtube/metadata.d.ts +1 -1
- package/mcp-server/dist/tools/youtube/metadata.d.ts.map +1 -1
- package/mcp-server/dist/tools/youtube/metadata.js +1 -1
- package/mcp-server/dist/tools/youtube/metadata.js.map +1 -1
- package/mcp-server/dist/tools/youtube/navigate.d.ts +1 -1
- package/mcp-server/dist/tools/youtube/navigate.d.ts.map +1 -1
- package/mcp-server/dist/tools/youtube/navigate.js +1 -1
- package/mcp-server/dist/tools/youtube/navigate.js.map +1 -1
- package/mcp-server/dist/tools/youtube/playlist.d.ts +1 -1
- package/mcp-server/dist/tools/youtube/playlist.d.ts.map +1 -1
- package/mcp-server/dist/tools/youtube/playlist.js +1 -1
- package/mcp-server/dist/tools/youtube/playlist.js.map +1 -1
- package/mcp-server/dist/tools/youtube/schedule.d.ts +1 -1
- package/mcp-server/dist/tools/youtube/schedule.d.ts.map +1 -1
- package/mcp-server/dist/tools/youtube/schedule.js +1 -1
- package/mcp-server/dist/tools/youtube/schedule.js.map +1 -1
- package/mcp-server/dist/tools/youtube/source.d.ts +1 -1
- package/mcp-server/dist/tools/youtube/source.d.ts.map +1 -1
- package/mcp-server/dist/tools/youtube/source.js +1 -1
- package/mcp-server/dist/tools/youtube/source.js.map +1 -1
- package/mcp-server/dist/tools/youtube/upload.d.ts +1 -1
- package/mcp-server/dist/tools/youtube/upload.d.ts.map +1 -1
- package/mcp-server/dist/tools/youtube/upload.js +4 -9
- package/mcp-server/dist/tools/youtube/upload.js.map +1 -1
- package/mcp-server/dist/tui.js +16 -4
- package/mcp-server/dist/tui.js.map +1 -1
- package/mcp-server/package-lock.json +3 -3
- package/mcp-server/package.json +3 -3
- package/mcp-server/src/services/browser-bench.ts +1 -1
- package/mcp-server/src/services/categories.ts +77 -32
- package/mcp-server/src/services/desktop-bench.ts +1 -1
- package/mcp-server/src/tools/agents.ts +1 -1
- package/mcp-server/src/tools/bluesky.ts +1 -1
- package/mcp-server/src/tools/browser-advanced.ts +1 -1
- package/mcp-server/src/tools/browser-batch.ts +1 -1
- package/mcp-server/src/tools/browser-cheatsheet.ts +1 -1
- package/mcp-server/src/tools/browser-interact.ts +1 -1
- package/mcp-server/src/tools/browser-navigate.ts +1 -1
- package/mcp-server/src/tools/browser-observe.ts +1 -1
- package/mcp-server/src/tools/card-tools.ts +1 -1
- package/mcp-server/src/tools/category.ts +6 -1
- package/mcp-server/src/tools/choice.ts +1 -1
- package/mcp-server/src/tools/computer.ts +1 -1
- package/mcp-server/src/tools/discord-channel.ts +1 -1
- package/mcp-server/src/tools/discord-guild.ts +1 -1
- package/mcp-server/src/tools/discord-member.ts +1 -1
- package/mcp-server/src/tools/discord-message.ts +1 -1
- package/mcp-server/src/tools/discord-onboarding.ts +1 -1
- package/mcp-server/src/tools/discord-role.ts +1 -1
- package/mcp-server/src/tools/discord-thread.ts +1 -1
- package/mcp-server/src/tools/discord-webhook.ts +1 -1
- package/mcp-server/src/tools/fb-groups.ts +1 -1
- package/mcp-server/src/tools/forget.ts +1 -1
- package/mcp-server/src/tools/git-tools.ts +1 -1
- package/mcp-server/src/tools/gsc/experience.ts +1 -1
- package/mcp-server/src/tools/gsc/indexing.ts +1 -1
- package/mcp-server/src/tools/gsc/inspect.ts +1 -1
- package/mcp-server/src/tools/gsc/navigate.ts +1 -1
- package/mcp-server/src/tools/gsc/performance.ts +1 -1
- package/mcp-server/src/tools/gsc/settings.ts +1 -1
- package/mcp-server/src/tools/image-tools.ts +1 -1
- package/mcp-server/src/tools/leonardo-browser-tools.ts +1 -1
- package/mcp-server/src/tools/loop.ts +1 -1
- package/mcp-server/src/tools/memories.ts +1 -1
- package/mcp-server/src/tools/morelogin.ts +1 -1
- package/mcp-server/src/tools/profile.ts +1 -1
- package/mcp-server/src/tools/recall.ts +1 -1
- package/mcp-server/src/tools/reflect.ts +1 -1
- package/mcp-server/src/tools/remember.ts +13 -12
- package/mcp-server/src/tools/restore.ts +1 -1
- package/mcp-server/src/tools/style-guide-tools.ts +1 -1
- package/mcp-server/src/tools/sync.ts +1 -1
- package/mcp-server/src/tools/tictactoe-tools.ts +1 -1
- package/mcp-server/src/tools/utils.ts +1 -1
- package/mcp-server/src/tools/whatsapp-send-file.ts +1 -1
- package/mcp-server/src/tools/whiteboard-tools.ts +1 -1
- package/mcp-server/src/tools/youtube/analytics.ts +1 -1
- package/mcp-server/src/tools/youtube/download.ts +1 -1
- package/mcp-server/src/tools/youtube/media.ts +1 -1
- package/mcp-server/src/tools/youtube/metadata.ts +1 -1
- package/mcp-server/src/tools/youtube/navigate.ts +1 -1
- package/mcp-server/src/tools/youtube/playlist.ts +1 -1
- package/mcp-server/src/tools/youtube/schedule.ts +1 -1
- package/mcp-server/src/tools/youtube/source.ts +1 -1
- package/mcp-server/src/tools/youtube/upload.ts +4 -12
- package/mcp-server/src/tui.ts +22 -9
- package/neural-interface/i18n/en.json +236 -7
- package/neural-interface/i18n/pt-BR.json +238 -9
- package/neural-interface/lib/account-usage.js +5 -3
- package/neural-interface/lib/agent-sandbox-elevation.js +102 -0
- package/neural-interface/lib/agent-sandbox-setting.js +57 -0
- package/neural-interface/lib/app-controls-api.js +161 -0
- package/neural-interface/lib/assistant-api.js +40 -7
- package/neural-interface/lib/assistant-brains/opencode.js +13 -1
- package/neural-interface/lib/assistant-brains/opencode2-browser-policy/index.js +44 -0
- package/neural-interface/lib/assistant-brains/opencode2-route-gate/index.js +57 -0
- package/neural-interface/lib/assistant-catalog.js +17 -2
- package/neural-interface/lib/assistant-dispatch.js +3 -0
- package/neural-interface/lib/assistant-envelope.js +19 -1
- package/neural-interface/lib/assistant-route-gate-codex.js +3 -1
- package/neural-interface/lib/assistant-runtime.js +131 -8
- package/neural-interface/lib/claude-model-catalog.js +64 -14
- package/neural-interface/lib/codex-account-auth.js +4 -2
- package/neural-interface/lib/codex-accounts.js +14 -1
- package/neural-interface/lib/codex-settings-schema.js +111 -47
- package/neural-interface/lib/editor-completion-opencode.js +132 -35
- package/neural-interface/lib/exec-loop-command.js +11 -2
- package/neural-interface/lib/install-details.js +23 -5
- package/neural-interface/lib/launch-cache.js +293 -0
- package/neural-interface/lib/login-start.js +524 -0
- package/neural-interface/lib/native-loop-providers.js +24 -4
- package/neural-interface/lib/native-loop-runtime.js +19 -0
- package/neural-interface/lib/opencode-db-schema.js +75 -0
- package/neural-interface/lib/opencode-history.js +16 -1
- package/neural-interface/lib/opencode-protocol.js +173 -0
- package/neural-interface/lib/opencode-provider-view.js +81 -0
- package/neural-interface/lib/opencode-runtime-config.js +10 -3
- package/neural-interface/lib/opencode-serve-records.js +606 -0
- package/neural-interface/lib/opencode-v2-client.js +33 -6
- package/neural-interface/lib/opencode-v2-ws-requests.js +19 -5
- package/neural-interface/lib/opencode-worker-activity.js +229 -0
- package/neural-interface/lib/opencode2-client.js +760 -0
- package/neural-interface/lib/opencode2-proxy.js +116 -0
- package/neural-interface/lib/opencode2-translate.js +1008 -0
- package/neural-interface/lib/process-identity.js +361 -0
- package/neural-interface/lib/provider-panel-terminal-auth.js +3 -2
- package/neural-interface/lib/session-cache-builder.js +26 -5
- package/neural-interface/lib/session-indexer.js +3 -1
- package/neural-interface/lib/session-title-generator.js +20 -0
- package/neural-interface/lib/style-guide/api.js +4 -3
- package/neural-interface/lib/untrusted-file-headers.js +27 -0
- package/neural-interface/package-lock.json +1342 -44
- package/neural-interface/package.json +5 -1
- package/neural-interface/public/index.html +2 -2
- package/neural-interface/public/shared/account-menu.js +4 -1
- package/neural-interface/public/shared/account-usage.js +62 -2
- package/neural-interface/public/shared/account-view.js +2 -1
- package/neural-interface/public/shared/agent-runtime-options.js +55 -11
- package/neural-interface/public/shared/assistant/asst-account-menu.js +298 -0
- package/neural-interface/public/shared/assistant/asst-agents-dock.js +172 -17
- package/neural-interface/public/shared/assistant/asst-api.js +18 -3
- package/neural-interface/public/shared/assistant/asst-brain-picker.js +424 -152
- package/neural-interface/public/shared/assistant/asst-composer.js +1 -1
- package/neural-interface/public/shared/assistant/asst-hosts.js +49 -2
- package/neural-interface/public/shared/assistant/asst-menu.js +275 -77
- package/neural-interface/public/shared/assistant/asst-model-chip.js +135 -0
- package/neural-interface/public/shared/assistant/asst-panel.js +236 -35
- package/neural-interface/public/shared/assistant/asst-rack-styles.js +28 -10
- package/neural-interface/public/shared/assistant/asst-redact.js +186 -0
- package/neural-interface/public/shared/assistant/asst-render.js +382 -36
- package/neural-interface/public/shared/assistant/asst-run-stage.js +231 -0
- package/neural-interface/public/shared/assistant/asst-sanitize.js +193 -3
- package/neural-interface/public/shared/assistant/asst-scene-styles.js +273 -0
- package/neural-interface/public/shared/assistant/asst-scenes.js +528 -0
- package/neural-interface/public/shared/assistant/asst-sidepanel-styles.js +3 -15
- package/neural-interface/public/shared/assistant/asst-state.js +14 -0
- package/neural-interface/public/shared/assistant/asst-styles.js +109 -17
- package/neural-interface/public/shared/assistant/asst-tool-kinds.js +13 -1
- package/neural-interface/public/shared/cc-dropdown.js +480 -0
- package/neural-interface/public/shared/cdx/cdx-panel.js +14 -2
- package/neural-interface/public/shared/cdx/cdx-render.js +15 -3
- package/neural-interface/public/shared/cdx/cdx-styles.js +49 -5
- package/neural-interface/public/shared/cdx/cdx-tabs.js +152 -35
- package/neural-interface/public/shared/cp/cp-markdown.js +4 -57
- package/neural-interface/public/shared/data-cache.js +223 -0
- package/neural-interface/public/shared/desktop-bridge.js +11 -0
- package/neural-interface/public/shared/desktop-titlebar-view.js +78 -1
- package/neural-interface/public/shared/desktop-titlebar.js +214 -28
- package/neural-interface/public/shared/desktop-update-view.js +15 -5
- package/neural-interface/public/shared/file-links.js +73 -8
- package/neural-interface/public/shared/html-shell.js +74 -80
- package/neural-interface/public/shared/launch-prefetch.js +59 -0
- package/neural-interface/public/shared/ocp-v2/ocp-v2-caps.js +3 -2
- package/neural-interface/public/shared/ocp-v2/ocp-v2-childpanel.js +11 -3
- package/neural-interface/public/shared/ocp-v2/ocp-v2-modelpicker.js +54 -17
- package/neural-interface/public/shared/ocp-v2/ocp-v2-panel.js +97 -3
- package/neural-interface/public/shared/ocp-v2/ocp-v2-projectbar.js +3 -2
- package/neural-interface/public/shared/ocp-v2/ocp-v2-providers.js +11 -9
- package/neural-interface/public/shared/ocp-v2/ocp-v2-rehydrate.js +15 -4
- package/neural-interface/public/shared/ocp-v2/ocp-v2-render.js +1 -1
- package/neural-interface/public/shared/ocp-v2/ocp-v2-send-logic.js +401 -0
- package/neural-interface/public/shared/ocp-v2/ocp-v2-send.js +16 -9
- package/neural-interface/public/shared/ocp-v2/ocp-v2-sessions-logic.js +60 -0
- package/neural-interface/public/shared/ocp-v2/ocp-v2-styles.js +83 -5
- package/neural-interface/public/shared/ocp-v2/ocp-v2-ws.js +195 -19
- package/neural-interface/public/shared/opencode-providers.js +39 -0
- package/neural-interface/public/shared/schedules-view.js +318 -0
- package/neural-interface/public/shared/settings/settings-app-pane.js +335 -0
- package/neural-interface/public/shared/settings/settings-app.js +243 -0
- package/neural-interface/public/shared/settings/settings-data.js +88 -0
- package/neural-interface/public/shared/settings/settings-ia.js +17 -0
- package/neural-interface/public/shared/showcase-privacy.js +2 -1
- package/neural-interface/public/shared/sidepanel-session-status.js +49 -0
- package/neural-interface/public/shared/styles.css +543 -372
- package/neural-interface/public/shared/ui-assistant.js +27 -5
- package/neural-interface/public/shared/ui-automation-studio.js +67 -27
- package/neural-interface/public/shared/ui-bookmarks.js +2 -1
- package/neural-interface/public/shared/ui-claude-panel.js +126 -12
- package/neural-interface/public/shared/ui-codex-panel.js +1 -0
- package/neural-interface/public/shared/ui-detail.js +6 -6
- package/neural-interface/public/shared/ui-explorer.js +2 -0
- package/neural-interface/public/shared/ui-file-links.js +1 -0
- package/neural-interface/public/shared/ui-image-gallery.js +6 -5
- package/neural-interface/public/shared/ui-invite.js +18 -2
- package/neural-interface/public/shared/ui-link.js +9 -9
- package/neural-interface/public/shared/ui-menubar.js +232 -33
- package/neural-interface/public/shared/ui-native-loop-router.js +2 -0
- package/neural-interface/public/shared/ui-opencode-panel-v2.js +1 -0
- package/neural-interface/public/shared/ui-schedules-studio.js +2099 -869
- package/neural-interface/public/shared/ui-sessions.js +15 -4
- package/neural-interface/public/shared/ui-settings.js +87 -36
- package/neural-interface/public/shared/ui-sidebar.js +4 -4
- package/neural-interface/public/shared/ui-sidepanel-session-entry.js +13 -0
- package/neural-interface/public/shared/ui-sidepanel-sessions.js +55 -4
- package/neural-interface/public/shared/ui-sidepanel-tray.js +84 -8
- package/neural-interface/public/shared/ui-skills.js +70 -12
- package/neural-interface/public/shared/ui-sync.js +9 -0
- package/neural-interface/public/shared/ui-terminal.js +36 -20
- package/neural-interface/public/shared/ui-trash.js +8 -1
- package/neural-interface/public/shared/ui-tutorial-steps.js +7 -3
- package/neural-interface/public/shared/ui-tutorial.js +15 -5
- package/neural-interface/public/shared/ui-update.js +87 -16
- package/neural-interface/public/shared/ui-whiteboard.js +2 -1
- package/neural-interface/public/variant/2d/main.js +4 -1
- package/neural-interface/public/variant/3d/main.js +3 -0
- package/neural-interface/public/vendor/highlight/LICENSE +29 -0
- package/neural-interface/public/vendor/highlight/github-dark-dimmed-11.12.0.min.css +9 -0
- package/neural-interface/public/vendor/highlight/highlight-11.12.0.min.js +1262 -0
- package/neural-interface/public/vendor/highlight/manifest.json +5 -0
- package/neural-interface/public/vendor/html2canvas/LICENSE +22 -0
- package/neural-interface/public/vendor/html2canvas/html2canvas-1.4.1.min.js +20 -0
- package/neural-interface/public/vendor/html2canvas/manifest.json +4 -0
- package/neural-interface/public/vendor/marked/LICENSE.md +44 -0
- package/neural-interface/public/vendor/marked/manifest.json +4 -0
- package/neural-interface/public/vendor/marked/marked-14.1.4.esm.js +2502 -0
- package/neural-interface/public/vendor/mermaid/LICENSE +21 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/abnfDiagram-PAR5IXJI.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/architecture-7GRP2DOG-M7G3SWCO.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/architectureDiagram-2PNWDENW.mjs +36 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/blockDiagram-PUWGUBOA.mjs +129 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/c4Diagram-3NLNHZP5.mjs +38 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-2NMSUPJH.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-32MCUW2P.mjs +206 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-3NPNU5GP.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-3SMGFRGC.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-4CAJECRU.mjs +14 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-4OADUZ5Q.mjs +10 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-5IMINLNL.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-5IQCK5RY.mjs +3 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-5MVFYWFU.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-AQ6EADP3.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-AZZRMDJM.mjs +15 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-CLS4B6BI.mjs +168 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-DZP67EKU.mjs +70 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-EICP7K5X.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-GDY2UAB4.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-H7VHZCWX.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-IJBDOHL6.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-JFFVMJIA.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-JQRUD6KW.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-JZGDI5JK.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-KD6SPIPN.mjs +2 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-NF366YI6.mjs +231 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-NUDPKBEF.mjs +32 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-OJINMHTS.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-Q6NGPIAH.mjs +2 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-QJSWEUOL.mjs +132 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-R7JDMUKV.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-RCXAM2GK.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-RT4MAWR3.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-SFMINX4W.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-SLDQ463B.mjs +88 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-STOV2HOB.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-T5OCTHI4.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-TCEHX7R3.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-VQC2EELJ.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-VRZKL3EH.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-VTHDQ7BV.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-VY2OSTJR.mjs +2 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-WUBWJARI.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-YBMQ5JGJ.mjs +2 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-YDMLL4PJ.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-Z6V44ARX.mjs +62 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-ZJJ7VQJP.mjs +339 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-ZNLK7HRR.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-ZQN7UHIL.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-ZS6VBONO.mjs +174 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-ZUNWM646.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/classDiagram-FGAMPOII.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/classDiagram-v2-AI5RJEK4.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/cose-bilkent-Q72C2OJ3.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/cynefin-OW5HDTMX-6OCTWOGG.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/cynefinDiagram-2OOWUY4M.mjs +62 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/dagre-MPVFI544.mjs +4 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/diagram-AE4NWXFW.mjs +24 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/diagram-BUJID5UG.mjs +41 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/diagram-C44CZ5NI.mjs +24 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/diagram-EYOLEXXS.mjs +30 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/diagram-P7B6PWOJ.mjs +3 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/ebnfDiagram-FTPZ3PV6.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/erDiagram-H3ERR26V.mjs +99 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/eventmodeling-NTZA5JFV-WKSWQHFS.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/flowDiagram-YHGXBVSY.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/ganttDiagram-RWFTB7ED.mjs +292 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/gitGraph-4MIJSDKK-JH2HKYUO.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/gitGraphDiagram-MAR6GKOP.mjs +106 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/info-A6RAGUB7-3TX3OFWW.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/infoDiagram-LV547AFH.mjs +2 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/ishikawaDiagram-PUJQGNDS.mjs +70 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/journeyDiagram-EW5QJT4W.mjs +139 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/kanban-definition-JSQERAZB.mjs +89 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/katex-CZ4GXH2S.mjs +257 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/mindmap-definition-VBJCLQLM.mjs +96 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/packet-AYTQ26CC-MKII6P3Y.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/pegDiagram-7WILOQTJ.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/pie-WAS4IAKB-6B4OPMJ5.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/pieDiagram-EFA7CBSB.mjs +39 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/quadrantDiagram-WARVYA7P.mjs +7 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/radar-RG4KPBEZ-UPTR3XUL.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/railroad-74A4TZTK-MEFUPFMS.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/railroad-abnf-HS5TGJTU-R5YG2S5D.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/railroad-ebnf-LZEXJU2U-G5QEX2L6.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/railroad-peg-WCYAUIDC-H5I2ZRY2.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/railroadDiagram-5NIQVNLQ.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/requirementDiagram-SXREGOKZ.mjs +84 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/sankeyDiagram-VV74M2VP.mjs +40 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/sequenceDiagram-52ZFIFDL.mjs +162 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/sizeCapture-FGA5AGYP.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/stateDiagram-3QAKPIVI.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/stateDiagram-v2-HN2P4RDZ.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/swimlanes-QLVP42QD.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/swimlanesDiagram-JKPDBFCN.mjs +8 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/timeline-definition-GS45XAXW.mjs +120 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/treeView-Q6P3EWNA-ZRJGANNQ.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/treemap-WGGIJYW6-K64VN5AS.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/vennDiagram-QIQU2ZRJ.mjs +34 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/wardley-WFR3VGLG-D67OWCCH.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/wardleyDiagram-U6YNM66N.mjs +78 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/xychartDiagram-XJAZEDNM.mjs +7 -0
- package/neural-interface/public/vendor/mermaid/manifest.json +4 -0
- package/neural-interface/public/vendor/mermaid/mermaid-11.17.2.esm.min.mjs +14 -0
- package/neural-interface/scripts/build-vendor.mjs +84 -3
- package/neural-interface/scripts/rebuild-pty.js +14 -2
- package/neural-interface/server.js +1279 -372
- package/npm-shrinkwrap.json +4424 -0
- package/package.json +54 -2
- package/postinstall.js +61 -47
- package/preuninstall.js +8 -35
- package/setup.js +76 -44
- package/skills/synabun/modules/idea.md +1 -1
- package/skills/synabun/modules/schedule.md +2 -2
- package/updater.cmd +2 -2
- package/updater.mjs +97 -3
|
@@ -0,0 +1,318 @@
|
|
|
1
|
+
// SynaBun Neural Interface — what the Schedules window shows, without the page.
|
|
2
|
+
// Which schedules a view lists and in which order, how a schedule's state reads, and the cron
|
|
3
|
+
// in plain words. ui-schedules-studio.js draws it; tests import this file under Node, so it
|
|
4
|
+
// has no imports and never touches the DOM.
|
|
5
|
+
|
|
6
|
+
export const MINUTE = 60_000;
|
|
7
|
+
export const HOUR = 60 * MINUTE;
|
|
8
|
+
export const DAY = 24 * HOUR;
|
|
9
|
+
// A run this late is overdue, not merely due: the scheduler missed it.
|
|
10
|
+
export const OVERDUE_AFTER = 5 * MINUTE;
|
|
11
|
+
// "Up next" looks this far ahead.
|
|
12
|
+
export const UP_NEXT_WINDOW = DAY;
|
|
13
|
+
|
|
14
|
+
const DAY_SHORT = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'];
|
|
15
|
+
|
|
16
|
+
export const VIEW_SCOPES = ['upcoming', 'all', 'attention', 'paused', 'timers', 'activity'];
|
|
17
|
+
export const LIST_SCOPES = ['upcoming', 'all', 'attention', 'paused'];
|
|
18
|
+
export const SORTS = ['next', 'name', 'last'];
|
|
19
|
+
|
|
20
|
+
const pad = (n) => String(n).padStart(2, '0');
|
|
21
|
+
const time = (t) => (Number.isFinite(t) ? t : NaN);
|
|
22
|
+
const parse = (iso) => (iso ? time(Date.parse(iso)) : NaN);
|
|
23
|
+
|
|
24
|
+
/** "a", "a and b", "a, b and c". */
|
|
25
|
+
export function listJoin(items) {
|
|
26
|
+
const list = items.filter(Boolean);
|
|
27
|
+
if (list.length <= 1) return list.join('');
|
|
28
|
+
return `${list.slice(0, -1).join(', ')} and ${list.at(-1)}`;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function dayList(field) {
|
|
32
|
+
if (field === '*' || field === '?') return 'Every day';
|
|
33
|
+
if (field === '1-5') return 'Weekdays';
|
|
34
|
+
if (field === '0,6' || field === '6,0' || field === '6,7') return 'Weekends';
|
|
35
|
+
const parts = field.split(',');
|
|
36
|
+
const names = [];
|
|
37
|
+
for (const part of parts) {
|
|
38
|
+
const range = part.match(/^(\d)-(\d)$/);
|
|
39
|
+
if (range) {
|
|
40
|
+
const from = DAY_SHORT[+range[1] % 7], to = DAY_SHORT[+range[2] % 7];
|
|
41
|
+
if (!from || !to) return null;
|
|
42
|
+
names.push(`${from}–${to}`);
|
|
43
|
+
} else if (/^\d$/.test(part)) {
|
|
44
|
+
names.push(DAY_SHORT[+part % 7]);
|
|
45
|
+
} else {
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
return listJoin(names);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function timesOf(minute, hour) {
|
|
53
|
+
const m = /^\d{1,2}$/.test(minute) ? +minute : null;
|
|
54
|
+
if (minute === '*' && hour === '*') return { every: 'every minute' };
|
|
55
|
+
const minStep = minute.match(/^\*\/(\d+)$/);
|
|
56
|
+
if (minStep && hour === '*') return { every: `every ${minStep[1]} minutes` };
|
|
57
|
+
if (m == null) {
|
|
58
|
+
// Several minutes in one hour: "at 09:00 and 09:30".
|
|
59
|
+
if (/^\d{1,2}(,\d{1,2})+$/.test(minute) && /^\d{1,2}$/.test(hour)) {
|
|
60
|
+
return { at: listJoin(minute.split(',').map(x => `${pad(+hour)}:${pad(+x)}`)) };
|
|
61
|
+
}
|
|
62
|
+
return null;
|
|
63
|
+
}
|
|
64
|
+
if (hour === '*') return { every: `every hour at :${pad(m)}` };
|
|
65
|
+
const hourStep = hour.match(/^\*\/(\d+)$/);
|
|
66
|
+
if (hourStep) return { every: `every ${hourStep[1]} hours at :${pad(m)}` };
|
|
67
|
+
const hourRange = hour.match(/^(\d{1,2})-(\d{1,2})$/);
|
|
68
|
+
if (hourRange) return { every: `every hour from ${pad(+hourRange[1])}:${pad(m)} to ${pad(+hourRange[2])}:${pad(m)}` };
|
|
69
|
+
if (/^\d{1,2}(,\d{1,2})*$/.test(hour)) return { at: listJoin(hour.split(',').map(h => `${pad(+h)}:${pad(m)}`)) };
|
|
70
|
+
return null;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* A five-field cron in plain words: "Every day at 06:55, 14:55 and 20:55", "Weekdays at 09:00",
|
|
75
|
+
* "Every 15 minutes". Anything it cannot say plainly comes back as the expression itself.
|
|
76
|
+
*/
|
|
77
|
+
export function describeCron(cron) {
|
|
78
|
+
const expr = String(cron ?? '').trim();
|
|
79
|
+
const fields = expr.split(/\s+/);
|
|
80
|
+
if (fields.length !== 5) return expr;
|
|
81
|
+
const [minute, hour, dom, month, dow] = fields;
|
|
82
|
+
if (month !== '*') return expr;
|
|
83
|
+
let days;
|
|
84
|
+
if (dom === '*' || dom === '?') days = dayList(dow);
|
|
85
|
+
else if (dow === '*' && /^\d{1,2}(,\d{1,2})*$/.test(dom)) {
|
|
86
|
+
const dates = dom.split(',').map(Number);
|
|
87
|
+
days = `${dates.length > 1 ? 'Days' : 'Day'} ${listJoin(dates)} of every month`;
|
|
88
|
+
} else return expr;
|
|
89
|
+
const times = timesOf(minute, hour);
|
|
90
|
+
if (!days || !times) return expr;
|
|
91
|
+
if (times.at) return `${days} at ${times.at}`;
|
|
92
|
+
const every = times.every.charAt(0).toUpperCase() + times.every.slice(1);
|
|
93
|
+
return days === 'Every day' ? every : `${days}, ${times.every}`;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/** One line of text from a run result: no newlines, no runs of spaces. */
|
|
97
|
+
export function oneLine(text) {
|
|
98
|
+
return String(text ?? '').replace(/\s+/g, ' ').trim();
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* How the last run went, apart from whether the schedule is on: `kind` is ok, failed, started,
|
|
103
|
+
* stopped, interrupted, never or other; `label` is the word to show; `reason` the server's words.
|
|
104
|
+
*/
|
|
105
|
+
export function lastRunState(schedule) {
|
|
106
|
+
const raw = schedule?.lastRunResult;
|
|
107
|
+
if (raw == null || raw === '') return { kind: 'never', label: 'Not run yet', reason: '' };
|
|
108
|
+
const text = String(raw);
|
|
109
|
+
if (text === 'completed') return { kind: 'ok', label: 'Completed', reason: '' };
|
|
110
|
+
if (text === 'launched' || text === 'running' || text === 'started') return { kind: 'started', label: 'Started', reason: '' };
|
|
111
|
+
if (text === 'template_missing') return { kind: 'failed', label: 'Automation missing', reason: 'The automation this schedule launches no longer exists.' };
|
|
112
|
+
const colon = text.indexOf(':');
|
|
113
|
+
const head = (colon >= 0 ? text.slice(0, colon) : text).trim();
|
|
114
|
+
const reason = colon >= 0 ? text.slice(colon + 1).trim() : '';
|
|
115
|
+
if (head === 'error') return { kind: 'failed', label: 'Failed', reason: reason || 'The run failed.' };
|
|
116
|
+
if (head === 'stopped') return { kind: 'stopped', label: 'Stopped', reason: reason === 'user' ? 'Stopped by you' : reason };
|
|
117
|
+
if (head === 'interrupted') return { kind: 'interrupted', label: 'Interrupted', reason };
|
|
118
|
+
return { kind: 'other', label: head.charAt(0).toUpperCase() + head.slice(1), reason };
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
export const isFailing = (schedule) => lastRunState(schedule).kind === 'failed';
|
|
122
|
+
|
|
123
|
+
export function isOverdue(schedule, now = Date.now()) {
|
|
124
|
+
const next = parse(schedule?.nextRun);
|
|
125
|
+
return !!schedule?.enabled && Number.isFinite(next) && next < now - OVERDUE_AFTER;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/** An active schedule whose last run failed, or that missed its time. A paused one waits for you. */
|
|
129
|
+
export function needsAttention(schedule, now = Date.now()) {
|
|
130
|
+
return !!schedule?.enabled && (isFailing(schedule) || isOverdue(schedule, now));
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
export function isUpcoming(schedule, now = Date.now(), within = UP_NEXT_WINDOW) {
|
|
134
|
+
const next = parse(schedule?.nextRun);
|
|
135
|
+
return !!schedule?.enabled && Number.isFinite(next) && next >= now - OVERDUE_AFTER && next <= now + within;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
const groupIdOf = (scope) => (typeof scope === 'string' && scope.startsWith('group:') ? scope.slice(6) : null);
|
|
139
|
+
export { groupIdOf };
|
|
140
|
+
|
|
141
|
+
/** Does a schedule belong to a list scope (a view, a group, or ungrouped)? */
|
|
142
|
+
export function inScope(schedule, scope, { now = Date.now(), groupIds = null } = {}) {
|
|
143
|
+
const groupId = groupIdOf(scope);
|
|
144
|
+
if (groupId) return schedule.groupId === groupId;
|
|
145
|
+
switch (scope) {
|
|
146
|
+
case 'upcoming': return isUpcoming(schedule, now);
|
|
147
|
+
case 'attention': return needsAttention(schedule, now);
|
|
148
|
+
case 'paused': return !schedule.enabled;
|
|
149
|
+
case 'ungrouped': return !schedule.groupId || (groupIds ? !groupIds.has(schedule.groupId) : false);
|
|
150
|
+
default: return true;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/** The counts the rail shows next to each view. */
|
|
155
|
+
export function scopeCounts(schedules, now = Date.now()) {
|
|
156
|
+
const counts = { upcoming: 0, all: schedules.length, attention: 0, paused: 0, active: 0, failing: 0 };
|
|
157
|
+
for (const s of schedules) {
|
|
158
|
+
if (isUpcoming(s, now)) counts.upcoming++;
|
|
159
|
+
if (needsAttention(s, now)) counts.attention++;
|
|
160
|
+
if (s.enabled) counts.active++; else counts.paused++;
|
|
161
|
+
if (isFailing(s)) counts.failing++;
|
|
162
|
+
}
|
|
163
|
+
return counts;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/** How many of a group's schedules are on, paused and need attention. */
|
|
167
|
+
export function groupSummary(schedules, now = Date.now()) {
|
|
168
|
+
const summary = { total: schedules.length, active: 0, paused: 0, attention: 0 };
|
|
169
|
+
for (const s of schedules) {
|
|
170
|
+
if (s.enabled) summary.active++; else summary.paused++;
|
|
171
|
+
if (needsAttention(s, now)) summary.attention++;
|
|
172
|
+
}
|
|
173
|
+
return summary;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/** Lower case, accents gone: "Ação" finds "acao". */
|
|
177
|
+
export function fold(text) {
|
|
178
|
+
return String(text ?? '').normalize('NFD').replace(/[̀-ͯ]/g, '').toLowerCase();
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Every word of the query must appear in the schedule's name, its automation, its group, its
|
|
183
|
+
* cadence or its model. `extra` adds the words the caller knows (automation and group names).
|
|
184
|
+
*/
|
|
185
|
+
export function matchesQuery(schedule, query, extra = []) {
|
|
186
|
+
const terms = fold(query).split(/\s+/).filter(Boolean);
|
|
187
|
+
if (!terms.length) return true;
|
|
188
|
+
const haystack = fold([
|
|
189
|
+
schedule.name, schedule.cron, describeCron(schedule.cron), schedule.model, schedule.profile, ...extra,
|
|
190
|
+
].filter(Boolean).join(' \u0000 '));
|
|
191
|
+
return terms.every(term => haystack.includes(term));
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
const byName = (a, b) => String(a.name || '').localeCompare(String(b.name || ''), undefined, { numeric: true, sensitivity: 'base' });
|
|
195
|
+
|
|
196
|
+
/** next: active first, soonest first. name: A to Z. last: most recent run first. */
|
|
197
|
+
export function sortSchedules(list, sort = 'next') {
|
|
198
|
+
const copy = [...list];
|
|
199
|
+
if (sort === 'name') return copy.sort(byName);
|
|
200
|
+
if (sort === 'last') {
|
|
201
|
+
return copy.sort((a, b) => {
|
|
202
|
+
const at = parse(a.lastRun), bt = parse(b.lastRun);
|
|
203
|
+
const av = Number.isFinite(at) ? at : -Infinity, bv = Number.isFinite(bt) ? bt : -Infinity;
|
|
204
|
+
return av !== bv ? bv - av : byName(a, b);
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
return copy.sort((a, b) => {
|
|
208
|
+
if (!!a.enabled !== !!b.enabled) return a.enabled ? -1 : 1;
|
|
209
|
+
const at = parse(a.nextRun), bt = parse(b.nextRun);
|
|
210
|
+
const av = Number.isFinite(at) ? at : Infinity, bv = Number.isFinite(bt) ? bt : Infinity;
|
|
211
|
+
return av !== bv ? av - bv : byName(a, b);
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/** "in 45 s", "in 26 min", "in 3 h 5 min", "in 2 d 4 h"; "Due now"; "Overdue". */
|
|
216
|
+
export function formatCountdown(iso, now = Date.now()) {
|
|
217
|
+
const next = parse(iso);
|
|
218
|
+
if (!Number.isFinite(next)) return { text: 'Not scheduled', state: 'none' };
|
|
219
|
+
const diff = next - now;
|
|
220
|
+
if (diff <= 0) return diff > -OVERDUE_AFTER ? { text: 'Due now', state: 'due' } : { text: 'Overdue', state: 'overdue' };
|
|
221
|
+
if (diff < MINUTE) return { text: `in ${Math.ceil(diff / 1000)} s`, state: 'soon' };
|
|
222
|
+
if (diff < HOUR) return { text: `in ${Math.floor(diff / MINUTE)} min`, state: diff < 15 * MINUTE ? 'soon' : 'future' };
|
|
223
|
+
if (diff < DAY) {
|
|
224
|
+
const h = Math.floor(diff / HOUR), m = Math.floor((diff % HOUR) / MINUTE);
|
|
225
|
+
return { text: m ? `in ${h} h ${m} min` : `in ${h} h`, state: 'future' };
|
|
226
|
+
}
|
|
227
|
+
const d = Math.floor(diff / DAY), h = Math.floor((diff % DAY) / HOUR);
|
|
228
|
+
return { text: h ? `in ${d} d ${h} h` : `in ${d} d`, state: 'future' };
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/** "just now", "5 min ago", "2 h ago", "yesterday", "3 d ago". */
|
|
232
|
+
export function formatAgo(iso, now = Date.now()) {
|
|
233
|
+
const then = parse(iso);
|
|
234
|
+
if (!Number.isFinite(then)) return '';
|
|
235
|
+
const diff = Math.max(0, now - then);
|
|
236
|
+
if (diff < MINUTE) return 'just now';
|
|
237
|
+
if (diff < HOUR) return `${Math.floor(diff / MINUTE)} min ago`;
|
|
238
|
+
if (diff < DAY) return `${Math.floor(diff / HOUR)} h ago`;
|
|
239
|
+
const days = Math.floor(diff / DAY);
|
|
240
|
+
return days === 1 ? 'yesterday' : `${days} d ago`;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
const sameDay = (a, b) => a.getFullYear() === b.getFullYear() && a.getMonth() === b.getMonth() && a.getDate() === b.getDate();
|
|
244
|
+
// The window's words are English, so are its dates: "Wed 22 Oct", never half in another language.
|
|
245
|
+
const LOCALE = 'en-GB';
|
|
246
|
+
|
|
247
|
+
/** "Today", "Tomorrow", "Yesterday" or the weekday and date, for a heading or a time. */
|
|
248
|
+
export function dayLabel(iso, now = Date.now(), locale = LOCALE) {
|
|
249
|
+
const then = new Date(parse(iso));
|
|
250
|
+
if (Number.isNaN(then.getTime())) return '';
|
|
251
|
+
const today = new Date(now);
|
|
252
|
+
if (sameDay(then, today)) return 'Today';
|
|
253
|
+
if (sameDay(then, new Date(now + DAY))) return 'Tomorrow';
|
|
254
|
+
if (sameDay(then, new Date(now - DAY))) return 'Yesterday';
|
|
255
|
+
return then.toLocaleDateString(locale, { weekday: 'short', day: 'numeric', month: 'short' });
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
/** 24-hour clock, as the cron descriptions are: "14:55". */
|
|
259
|
+
export function clockTime(iso, locale = LOCALE) {
|
|
260
|
+
const then = new Date(parse(iso));
|
|
261
|
+
if (Number.isNaN(then.getTime())) return '';
|
|
262
|
+
return then.toLocaleTimeString(locale, { hour: '2-digit', minute: '2-digit', hourCycle: 'h23' });
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
/** "Today 14:55", "Tomorrow 09:00", "Wed 22 Oct 09:00". */
|
|
266
|
+
export function whenLabel(iso, now = Date.now(), locale = LOCALE) {
|
|
267
|
+
const day = dayLabel(iso, now, locale);
|
|
268
|
+
const clock = clockTime(iso, locale);
|
|
269
|
+
return day && clock ? `${day} ${clock}` : '';
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
/** A run time in the fewest words: "14:55" today, "Tomorrow 09:00", "Wed 09:00" this week, then "22 Oct". */
|
|
273
|
+
export function shortWhen(iso, now = Date.now(), locale = LOCALE) {
|
|
274
|
+
const at = parse(iso);
|
|
275
|
+
if (!Number.isFinite(at)) return '';
|
|
276
|
+
const then = new Date(at);
|
|
277
|
+
const clock = clockTime(iso, locale);
|
|
278
|
+
if (sameDay(then, new Date(now))) return clock;
|
|
279
|
+
if (sameDay(then, new Date(now + DAY))) return `Tomorrow ${clock}`;
|
|
280
|
+
if (at > now && at - now < 6 * DAY) return `${then.toLocaleDateString(locale, { weekday: 'short' })} ${clock}`;
|
|
281
|
+
return then.toLocaleDateString(locale, { day: 'numeric', month: 'short' });
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
/**
|
|
285
|
+
* The launch settings a run will use and where each comes from: the schedule's own value wins,
|
|
286
|
+
* then its group's, then the automation's. `labels` names CLIs (`profiles`) and MCP presets.
|
|
287
|
+
*/
|
|
288
|
+
export function launchFacts(schedule, { group = null, template = null, profiles = [], mcpLabels = {} } = {}) {
|
|
289
|
+
const pick = (field) => {
|
|
290
|
+
for (const [source, owner] of [['schedule', schedule], ['group', group], ['automation', template]]) {
|
|
291
|
+
const value = owner?.[field];
|
|
292
|
+
if (value !== undefined && value !== null && value !== '') return { value, source };
|
|
293
|
+
}
|
|
294
|
+
return { value: null, source: 'default' };
|
|
295
|
+
};
|
|
296
|
+
const facts = [];
|
|
297
|
+
const profile = pick('profile');
|
|
298
|
+
const profileId = profile.value || 'claude-code';
|
|
299
|
+
facts.push({ key: 'profile', label: 'CLI', value: profiles.find(p => p.id === profileId)?.label || profileId, source: profile.value ? profile.source : 'default' });
|
|
300
|
+
const model = pick('model');
|
|
301
|
+
facts.push({ key: 'model', label: 'Model', value: model.value || 'Default', source: model.source, mono: !!model.value });
|
|
302
|
+
const context = pick('contextMode');
|
|
303
|
+
if (context.value && context.value !== 'default') facts.push({ key: 'contextMode', label: 'Context', value: context.value === '1m' ? '1M tokens' : context.value.charAt(0).toUpperCase() + context.value.slice(1), source: context.source });
|
|
304
|
+
const effort = pick('effort');
|
|
305
|
+
if (effort.value && effort.value !== 'off') facts.push({ key: 'effort', label: 'Thinking', value: effort.value.charAt(0).toUpperCase() + effort.value.slice(1), source: effort.source });
|
|
306
|
+
const mcp = pick('mcpProfile');
|
|
307
|
+
if (mcp.value) {
|
|
308
|
+
const value = mcpLabels[mcp.value] || String(mcp.value).split(',').map(x => x.trim()).filter(Boolean).join(', ');
|
|
309
|
+
facts.push({ key: 'mcpProfile', label: 'MCP tools', value, source: mcp.source });
|
|
310
|
+
}
|
|
311
|
+
if (profileId === 'codex') {
|
|
312
|
+
const account = pick('codexAccountId');
|
|
313
|
+
if (account.value) facts.push({ key: 'codexAccountId', label: 'OpenAI account', value: account.value, source: account.source });
|
|
314
|
+
}
|
|
315
|
+
const browser = pick('usesBrowser');
|
|
316
|
+
facts.push({ key: 'usesBrowser', label: 'Browser', value: browser.value === true ? 'On' : 'Off', source: browser.source });
|
|
317
|
+
return facts;
|
|
318
|
+
}
|
|
@@ -0,0 +1,335 @@
|
|
|
1
|
+
// ═══════════════════════════════════════════
|
|
2
|
+
// SETTINGS — App (the pane)
|
|
3
|
+
// ═══════════════════════════════════════════
|
|
4
|
+
//
|
|
5
|
+
// The software's own controls, in four sections: Updates (the version, the last
|
|
6
|
+
// check, Check for updates, Install update, automatic updates), Startup (start at
|
|
7
|
+
// login, start in the background), Window (what the close button does, hardware
|
|
8
|
+
// acceleration) and Maintenance (the data and logs folders, Restart SynaBun).
|
|
9
|
+
//
|
|
10
|
+
// Inside the desktop app the settings are the app's (window.synabunDesktop.app,
|
|
11
|
+
// desktop-bridge.js desktopApp); in an npm or Git install they are the server's
|
|
12
|
+
// (GET /api/system/app). Updates go through ui-update.js, the same check and the
|
|
13
|
+
// same dialog as the update badge. Restart SynaBun is the Apps menu's own item
|
|
14
|
+
// (#menu-restart-server). settings-app.js decides what each row shows; this draws.
|
|
15
|
+
|
|
16
|
+
import * as kit from './settings-kit.js';
|
|
17
|
+
import { desktopApp } from '../desktop-bridge.js';
|
|
18
|
+
import { on } from '../state.js';
|
|
19
|
+
import { forceCheckSynabunUpdate, getSynabunUpdateData, noteSynabunAutoCheck, openSynabunUpdateModal, refreshSynabunUpdateData } from '../ui-update.js';
|
|
20
|
+
import {
|
|
21
|
+
APP_URL, autoUpdateRow, canInstallUpdate, checkedLine, guessPlatform, hiddenRow, loginRow, maintenanceRow, readDesktopAppState,
|
|
22
|
+
readServerAppState, serverRefusal, updateStatus, windowRows,
|
|
23
|
+
} from './settings-app.js';
|
|
24
|
+
|
|
25
|
+
const RESTART_MENU_ITEM = 'menu-restart-server';
|
|
26
|
+
const WRITE = Object.freeze({ 'Content-Type': 'application/json', 'X-SynaBun-UI': '1', Accept: 'application/json' });
|
|
27
|
+
|
|
28
|
+
const row = (id) => `<div class="stg-help stg-app-note" id="${id}" role="status" hidden></div>`;
|
|
29
|
+
|
|
30
|
+
export function buildAppTab() {
|
|
31
|
+
return `
|
|
32
|
+
<div class="stg-pane stg-app" data-stg-pane="app">
|
|
33
|
+
${kit.card({
|
|
34
|
+
id: 'stg-sec-app-updates', section: 'app.updates',
|
|
35
|
+
status: kit.pill('', { id: 'stg-app-update-pill' }),
|
|
36
|
+
body: `
|
|
37
|
+
${kit.toggleField('SYS001', `
|
|
38
|
+
<button type="button" class="stg-action-btn" id="stg-app-install" ${kit.inv('SYS003')} title="${kit.H('SYS003')}" hidden>${kit.L('SYS003')}</button>
|
|
39
|
+
<button type="button" class="stg-action-btn" id="stg-app-check" title="${kit.H('SYS001')}">${kit.L('SYS001')}</button>`, {
|
|
40
|
+
label: `<span id="stg-app-version">${kit.te('settings.redesign.about.loading')}</span>`,
|
|
41
|
+
helpHtml: '<span id="stg-app-update-line" aria-live="polite"></span>',
|
|
42
|
+
})}
|
|
43
|
+
${kit.toggleField('SYS002', kit.switchInput('stg-app-auto-update'), { forId: 'stg-app-auto-update', helpHtml: '<span id="stg-app-auto-help"></span>' })}`,
|
|
44
|
+
})}
|
|
45
|
+
${kit.card({
|
|
46
|
+
id: 'stg-sec-app-startup', section: 'app.startup',
|
|
47
|
+
body: `
|
|
48
|
+
${kit.toggleField('SYS004', `
|
|
49
|
+
<button type="button" class="stg-action-btn compact" id="stg-app-login-settings" hidden></button>
|
|
50
|
+
${kit.switchInput('stg-app-login')}`, { forId: 'stg-app-login', helpHtml: '<span id="stg-app-login-help"></span>' })}
|
|
51
|
+
${row('stg-app-login-note')}
|
|
52
|
+
${kit.toggleField('SYS005', kit.switchInput('stg-app-hidden'), { forId: 'stg-app-hidden', attrs: 'id="stg-app-hidden-row" hidden' })}
|
|
53
|
+
${row('stg-app-hidden-note')}`,
|
|
54
|
+
})}
|
|
55
|
+
${kit.card({
|
|
56
|
+
id: 'stg-sec-app-window', section: 'app.window',
|
|
57
|
+
body: `
|
|
58
|
+
${row('stg-app-window-note')}
|
|
59
|
+
${kit.toggleField('SYS006', kit.switchInput('stg-app-close-tray'), { forId: 'stg-app-close-tray' })}
|
|
60
|
+
${row('stg-app-close-note')}
|
|
61
|
+
${kit.toggleField('SYS007', `
|
|
62
|
+
<button type="button" class="stg-action-btn compact" id="stg-app-relaunch" ${kit.inv('SYS008')} title="${kit.H('SYS008')}" hidden>${kit.L('SYS008')}</button>
|
|
63
|
+
${kit.switchInput('stg-app-gpu')}`, { forId: 'stg-app-gpu' })}
|
|
64
|
+
${row('stg-app-gpu-note')}`,
|
|
65
|
+
})}
|
|
66
|
+
${kit.card({
|
|
67
|
+
id: 'stg-sec-app-maintenance', section: 'app.maintenance',
|
|
68
|
+
body: `
|
|
69
|
+
<div class="stg-action-row">
|
|
70
|
+
<button type="button" class="stg-action-btn" id="stg-app-open-data" ${kit.inv('SYS009')} title="${kit.H('SYS009')}">${kit.L('SYS009')}</button>
|
|
71
|
+
<button type="button" class="stg-action-btn" id="stg-app-open-logs" ${kit.inv('SYS010')} title="${kit.H('SYS010')}">${kit.L('SYS010')}</button>
|
|
72
|
+
<button type="button" class="stg-action-btn" id="stg-app-restart" ${kit.inv('SYS011')} title="${kit.H('SYS011')}">${kit.L('SYS011')}</button>
|
|
73
|
+
</div>
|
|
74
|
+
${row('stg-app-maint-note')}`,
|
|
75
|
+
})}
|
|
76
|
+
</div>`;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Load and draw the page the first time it is shown (and on every return to it), and wire it.
|
|
81
|
+
* @param {HTMLElement} overlay the Settings panel
|
|
82
|
+
* @param {Object} [o]
|
|
83
|
+
* @param {Function} [o.fetchImpl] fetch (tests)
|
|
84
|
+
* @param {(msg: string) => void} [o.toast]
|
|
85
|
+
* @param {Window} [o.win] where the desktop app's bridge and the user agent are read
|
|
86
|
+
*/
|
|
87
|
+
export function wireAppTab(overlay, { fetchImpl = (...args) => fetch(...args), toast = () => {}, win = window } = {}) {
|
|
88
|
+
const pane = kit.paneOf(overlay, 'app');
|
|
89
|
+
if (!pane) return;
|
|
90
|
+
const $ = (id) => pane.querySelector(`#${id}`);
|
|
91
|
+
const bridge = desktopApp(win);
|
|
92
|
+
const platformGuess = guessPlatform(win.navigator?.userAgent);
|
|
93
|
+
let desktop = null; // readDesktopAppState()
|
|
94
|
+
let server = null; // readServerAppState()
|
|
95
|
+
let remote = false; // the server refused: this page is not on the computer that runs SynaBun
|
|
96
|
+
let update = getSynabunUpdateData();
|
|
97
|
+
let busy = false;
|
|
98
|
+
// Nothing is said before the first answer: a row that waits for it is off, without a note.
|
|
99
|
+
let loaded = false;
|
|
100
|
+
|
|
101
|
+
const time = (iso) => {
|
|
102
|
+
try { return new Date(iso).toLocaleTimeString(undefined, { hour: '2-digit', minute: '2-digit' }); } catch { return String(iso).slice(11, 16); }
|
|
103
|
+
};
|
|
104
|
+
const say = (node, key, params) => {
|
|
105
|
+
if (!node) return;
|
|
106
|
+
const textValue = key ? kit.tx(key, params) : '';
|
|
107
|
+
node.textContent = textValue;
|
|
108
|
+
node.hidden = !textValue;
|
|
109
|
+
};
|
|
110
|
+
const setSwitch = (id, { checked, disabled }) => {
|
|
111
|
+
const input = $(id);
|
|
112
|
+
if (!input) return;
|
|
113
|
+
input.checked = !!checked;
|
|
114
|
+
input.disabled = !!disabled || busy;
|
|
115
|
+
};
|
|
116
|
+
const tone = (node, value) => { if (node) node.dataset.state = value || ''; };
|
|
117
|
+
|
|
118
|
+
// ── Drawing ──
|
|
119
|
+
|
|
120
|
+
function drawUpdates() {
|
|
121
|
+
const version = update?.current || desktop?.version || '';
|
|
122
|
+
say($('stg-app-version'), version ? 'settings.redesign.app.version' : 'settings.redesign.about.loading', { version });
|
|
123
|
+
const status = updateStatus(update);
|
|
124
|
+
const line = [kit.tx(status.key, status.params), checkedLine(update, { tx: kit.tx, time })].filter(Boolean).join(' · ');
|
|
125
|
+
const lineNode = $('stg-app-update-line');
|
|
126
|
+
if (lineNode) lineNode.textContent = line;
|
|
127
|
+
const pill = $('stg-app-update-pill');
|
|
128
|
+
if (pill) { pill.textContent = kit.tx(status.key, status.params); pill.dataset.state = status.tone; }
|
|
129
|
+
const install = $('stg-app-install');
|
|
130
|
+
if (install) install.hidden = !canInstallUpdate(update);
|
|
131
|
+
const auto = autoUpdateRow({ desktop, server, update });
|
|
132
|
+
setSwitch('stg-app-auto-update', auto);
|
|
133
|
+
say($('stg-app-auto-help'), auto.helpKey);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function drawStartup() {
|
|
137
|
+
const login = loginRow({ desktop, server, remote, platform: desktop?.platform || platformGuess });
|
|
138
|
+
setSwitch('stg-app-login', login);
|
|
139
|
+
say($('stg-app-login-help'), login.helpKey);
|
|
140
|
+
const note = $('stg-app-login-note');
|
|
141
|
+
say(note, login.note?.key, login.note?.params);
|
|
142
|
+
tone(note, login.note?.tone);
|
|
143
|
+
const open = $('stg-app-login-settings');
|
|
144
|
+
if (open) {
|
|
145
|
+
open.hidden = !(bridge && login.settingsButton);
|
|
146
|
+
if (login.settingsButton) open.textContent = kit.tx(`settings.redesign.app.startup.openSettings.${login.settingsButton}`);
|
|
147
|
+
}
|
|
148
|
+
const hidden = hiddenRow({ desktop });
|
|
149
|
+
const hiddenField = $('stg-app-hidden-row');
|
|
150
|
+
if (hiddenField) hiddenField.hidden = !hidden.shown;
|
|
151
|
+
setSwitch('stg-app-hidden', hidden);
|
|
152
|
+
say($('stg-app-hidden-note'), hidden.shown ? hidden.noteKey : '');
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
function drawWindow() {
|
|
156
|
+
const rows = windowRows({ desktop });
|
|
157
|
+
say($('stg-app-window-note'), rows.reasonKey);
|
|
158
|
+
setSwitch('stg-app-close-tray', rows.closeToTray);
|
|
159
|
+
say($('stg-app-close-note'), rows.closeToTray.noteKey);
|
|
160
|
+
setSwitch('stg-app-gpu', rows.gpu);
|
|
161
|
+
const relaunch = $('stg-app-relaunch');
|
|
162
|
+
if (relaunch) relaunch.hidden = !rows.gpu.restartNeeded;
|
|
163
|
+
say($('stg-app-gpu-note'), rows.gpu.restartNeeded ? 'settings.redesign.app.window.restartNeeded' : '');
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
function drawMaintenance() {
|
|
167
|
+
const maint = maintenanceRow({ desktop, server, remote, restartAvailable: !!document.getElementById(RESTART_MENU_ITEM) });
|
|
168
|
+
for (const id of ['stg-app-open-data', 'stg-app-open-logs']) { const btn = $(id); if (btn) btn.disabled = !maint.folders || busy; }
|
|
169
|
+
const restart = $('stg-app-restart');
|
|
170
|
+
if (restart) restart.hidden = !maint.restart;
|
|
171
|
+
const note = $('stg-app-maint-note');
|
|
172
|
+
if (note && !note.dataset.error) say(note, maint.noteKey);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
function draw() {
|
|
176
|
+
if (!loaded) {
|
|
177
|
+
pane.querySelectorAll('input[type="checkbox"]').forEach((input) => { input.disabled = true; });
|
|
178
|
+
return;
|
|
179
|
+
}
|
|
180
|
+
drawUpdates();
|
|
181
|
+
drawStartup();
|
|
182
|
+
drawWindow();
|
|
183
|
+
drawMaintenance();
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// ── Loading ──
|
|
187
|
+
|
|
188
|
+
async function readServer() {
|
|
189
|
+
try {
|
|
190
|
+
const res = await fetchImpl(APP_URL, { headers: { Accept: 'application/json' } });
|
|
191
|
+
const body = await res.json().catch(() => null);
|
|
192
|
+
if (res.ok) { server = readServerAppState(body); remote = false; return; }
|
|
193
|
+
remote = serverRefusal(res.status, body) === 'remote';
|
|
194
|
+
server = null;
|
|
195
|
+
} catch {
|
|
196
|
+
server = null;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* The update state as it is, without a check of its own: the desktop app's last report, or the server's last
|
|
202
|
+
* look (GET /api/system/version without ?force: with automatic updates off, opening this page asks nothing).
|
|
203
|
+
* Through ui-update.js, so the badge and Install update see the same offer this page shows.
|
|
204
|
+
*/
|
|
205
|
+
async function readUpdate() {
|
|
206
|
+
try { return (await refreshSynabunUpdateData()) || getSynabunUpdateData(); } catch { return getSynabunUpdateData(); }
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
async function load() {
|
|
210
|
+
if (bridge) {
|
|
211
|
+
try { desktop = readDesktopAppState(await bridge.state()); } catch { desktop = null; }
|
|
212
|
+
} else {
|
|
213
|
+
await readServer();
|
|
214
|
+
}
|
|
215
|
+
update = (await readUpdate()) || update;
|
|
216
|
+
loaded = true;
|
|
217
|
+
if (!document.body.contains(overlay)) return;
|
|
218
|
+
draw();
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
let loading = null;
|
|
222
|
+
const ensure = () => { if (!loading) loading = load().finally(() => { loading = null; }); return loading; };
|
|
223
|
+
draw();
|
|
224
|
+
if (kit.paneActive(overlay, 'app')) ensure();
|
|
225
|
+
overlay.addEventListener('stg-page-change', (e) => { if (e.detail?.page === 'app') ensure(); });
|
|
226
|
+
|
|
227
|
+
// The app's updater and its settings speak when they change (a check on its schedule, the login item put back).
|
|
228
|
+
const offUpdate = on('synabun-update:changed', (data) => { update = data || update; if (loaded && document.body.contains(overlay)) drawUpdates(); });
|
|
229
|
+
let offApp = null;
|
|
230
|
+
try {
|
|
231
|
+
offApp = bridge?.onChange?.((state) => {
|
|
232
|
+
const next = readDesktopAppState(state);
|
|
233
|
+
if (next && document.body.contains(overlay)) { desktop = next; draw(); }
|
|
234
|
+
});
|
|
235
|
+
} catch {}
|
|
236
|
+
const observer = new MutationObserver(() => {
|
|
237
|
+
if (document.body.contains(overlay)) return;
|
|
238
|
+
observer.disconnect();
|
|
239
|
+
try { offUpdate?.(); } catch {}
|
|
240
|
+
try { offApp?.(); } catch {}
|
|
241
|
+
});
|
|
242
|
+
observer.observe(document.body, { childList: true });
|
|
243
|
+
|
|
244
|
+
// ── Changes ──
|
|
245
|
+
|
|
246
|
+
/** One switch of the desktop app: the app answers with its whole state (and what the system said). */
|
|
247
|
+
async function setDesktop(key, value) {
|
|
248
|
+
try {
|
|
249
|
+
desktop = readDesktopAppState(await bridge.set(key, value)) || desktop;
|
|
250
|
+
} catch {
|
|
251
|
+
desktop = desktop ? { ...desktop, error: 'save-failed' } : desktop;
|
|
252
|
+
toast(kit.tx('settings.redesign.app.saveFailed'));
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
/** One switch of an npm or Git install. */
|
|
257
|
+
async function setServer(path, payload) {
|
|
258
|
+
try {
|
|
259
|
+
const res = await fetchImpl(`${APP_URL}/${path}`, { method: 'PUT', headers: WRITE, body: JSON.stringify(payload) });
|
|
260
|
+
const body = await res.json().catch(() => null);
|
|
261
|
+
const next = readServerAppState(body);
|
|
262
|
+
if (next) server = next;
|
|
263
|
+
else {
|
|
264
|
+
if (serverRefusal(res.status, body) === 'remote') remote = true;
|
|
265
|
+
toast(kit.tx('settings.redesign.app.saveFailed'));
|
|
266
|
+
}
|
|
267
|
+
return res.ok;
|
|
268
|
+
} catch {
|
|
269
|
+
if (server) server = { ...server, error: 'network' };
|
|
270
|
+
toast(kit.tx('settings.redesign.app.saveFailed'));
|
|
271
|
+
return false;
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
async function change(work) {
|
|
276
|
+
if (busy) return;
|
|
277
|
+
busy = true;
|
|
278
|
+
try { await work(); } finally { busy = false; draw(); }
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
$('stg-app-auto-update')?.addEventListener('change', (e) => change(async () => {
|
|
282
|
+
const on = e.target.checked;
|
|
283
|
+
if (desktop) {
|
|
284
|
+
await setDesktop('autoUpdate', on);
|
|
285
|
+
update = (await readUpdate()) || update;
|
|
286
|
+
} else if (await setServer('updates', { auto: on })) {
|
|
287
|
+
noteSynabunAutoCheck(on);
|
|
288
|
+
// Turned on: look now, as a start of the server would.
|
|
289
|
+
if (on) { try { update = (await forceCheckSynabunUpdate({ alert: false })) || update; } catch {} }
|
|
290
|
+
}
|
|
291
|
+
}));
|
|
292
|
+
|
|
293
|
+
$('stg-app-login')?.addEventListener('change', (e) => change(async () => {
|
|
294
|
+
if (desktop) await setDesktop('openAtLogin', e.target.checked);
|
|
295
|
+
else await setServer('startup', { enabled: e.target.checked });
|
|
296
|
+
}));
|
|
297
|
+
$('stg-app-hidden')?.addEventListener('change', (e) => change(() => setDesktop('startHidden', e.target.checked)));
|
|
298
|
+
$('stg-app-close-tray')?.addEventListener('change', (e) => change(() => setDesktop('closeToTray', e.target.checked)));
|
|
299
|
+
$('stg-app-gpu')?.addEventListener('change', (e) => change(() => setDesktop('hardwareAcceleration', e.target.checked)));
|
|
300
|
+
$('stg-app-relaunch')?.addEventListener('click', () => { try { bridge?.relaunch?.(); } catch {} });
|
|
301
|
+
$('stg-app-login-settings')?.addEventListener('click', () => { try { bridge?.openLoginSettings?.(); } catch {} });
|
|
302
|
+
|
|
303
|
+
$('stg-app-check')?.addEventListener('click', () => change(async () => {
|
|
304
|
+
const btn = $('stg-app-check');
|
|
305
|
+
if (btn) btn.disabled = true;
|
|
306
|
+
say($('stg-app-update-line'), 'settings.redesign.app.update.checking');
|
|
307
|
+
let found = null;
|
|
308
|
+
try { found = await forceCheckSynabunUpdate({ alert: false }); } catch {}
|
|
309
|
+
// A failed check answers null: the desktop app's report says why; for the server, the check failed.
|
|
310
|
+
if (found) update = found;
|
|
311
|
+
else if (bridge) update = (await readUpdate()) || update;
|
|
312
|
+
else update = { ...(update || {}), updateAvailable: false, checkedAt: new Date().toISOString(), npmError: 'failed', gitError: 'failed' };
|
|
313
|
+
if (btn) btn.disabled = false;
|
|
314
|
+
}));
|
|
315
|
+
$('stg-app-install')?.addEventListener('click', () => openSynabunUpdateModal());
|
|
316
|
+
|
|
317
|
+
async function openFolder(which) {
|
|
318
|
+
const note = $('stg-app-maint-note');
|
|
319
|
+
let ok = false;
|
|
320
|
+
if (bridge) {
|
|
321
|
+
try { ok = !!(await bridge.openFolder(which))?.ok; } catch { ok = false; }
|
|
322
|
+
} else {
|
|
323
|
+
try {
|
|
324
|
+
const res = await fetchImpl(`${APP_URL}/open-folder`, { method: 'POST', headers: WRITE, body: JSON.stringify({ which }) });
|
|
325
|
+
ok = res.ok;
|
|
326
|
+
} catch { ok = false; }
|
|
327
|
+
}
|
|
328
|
+
if (note) {
|
|
329
|
+
if (ok) { delete note.dataset.error; drawMaintenance(); } else { note.dataset.error = '1'; say(note, 'settings.redesign.app.maintenance.openFailed'); tone(note, 'err'); }
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
$('stg-app-open-data')?.addEventListener('click', () => openFolder('data'));
|
|
333
|
+
$('stg-app-open-logs')?.addEventListener('click', () => openFolder('logs'));
|
|
334
|
+
$('stg-app-restart')?.addEventListener('click', () => document.getElementById(RESTART_MENU_ITEM)?.click());
|
|
335
|
+
}
|