synabun 2.0.0 → 2.0.7
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 +97 -0
- package/NOTICE +5 -0
- package/README.md +2 -1
- package/THIRD-PARTY-LICENSES.md +59 -0
- package/benchmarks/fixtures/browser/README.md +1 -1
- package/benchmarks/fixtures/desktop/README.md +1 -1
- package/docs/whatsapp.md +153 -18
- package/hooks/claude-code/pre-compact.mjs +11 -0
- package/hooks/claude-code/prompt-submit.mjs +60 -184
- package/hooks/claude-code/session-start.mjs +73 -16
- package/hooks/claude-code/shared.mjs +26 -3
- package/hooks/claude-code/stop.mjs +5 -5
- 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/packaged-runtime.js +138 -11
- package/lib/start-launcher.js +88 -1
- 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/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/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/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 +10 -7
- package/mcp-server/dist/tools/agents.d.ts.map +1 -1
- package/mcp-server/dist/tools/agents.js +34 -13
- package/mcp-server/dist/tools/agents.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/tui.js +2 -1
- 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 +2 -2
- package/mcp-server/package.json +1 -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/budget-judgments.ts +183 -0
- package/mcp-server/src/services/codex-config-heal.ts +35 -4
- 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 +33 -14
- package/mcp-server/src/tools/whatsapp-send-file.ts +51 -0
- package/mcp-server/src/tui.ts +2 -1
- package/mcp-server/src/version.ts +22 -0
- package/neural-interface/i18n/en.json +2126 -209
- package/neural-interface/i18n/pt-BR.json +2123 -207
- package/neural-interface/lib/account-usage.js +163 -0
- package/neural-interface/lib/assistant-api.js +316 -57
- package/neural-interface/lib/assistant-brains/claude.js +19 -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-catalog.js +179 -27
- package/neural-interface/lib/assistant-config.js +91 -14
- package/neural-interface/lib/assistant-dispatch.js +494 -138
- package/neural-interface/lib/assistant-hidden-models.js +393 -66
- package/neural-interface/lib/assistant-jev.js +26 -3
- package/neural-interface/lib/assistant-memory.js +24 -10
- package/neural-interface/lib/assistant-persona.js +73 -12
- package/neural-interface/lib/assistant-plan-permissions.js +8 -1
- package/neural-interface/lib/assistant-pricing.js +21 -6
- package/neural-interface/lib/assistant-route-accounts.js +14 -0
- 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 +488 -59
- package/neural-interface/lib/assistant-task-prompt.js +13 -3
- package/neural-interface/lib/assistant-test-density.js +142 -0
- package/neural-interface/lib/assistant-user-context-budgets.js +123 -0
- package/neural-interface/lib/assistant-user-context.js +908 -0
- package/neural-interface/lib/assistant-user-knowledge.js +328 -0
- package/neural-interface/lib/assistant-user-learn.js +404 -0
- package/neural-interface/lib/assistant-user-threads.js +226 -0
- package/neural-interface/lib/assistant-user-writer.js +183 -0
- package/neural-interface/lib/augmented-path.js +317 -53
- package/neural-interface/lib/browser-background-tab.js +134 -0
- package/neural-interface/lib/browser-screenshot.js +678 -61
- package/neural-interface/lib/claude-accounts.js +342 -31
- package/neural-interface/lib/claude-agent-bridge.js +123 -9
- package/neural-interface/lib/claude-executable.js +6 -1
- package/neural-interface/lib/claude-model-catalog.js +33 -5
- 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/claude-temporary.js +46 -7
- 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/cloudflared/install.js +533 -0
- package/neural-interface/lib/cloudflared/manager.js +545 -0
- package/neural-interface/lib/cloudflared/pins.js +115 -0
- package/neural-interface/lib/cloudflared/pins.json +63 -0
- package/neural-interface/lib/cloudflared/tunnel.js +265 -0
- package/neural-interface/lib/codex-account-auth.js +178 -0
- package/neural-interface/lib/codex-accounts.js +489 -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 +120 -0
- package/neural-interface/lib/desktop/config.js +3 -0
- package/neural-interface/lib/editor-completion-endpoint.js +264 -0
- package/neural-interface/lib/editor-completion-opencode.js +436 -0
- package/neural-interface/lib/editor-completion.js +714 -0
- package/neural-interface/lib/editor-config.js +149 -0
- package/neural-interface/lib/editor-files-api.js +268 -0
- package/neural-interface/lib/editor-git-api.js +134 -0
- package/neural-interface/lib/file-explorer-api.js +1039 -0
- package/neural-interface/lib/file-explorer-system.js +355 -0
- package/neural-interface/lib/file-explorer-trash.ps1 +85 -0
- package/neural-interface/lib/file-explorer-watch.js +239 -0
- 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-diff-api.js +103 -0
- package/neural-interface/lib/git-read.js +234 -0
- package/neural-interface/lib/greeting/api.js +89 -0
- package/neural-interface/lib/greeting/budgets.js +90 -0
- package/neural-interface/lib/greeting/engine.js +461 -0
- package/neural-interface/lib/greeting/facts.js +320 -0
- package/neural-interface/lib/greeting/sources.js +252 -0
- package/neural-interface/lib/greeting/writer.js +139 -0
- package/neural-interface/lib/http-guards.js +189 -13
- package/neural-interface/lib/install-details.js +145 -0
- package/neural-interface/lib/machine-names.js +88 -0
- package/neural-interface/lib/mcp-installer.js +14 -2
- package/neural-interface/lib/memory-api.js +31 -7
- package/neural-interface/lib/native-loop-providers.js +26 -8
- package/neural-interface/lib/native-loop-runtime.js +9 -4
- package/neural-interface/lib/os-path.js +319 -0
- package/neural-interface/lib/plugin-installer.js +4 -2
- package/neural-interface/lib/project-storage.js +5 -7
- package/neural-interface/lib/provider-panel-api-key-auth.js +92 -0
- package/neural-interface/lib/provider-panel-terminal-auth.js +95 -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/request-gate.js +589 -0
- package/neural-interface/lib/session-title-generator.js +4 -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/ui-blob-api.js +108 -0
- package/neural-interface/lib/ui-blob-store.js +68 -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 +701 -2
- package/neural-interface/package.json +13 -2
- package/neural-interface/public/index.html +2 -0
- package/neural-interface/public/index2d.html +2 -0
- package/neural-interface/public/install-app.html +9 -6
- package/neural-interface/public/offline.html +19 -3
- package/neural-interface/public/onboarding.html +107 -2
- package/neural-interface/public/shared/account-i18n.js +7 -0
- package/neural-interface/public/shared/account-menu.js +539 -0
- package/neural-interface/public/shared/account-usage.js +19 -0
- package/neural-interface/public/shared/account-view.js +335 -0
- package/neural-interface/public/shared/agent-runtime-options.js +50 -14
- package/neural-interface/public/shared/assistant/asst-accounts.js +182 -184
- package/neural-interface/public/shared/assistant/asst-api.js +32 -2
- package/neural-interface/public/shared/assistant/asst-automation.js +286 -0
- package/neural-interface/public/shared/assistant/asst-brain-picker.js +430 -81
- package/neural-interface/public/shared/assistant/asst-budget.js +259 -303
- package/neural-interface/public/shared/assistant/asst-composer.js +11 -1
- package/neural-interface/public/shared/assistant/asst-mascot.js +140 -61
- package/neural-interface/public/shared/assistant/asst-menu.js +27 -11
- package/neural-interface/public/shared/assistant/asst-panel.js +198 -42
- package/neural-interface/public/shared/assistant/asst-render.js +197 -23
- package/neural-interface/public/shared/assistant/asst-route-models.js +108 -0
- package/neural-interface/public/shared/assistant/asst-route.js +95 -343
- package/neural-interface/public/shared/assistant/asst-routes-editor.js +534 -0
- package/neural-interface/public/shared/assistant/asst-sanitize.js +8 -1
- package/neural-interface/public/shared/assistant/asst-sidepanel.js +31 -9
- package/neural-interface/public/shared/assistant/asst-slash.js +1 -1
- package/neural-interface/public/shared/assistant/asst-state.js +186 -2
- package/neural-interface/public/shared/assistant/asst-styles.js +247 -11
- package/neural-interface/public/shared/blob-store.js +111 -22
- 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 +80 -118
- package/neural-interface/public/shared/cdx/cdx-render.js +25 -89
- package/neural-interface/public/shared/cdx/cdx-settings.js +231 -0
- package/neural-interface/public/shared/cdx/cdx-styles.js +164 -107
- package/neural-interface/public/shared/cdx/cdx-tabs.js +348 -668
- 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/constants.js +6 -1
- package/neural-interface/public/shared/cp/cp-events.js +2 -1
- package/neural-interface/public/shared/cp/cp-rehydrate.js +2 -1
- package/neural-interface/public/shared/cp/cp-styles.js +61 -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-ai-logic.js +409 -0
- package/neural-interface/public/shared/editor/editor-ai.js +619 -0
- package/neural-interface/public/shared/editor/editor-commands.js +212 -0
- package/neural-interface/public/shared/editor/editor-compare.js +103 -0
- package/neural-interface/public/shared/editor/editor-completion-list.js +172 -0
- package/neural-interface/public/shared/editor/editor-doc.js +257 -0
- package/neural-interface/public/shared/editor/editor-git-logic.js +131 -0
- package/neural-interface/public/shared/editor/editor-git.js +95 -0
- package/neural-interface/public/shared/editor/editor-languages.js +156 -0
- package/neural-interface/public/shared/editor/editor-lint.js +229 -0
- package/neural-interface/public/shared/editor/editor-loader.js +51 -0
- package/neural-interface/public/shared/editor/editor-markdown.js +90 -0
- package/neural-interface/public/shared/editor/editor-menu.js +233 -0
- package/neural-interface/public/shared/editor/editor-minimap.js +371 -0
- package/neural-interface/public/shared/editor/editor-palette-logic.js +192 -0
- package/neural-interface/public/shared/editor/editor-palette.js +125 -0
- package/neural-interface/public/shared/editor/editor-panel-logic.js +288 -0
- package/neural-interface/public/shared/editor/editor-panel.js +2099 -0
- package/neural-interface/public/shared/editor/editor-prefs.js +224 -0
- package/neural-interface/public/shared/editor/editor-safety.js +500 -0
- package/neural-interface/public/shared/editor/editor-search.js +352 -0
- package/neural-interface/public/shared/editor/editor-statusbar.js +144 -0
- package/neural-interface/public/shared/editor/editor-tabs-logic.js +331 -0
- package/neural-interface/public/shared/editor/editor-tabs.js +233 -0
- package/neural-interface/public/shared/editor/editor-theme.js +244 -0
- package/neural-interface/public/shared/editor/editor-view.js +332 -0
- package/neural-interface/public/shared/explorer/fx-api.js +216 -0
- package/neural-interface/public/shared/explorer/fx-links.js +204 -0
- package/neural-interface/public/shared/explorer/fx-logic.js +537 -0
- package/neural-interface/public/shared/explorer/fx-main.js +865 -0
- package/neural-interface/public/shared/explorer/fx-names.js +87 -0
- package/neural-interface/public/shared/explorer/fx-ops.js +370 -0
- package/neural-interface/public/shared/explorer/fx-popups.js +490 -0
- package/neural-interface/public/shared/explorer/fx-tree.js +790 -0
- package/neural-interface/public/shared/file-links.js +339 -0
- package/neural-interface/public/shared/git-bash-view.js +145 -0
- package/neural-interface/public/shared/greeting/greeting-card.js +178 -0
- package/neural-interface/public/shared/greeting/greeting-view.js +148 -0
- package/neural-interface/public/shared/html-shell.js +161 -165
- 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 -0
- package/neural-interface/public/shared/ocp-v2/ocp-v2-greeting.js +47 -0
- package/neural-interface/public/shared/ocp-v2/ocp-v2-panel.js +131 -17
- package/neural-interface/public/shared/ocp-v2/ocp-v2-render.js +7 -1
- package/neural-interface/public/shared/ocp-v2/ocp-v2-send.js +22 -0
- package/neural-interface/public/shared/ocp-v2/ocp-v2-styles.js +158 -0
- package/neural-interface/public/shared/ocp-v2/ocp-v2-ws.js +3 -2
- package/neural-interface/public/shared/pwa-install-page.js +32 -2
- package/neural-interface/public/shared/pwa-install.css +3 -3
- package/neural-interface/public/shared/pwa-install.js +100 -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-code-editor-pane.js +412 -0
- package/neural-interface/public/shared/settings/settings-code-editor.js +328 -0
- package/neural-interface/public/shared/settings/settings-greeting.js +133 -0
- package/neural-interface/public/shared/settings/settings-ia.js +33 -4
- package/neural-interface/public/shared/settings/settings-user-context.js +154 -0
- package/neural-interface/public/shared/showcase-privacy.js +489 -0
- package/neural-interface/public/shared/start-bridge.js +238 -19
- package/neural-interface/public/shared/storage.js +30 -3
- package/neural-interface/public/shared/styles.css +1710 -770
- package/neural-interface/public/shared/synabun-mascot.js +68 -58
- package/neural-interface/public/shared/tunnel-view.js +167 -0
- package/neural-interface/public/shared/ui-claude-panel.js +369 -229
- package/neural-interface/public/shared/ui-cli-banner.js +355 -0
- package/neural-interface/public/shared/ui-detail.js +15 -0
- package/neural-interface/public/shared/ui-explorer.js +22 -0
- package/neural-interface/public/shared/ui-file-explorer.js +481 -2496
- package/neural-interface/public/shared/ui-file-links.js +103 -0
- package/neural-interface/public/shared/ui-git-bash.js +124 -0
- package/neural-interface/public/shared/ui-help.js +26 -0
- package/neural-interface/public/shared/ui-invite.js +126 -116
- package/neural-interface/public/shared/ui-judgments.js +10 -1
- package/neural-interface/public/shared/ui-keybinds.js +4 -0
- package/neural-interface/public/shared/ui-loading.js +187 -19
- package/neural-interface/public/shared/ui-menubar.js +25 -33
- package/neural-interface/public/shared/ui-native-loop-router-state.js +11 -0
- package/neural-interface/public/shared/ui-native-loop-router.js +15 -2
- package/neural-interface/public/shared/ui-navbar.js +48 -22
- 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 +509 -490
- package/neural-interface/public/shared/ui-sidepanel-runtime.js +2 -1
- package/neural-interface/public/shared/ui-sidepanel-sessions.js +3 -1
- package/neural-interface/public/shared/ui-sync.js +22 -0
- package/neural-interface/public/shared/ui-terminal.js +40 -5
- package/neural-interface/public/shared/ui-tool-updates.js +5 -0
- package/neural-interface/public/shared/ui-tunnel.js +130 -0
- package/neural-interface/public/shared/ui-tutorial-steps.js +6 -2
- package/neural-interface/public/shared/ui-update.js +594 -5
- package/neural-interface/public/shared/ui-whatsapp.js +21 -6
- package/neural-interface/public/shared/ui-whiteboard.js +12 -0
- package/neural-interface/public/shared/ui-worker-end.js +245 -0
- package/neural-interface/public/shared/ui-workspaces.js +41 -27
- package/neural-interface/public/shared/whatsapp/wa-view.js +51 -8
- package/neural-interface/public/shared/worker-end.js +253 -0
- package/neural-interface/public/sw.js +2 -2
- package/neural-interface/public/variant/2d/main.js +10 -1
- package/neural-interface/public/variant/3d/main.js +10 -1
- package/neural-interface/public/vendor/codemirror/chunks/apl-CSDKT72Z.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/asciiarmor-JYYPU5BM.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/asn1-NRWCZDA6.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/asterisk-5OU6D4ZQ.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/brainfuck-ID4N4SYC.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/chunk-4RZWGCS2.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/chunk-ACTJM3JR.js +11 -0
- package/neural-interface/public/vendor/codemirror/chunks/chunk-S6BMAXLK.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/chunk-TO2RDOHQ.js +3 -0
- package/neural-interface/public/vendor/codemirror/chunks/chunk-US2GMZZQ.js +13 -0
- package/neural-interface/public/vendor/codemirror/chunks/chunk-WYYO5ZAJ.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/chunk-XALZTDJC.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/chunk-YRNUTRDV.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/chunk-Z3QOKH62.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/clike-OWQBSOGL.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/clojure-KV75UQWC.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/cmake-GTEP5Y3C.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/cobol-WCZ7QEGJ.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/coffeescript-TGLBOT6B.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/commonlisp-WVVI432V.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/crystal-S6R6HLKQ.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/css-PDA2RINP.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/cypher-HL5QPL4C.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/d-HHJH2HAJ.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/diff-MN3A35BY.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/dist-25SQLBVS.js +3 -0
- package/neural-interface/public/vendor/codemirror/chunks/dist-27ZGGJJS.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/dist-4E52OJIF.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/dist-6FT73XDC.js +2 -0
- package/neural-interface/public/vendor/codemirror/chunks/dist-BE6YGRMW.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/dist-CJSGKW2O.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/dist-CRPH2WLI.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/dist-CTG7JEQN.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/dist-I3FEVELX.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/dist-IM5BBSWZ.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/dist-KERUKTIF.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/dist-LXTMWZAM.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/dist-MABASSET.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/dist-MJVBUZLL.js +3 -0
- package/neural-interface/public/vendor/codemirror/chunks/dist-MNB3WZVU.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/dist-QYWXOE3U.js +7 -0
- package/neural-interface/public/vendor/codemirror/chunks/dist-TEBOFWLZ.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/dist-UNXFROM3.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/dist-XL2SLQO6.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/dist-XO42Y3AP.js +6 -0
- package/neural-interface/public/vendor/codemirror/chunks/dist-Y2WWDWJK.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/dist-YFEZKWMU.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/dist-ZY7SWQO6.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/dockerfile-3Y4R4NCH.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/dtd-5343GJ4P.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/dylan-FI2CXSW2.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/ebnf-GNQ77YWA.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/ecl-MRPUAE2L.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/eiffel-DZROES27.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/elm-4KICRFKQ.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/erlang-3IHYC263.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/factor-C6D3E5UK.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/fcl-VYTWUJMI.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/forth-ZBO2EH5M.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/fortran-5NDCRWIK.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/gas-SKRMOXFP.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/gherkin-L45YXZLW.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/groovy-GLPTLN44.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/haskell-E7NF7P4Y.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/haxe-K2NVDOB6.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/http-Q6DRQIJB.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/idl-MZYNJVLN.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/javascript-6YRYK2HR.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/julia-HXEL2T5G.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/livescript-MV7CI2JZ.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/lua-E36NHOOD.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/mathematica-3XOZZCMC.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/mbox-ODYGFKS7.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/mirc-WB43AWF5.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/mllike-ZT6ET7ZH.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/modelica-LLMZPFGA.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/mscgen-2OPNNYFH.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/mumps-6P3HWJH6.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/nginx-TCGKQTRW.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/nsis-ATIGIXTJ.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/ntriples-5PLBI5OV.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/octave-QF2S3NL6.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/oz-JFIVFFH2.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/pascal-JG6SW76V.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/perl-4B3IWVVO.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/pig-ABEBH4MI.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/powershell-H32XE7TL.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/properties-NN644MMA.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/protobuf-YKWQK2J3.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/pug-RIYXEKFW.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/puppet-JZQ5WPEV.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/python-EBCGGHAC.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/q-5UMH2YQY.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/r-7XMGG453.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/rpm-IEV7TSLR.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/ruby-T4SPFPNM.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/sas-GEDX67ET.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/scheme-FTLOWKPC.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/shell-HUFRKFSJ.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/sieve-P5QUDVYO.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/smalltalk-2SORJNQI.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/solr-M22TGAR5.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/sparql-4M3KL752.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/spreadsheet-3K23LA4N.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/sql-VHRFMUBI.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/stex-QZDAQUY2.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/stylus-7ZDKAKBD.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/swift-WCLCLGMR.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/tcl-JLMU6WOK.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/textile-4CHDKZ4T.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/tiddlywiki-DKMCZZQ4.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/tiki-F4JIG36Q.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/toml-BHMHTIM2.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/troff-YQ2KV5NT.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/ttcn-ZHRCVFJY.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/ttcn-cfg-KJOVTY4X.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/turtle-535P5N6I.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/vb-76UMQHOT.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/vbscript-CGNDTK6Z.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/velocity-VSJDO5T5.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/verilog-4T6ETXIP.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/vhdl-T7FNAXUN.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/webidl-P6MJSJ6J.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/xquery-UOVILRGN.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/yacas-CV2APROL.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/z80-3KFNVM4M.js +1 -0
- package/neural-interface/public/vendor/codemirror/codemirror-6.43.14-75f05e67.min.js +2 -0
- package/neural-interface/public/vendor/codemirror/manifest.json +60 -0
- package/neural-interface/scripts/build-vendor.mjs +78 -2
- package/neural-interface/scripts/vendor-codemirror-entry.js +121 -0
- package/neural-interface/server.js +2085 -1629
- package/neural-interface/templates/rulesets/README.md +1 -1
- package/package.json +5 -8
- package/setup.js +53 -15
- package/benchmarks/browser-live-results.json +0 -79
- package/benchmarks/browser-v2-results.json +0 -258
- package/neural-interface/lib/codex-capabilities.d.ts +0 -41
- package/neural-interface/public/category-logos/claude.svg +0 -1
- package/neural-interface/public/category-logos/gemini-color.svg +0 -1
- package/neural-interface/public/category-logos/openai.svg +0 -1
- package/neural-interface/public/category-logos/synabun.png +0 -0
- package/neural-interface/public/claude.svg +0 -1
- package/neural-interface/public/gallery.svg +0 -6
- package/neural-interface/public/leonardoai.svg +0 -1
- package/neural-interface/public/mcp.svg +0 -4
- package/neural-interface/public/opencode-logo-dark-square.svg +0 -1
- package/neural-interface/public/pin-svgrepo-com.svg +0 -25
- package/neural-interface/public/sessionicon.svg +0 -8
- package/neural-interface/public/toolbar-album.svg +0 -8
- package/neural-interface/public/toolbar-question.svg +0 -6
- package/neural-interface/public/toolbar-sessions.svg +0 -6
- package/neural-interface/scripts/codex-protocol-fixture.mjs +0 -83
- package/skills/synabun/synabunicon.png +0 -0
|
@@ -0,0 +1,490 @@
|
|
|
1
|
+
// ═══════════════════════════════════════════
|
|
2
|
+
// SynaBun Neural Interface — File Explorer: what opens over the page
|
|
3
|
+
// ═══════════════════════════════════════════
|
|
4
|
+
//
|
|
5
|
+
// The context menu and the preview card (flyouts docked to the right of the
|
|
6
|
+
// panel, an arrow pointing at their row), the questions (confirm dialogs), and
|
|
7
|
+
// the line at the foot of the panel that says what happened.
|
|
8
|
+
// While one is open its keys are taken at the window, before anything in the
|
|
9
|
+
// page sees them: the whiteboard listens at the document in the capture phase
|
|
10
|
+
// and takes Escape, Delete and letters whenever the keyboard is not in a text
|
|
11
|
+
// field. Taken, not cancelled: a key reaches nothing in the page under the
|
|
12
|
+
// popup, and only the keys the popup itself acts on lose what the browser
|
|
13
|
+
// would do with them. Reload (F5, Ctrl+R) and the like stay the browser's.
|
|
14
|
+
// One flyout is open at a time; closed with the keyboard, it gives the
|
|
15
|
+
// keyboard back to where it was. One question is open at a time too: a second
|
|
16
|
+
// one answers the first with "given up" before it takes its place.
|
|
17
|
+
// No imports: the caller hands in the words.
|
|
18
|
+
|
|
19
|
+
const KEY_EVENTS = ['keydown', 'keyup', 'keypress'];
|
|
20
|
+
const stroke = (d) => `<svg viewBox="0 0 24 24" aria-hidden="true">${d}</svg>`;
|
|
21
|
+
|
|
22
|
+
/** The glyph of a menu item, by its id (24 × 24, drawn with the row's colour). */
|
|
23
|
+
export const MENU_ICONS = {
|
|
24
|
+
open: stroke('<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/>'),
|
|
25
|
+
openKeep: stroke('<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/><polyline points="9 15 11 17 15 13"/>'),
|
|
26
|
+
openAsText: stroke('<path d="M17 3a2.83 2.83 0 1 1 4 4L7.5 20.5 2 22l1.5-5.5Z"/>'),
|
|
27
|
+
preview: stroke('<path d="M2 12s3.5-7 10-7 10 7 10 7-3.5 7-10 7-10-7-10-7z"/><circle cx="12" cy="12" r="3"/>'),
|
|
28
|
+
newFile: stroke('<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/><line x1="12" y1="12" x2="12" y2="18"/><line x1="9" y1="15" x2="15" y2="15"/>'),
|
|
29
|
+
newFolder: stroke('<path d="M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z"/><line x1="12" y1="11" x2="12" y2="17"/><line x1="9" y1="14" x2="15" y2="14"/>'),
|
|
30
|
+
send: stroke('<path d="M12 3l1.5 4.5L18 9l-4.5 1.5L12 15l-1.5-4.5L6 9l4.5-1.5L12 3z"/><path d="M18 14l.9 2.1L21 17l-2.1.9L18 20l-.9-2.1L15 17l2.1-.9L18 14z"/>'),
|
|
31
|
+
sendTo: stroke('<line x1="22" y1="2" x2="11" y2="13"/><polygon points="22 2 15 22 11 13 2 9 22 2"/>'),
|
|
32
|
+
cut: stroke('<circle cx="6" cy="6" r="3"/><circle cx="6" cy="18" r="3"/><line x1="20" y1="4" x2="8.12" y2="15.88"/><line x1="14.47" y1="14.48" x2="20" y2="20"/><line x1="8.12" y1="8.12" x2="12" y2="12"/>'),
|
|
33
|
+
copy: stroke('<rect x="9" y="9" width="13" height="13" rx="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/>'),
|
|
34
|
+
paste: stroke('<path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"/><rect x="8" y="2" width="8" height="4" rx="1"/>'),
|
|
35
|
+
duplicate: stroke('<rect x="8" y="8" width="13" height="13" rx="2"/><path d="M4 16V5a2 2 0 0 1 2-2h11"/><line x1="14.5" y1="11.5" x2="14.5" y2="17.5"/><line x1="11.5" y1="14.5" x2="17.5" y2="14.5"/>'),
|
|
36
|
+
copyPath: stroke('<path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/>'),
|
|
37
|
+
copyRelativePath: stroke('<path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/>'),
|
|
38
|
+
copyName: stroke('<path d="M4 7V4h16v3"/><path d="M9 20h6"/><path d="M12 4v16"/>'),
|
|
39
|
+
rename: stroke('<path d="M12 20h9"/><path d="M16.5 3.5a2.12 2.12 0 0 1 3 3L7 19l-4 1 1-4Z"/>'),
|
|
40
|
+
delete: stroke('<polyline points="3 6 5 6 21 6"/><path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"/>'),
|
|
41
|
+
reveal: stroke('<path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/><polyline points="15 3 21 3 21 9"/><line x1="10" y1="14" x2="21" y2="3"/>'),
|
|
42
|
+
color: stroke('<circle cx="13.5" cy="6.5" r="2.5"/><circle cx="17.5" cy="10.5" r="2.5"/><circle cx="8.5" cy="7.5" r="2.5"/><circle cx="6.5" cy="12.5" r="2.5"/><path d="M12 2C6.5 2 2 6.5 2 12s4.5 10 10 10c.93 0 1.5-.67 1.5-1.5 0-.39-.14-.74-.39-1.04-.24-.28-.37-.59-.37-.96 0-.83.67-1.5 1.5-1.5H16c3.31 0 6-2.69 6-6 0-5.5-4.5-10-10-10z"/>'),
|
|
43
|
+
expand: stroke('<polyline points="9 6 15 12 9 18"/>'),
|
|
44
|
+
collapse: stroke('<polyline points="6 9 12 15 18 9"/>'),
|
|
45
|
+
collapseChildren: stroke('<path d="M4 14h6v6"/><path d="M20 10h-6V4"/><path d="M14 10l7-7"/><path d="M3 21l7-7"/>'),
|
|
46
|
+
collapseAll: stroke('<path d="M4 14h6v6"/><path d="M20 10h-6V4"/><path d="M14 10l7-7"/><path d="M3 21l7-7"/>'),
|
|
47
|
+
expandAll: stroke('<path d="M15 3h6v6"/><path d="M9 21H3v-6"/><path d="M21 3l-7 7"/><path d="M3 21l7-7"/>'),
|
|
48
|
+
refresh: stroke('<polyline points="23 4 23 10 17 10"/><path d="M20.49 15a9 9 0 1 1-2.12-9.36L23 10"/>'),
|
|
49
|
+
changedOnly: stroke('<circle cx="18" cy="18" r="3"/><circle cx="6" cy="6" r="3"/><path d="M6 21V9a9 9 0 0 0 9 9"/>'),
|
|
50
|
+
sortBy: stroke('<path d="M3 6h7M3 12h5M3 18h3M16 4v16M12 16l4 4 4-4"/>'),
|
|
51
|
+
back: stroke('<polyline points="15 6 9 12 15 18"/>'),
|
|
52
|
+
check: stroke('<polyline points="20 6 9 17 4 12"/>'),
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
let _flyout = null; // the open flyout: { close }
|
|
56
|
+
let _dialog = null; // the open question: { giveUp, before }
|
|
57
|
+
const MENU_KEYS = new Set(['Tab', 'Escape', 'ArrowDown', 'ArrowUp', 'ArrowLeft', 'ArrowRight', 'Home', 'End', 'Enter', ' ']);
|
|
58
|
+
const CARD_KEYS = new Set(['Tab', 'Escape', 'ArrowLeft', 'ArrowRight']);
|
|
59
|
+
const plain = (event) => !event.ctrlKey && !event.metaKey && !event.altKey;
|
|
60
|
+
|
|
61
|
+
/** Close the menu or the preview that is open, if one is. */
|
|
62
|
+
export function closeFlyout(o) { _flyout?.close(o); }
|
|
63
|
+
export const isFlyoutOpen = () => !!_flyout;
|
|
64
|
+
|
|
65
|
+
// Put a flyout to the right of the panel with its arrow at `y`; over the panel when the window has no room there.
|
|
66
|
+
function dock(flyout, arrow, { panel, y }) {
|
|
67
|
+
const frame = panel.getBoundingClientRect();
|
|
68
|
+
const size = flyout.getBoundingClientRect();
|
|
69
|
+
const fits = frame.right + 6 + size.width <= window.innerWidth - 8;
|
|
70
|
+
const left = fits ? frame.right + 6 : Math.max(8, window.innerWidth - size.width - 8);
|
|
71
|
+
const top = Math.max(8, Math.min(window.innerHeight - size.height - 8, y - size.height / 2));
|
|
72
|
+
flyout.style.left = `${Math.round(left)}px`;
|
|
73
|
+
flyout.style.top = `${Math.round(top)}px`;
|
|
74
|
+
flyout.classList.toggle('fe-ctx-flyout--over', !fits);
|
|
75
|
+
if (arrow) arrow.style.top = `${Math.round(Math.max(12, Math.min(size.height - 12, y - top)))}px`;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// What every flyout shares: one at a time, keys at the window, gone on a press outside, the keyboard handed back.
|
|
79
|
+
// `owns(event)` says whether a key is one the flyout acts on: only those are cancelled (on the way down, where
|
|
80
|
+
// `onKey` acts, and on the way up, where a button would otherwise take a Space as a click).
|
|
81
|
+
function mount(flyout, { row, onKey, owns, onClose }) {
|
|
82
|
+
closeFlyout();
|
|
83
|
+
const before = document.activeElement;
|
|
84
|
+
let closed = false;
|
|
85
|
+
const keys = (event) => {
|
|
86
|
+
event.stopPropagation();
|
|
87
|
+
if (!owns(event)) return;
|
|
88
|
+
if (event.type === 'keydown') onKey(event);
|
|
89
|
+
else event.preventDefault();
|
|
90
|
+
};
|
|
91
|
+
const outside = (event) => { if (!flyout.contains(event.target)) close({ refocus: false }); };
|
|
92
|
+
const gone = () => close({ refocus: false });
|
|
93
|
+
function close({ refocus = true } = {}) {
|
|
94
|
+
if (closed) return;
|
|
95
|
+
closed = true;
|
|
96
|
+
if (_flyout === handle) _flyout = null;
|
|
97
|
+
for (const type of KEY_EVENTS) window.removeEventListener(type, keys, true);
|
|
98
|
+
document.removeEventListener('pointerdown', outside, true);
|
|
99
|
+
window.removeEventListener('resize', gone);
|
|
100
|
+
window.removeEventListener('blur', gone);
|
|
101
|
+
row?.classList.remove('fe-row--ctx-active');
|
|
102
|
+
flyout.classList.remove('fe-ctx-flyout--in');
|
|
103
|
+
flyout.classList.add('fe-ctx-flyout--out');
|
|
104
|
+
setTimeout(() => flyout.remove(), 150);
|
|
105
|
+
// The keyboard goes back to where it was, unless what was chosen took it somewhere (a name being typed, a question).
|
|
106
|
+
const held = flyout.contains(document.activeElement) || document.activeElement === document.body || !document.activeElement;
|
|
107
|
+
if (refocus && held && before?.isConnected) before.focus({ preventScroll: true });
|
|
108
|
+
onClose?.();
|
|
109
|
+
}
|
|
110
|
+
const handle = { close, element: flyout };
|
|
111
|
+
_flyout = handle;
|
|
112
|
+
document.body.appendChild(flyout);
|
|
113
|
+
row?.classList.add('fe-row--ctx-active');
|
|
114
|
+
for (const type of KEY_EVENTS) window.addEventListener(type, keys, true);
|
|
115
|
+
// Not the press that opened it.
|
|
116
|
+
setTimeout(() => { if (!closed) document.addEventListener('pointerdown', outside, true); }, 0);
|
|
117
|
+
window.addEventListener('resize', gone);
|
|
118
|
+
window.addEventListener('blur', gone);
|
|
119
|
+
requestAnimationFrame(() => flyout.classList.add('fe-ctx-flyout--in'));
|
|
120
|
+
return handle;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* @typedef {object} MenuItem
|
|
125
|
+
* @property {string} [id] @property {string} [label] @property {string} [keyLabel] its shortcut, as it is shown
|
|
126
|
+
* @property {boolean} [danger] @property {boolean} [disabled] @property {string} [title] why it is disabled
|
|
127
|
+
* @property {boolean} [checked] @property {boolean} [more] it opens another page of the menu
|
|
128
|
+
* @property {boolean} [back] the row that leads back to the page before
|
|
129
|
+
* @property {string} [swatch] a colour: drawn as a round swatch, in a row with the others
|
|
130
|
+
* @property {boolean} [separator]
|
|
131
|
+
*/
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Open the context menu.
|
|
135
|
+
* @param {object} o
|
|
136
|
+
* @param {HTMLElement} o.panel the explorer panel (the menu docks to its right edge)
|
|
137
|
+
* @param {HTMLElement | null} o.row the row it belongs to, or null for the empty space
|
|
138
|
+
* @param {number} [o.y] where the arrow points when there is no row
|
|
139
|
+
* @param {string} o.label its accessible name
|
|
140
|
+
* @param {MenuItem[]} o.items
|
|
141
|
+
* @param {boolean} [o.keyboard] opened with the keyboard: the first item has it from the start
|
|
142
|
+
* @param {(item: MenuItem) => (MenuItem[] | void)} o.onSelect what an item does; a list it returns is shown as the
|
|
143
|
+
* menu's next page
|
|
144
|
+
* @param {() => void} [o.onClose]
|
|
145
|
+
*/
|
|
146
|
+
export function openContextMenu({ panel, row, y = null, label, items, keyboard = false, onSelect, onClose }) {
|
|
147
|
+
const flyout = document.createElement('div');
|
|
148
|
+
flyout.className = 'fe-ctx-flyout';
|
|
149
|
+
const arrow = document.createElement('div');
|
|
150
|
+
arrow.className = 'fe-ctx-arrow';
|
|
151
|
+
const menu = document.createElement('div');
|
|
152
|
+
menu.className = 'fe-ctx-menu';
|
|
153
|
+
menu.setAttribute('role', 'menu');
|
|
154
|
+
menu.setAttribute('aria-label', label);
|
|
155
|
+
menu.tabIndex = -1;
|
|
156
|
+
flyout.append(arrow, menu);
|
|
157
|
+
const at = row ? (() => { const box = row.getBoundingClientRect(); return box.top + box.height / 2; })() : y ?? window.innerHeight / 2;
|
|
158
|
+
const pages = []; // the pages left behind, for "back"
|
|
159
|
+
let shown = [];
|
|
160
|
+
let typed = '';
|
|
161
|
+
let typedAt = 0;
|
|
162
|
+
|
|
163
|
+
const stops = () => [...menu.querySelectorAll('[role^="menuitem"]:not([aria-disabled="true"])')];
|
|
164
|
+
const focusAt = (index) => { const list = stops(); if (list.length) list[(index + list.length) % list.length].focus({ preventScroll: true }); };
|
|
165
|
+
|
|
166
|
+
function draw(list, { focusFirst = false } = {}) {
|
|
167
|
+
shown = list;
|
|
168
|
+
menu.textContent = '';
|
|
169
|
+
let swatches = null;
|
|
170
|
+
for (const item of list) {
|
|
171
|
+
if (item.separator) {
|
|
172
|
+
swatches = null;
|
|
173
|
+
const line = document.createElement('div');
|
|
174
|
+
line.className = 'fe-ctx-separator';
|
|
175
|
+
line.setAttribute('role', 'separator');
|
|
176
|
+
menu.append(line);
|
|
177
|
+
continue;
|
|
178
|
+
}
|
|
179
|
+
const button = document.createElement('button');
|
|
180
|
+
button.type = 'button';
|
|
181
|
+
button.tabIndex = -1;
|
|
182
|
+
button.dataset.id = item.id;
|
|
183
|
+
if (item.swatch) {
|
|
184
|
+
if (!swatches) { swatches = document.createElement('div'); swatches.className = 'fe-ctx-swatches'; swatches.setAttribute('role', 'group'); menu.append(swatches); }
|
|
185
|
+
button.className = `fe-color-swatch${item.checked ? ' fe-color-swatch--active' : ''}`;
|
|
186
|
+
button.style.background = item.swatch;
|
|
187
|
+
button.setAttribute('role', 'menuitemradio');
|
|
188
|
+
button.setAttribute('aria-checked', String(!!item.checked));
|
|
189
|
+
button.setAttribute('aria-label', item.label);
|
|
190
|
+
button.dataset.tooltip = item.label;
|
|
191
|
+
swatches.append(button);
|
|
192
|
+
continue;
|
|
193
|
+
}
|
|
194
|
+
swatches = null;
|
|
195
|
+
button.className = `fe-ctx-item${item.danger ? ' fe-ctx-item--danger' : ''}`;
|
|
196
|
+
const checkable = item.checked !== undefined;
|
|
197
|
+
button.setAttribute('role', checkable ? 'menuitemcheckbox' : 'menuitem');
|
|
198
|
+
if (checkable) button.setAttribute('aria-checked', String(!!item.checked));
|
|
199
|
+
if (item.more) button.setAttribute('aria-haspopup', 'menu');
|
|
200
|
+
if (item.disabled) { button.setAttribute('aria-disabled', 'true'); if (item.title) button.title = item.title; }
|
|
201
|
+
const icon = document.createElement('span');
|
|
202
|
+
icon.className = 'fe-ctx-icon';
|
|
203
|
+
icon.innerHTML = (checkable ? (item.checked ? MENU_ICONS.check : '') : MENU_ICONS[item.back ? 'back' : item.icon || item.id]) || '';
|
|
204
|
+
const text = document.createElement('span');
|
|
205
|
+
text.className = 'fe-ctx-label';
|
|
206
|
+
text.textContent = item.label;
|
|
207
|
+
button.append(icon, text);
|
|
208
|
+
if (item.keyLabel || item.more) {
|
|
209
|
+
const key = document.createElement('span');
|
|
210
|
+
key.className = 'fe-ctx-key';
|
|
211
|
+
key.textContent = item.more ? '›' : item.keyLabel;
|
|
212
|
+
key.setAttribute('aria-hidden', 'true');
|
|
213
|
+
if (item.keyLabel && !item.more) button.setAttribute('aria-keyshortcuts', item.keyLabel);
|
|
214
|
+
button.append(key);
|
|
215
|
+
}
|
|
216
|
+
menu.append(button);
|
|
217
|
+
}
|
|
218
|
+
dock(flyout, arrow, { panel, y: at });
|
|
219
|
+
if (focusFirst) focusAt(0); else menu.focus({ preventScroll: true });
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
function choose(item) {
|
|
223
|
+
if (!item || item.disabled) return;
|
|
224
|
+
if (item.back) { const page = pages.pop(); if (page) draw(page, { focusFirst: true }); return; }
|
|
225
|
+
const next = onSelect(item);
|
|
226
|
+
if (Array.isArray(next)) { pages.push(shown); draw(next, { focusFirst: true }); return; }
|
|
227
|
+
handle.close();
|
|
228
|
+
}
|
|
229
|
+
const itemOf = (node) => shown.find((item) => !item.separator && item.id === node?.dataset.id);
|
|
230
|
+
|
|
231
|
+
menu.addEventListener('click', (event) => choose(itemOf(event.target.closest('[data-id]'))));
|
|
232
|
+
// The row under the pointer is the one the keyboard is on: one highlight, not two.
|
|
233
|
+
menu.addEventListener('pointermove', (event) => {
|
|
234
|
+
const node = event.target.closest('[role^="menuitem"]:not([aria-disabled="true"])');
|
|
235
|
+
if (node && node !== document.activeElement) node.focus({ preventScroll: true });
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
const handle = mount(flyout, {
|
|
239
|
+
row,
|
|
240
|
+
onClose,
|
|
241
|
+
// The menu's own keys: moving, choosing, closing, and a letter typed for an item. With Ctrl, Cmd or Alt held a
|
|
242
|
+
// key is the browser's (it still reaches nothing in the page).
|
|
243
|
+
owns: (event) => !event.ctrlKey && !event.metaKey && !event.altKey && (MENU_KEYS.has(event.key) || event.key.length === 1),
|
|
244
|
+
onKey(event) {
|
|
245
|
+
const list = stops();
|
|
246
|
+
const index = list.indexOf(document.activeElement);
|
|
247
|
+
const { key } = event;
|
|
248
|
+
event.preventDefault();
|
|
249
|
+
if (key === 'Tab') { handle.close(); return; }
|
|
250
|
+
if (key === 'Escape') { handle.close(); return; }
|
|
251
|
+
if (key === 'ArrowDown') { focusAt(index + 1); return; }
|
|
252
|
+
if (key === 'ArrowUp') { focusAt(index < 0 ? list.length - 1 : index - 1); return; }
|
|
253
|
+
if (key === 'Home') { focusAt(0); return; }
|
|
254
|
+
if (key === 'End') { focusAt(list.length - 1); return; }
|
|
255
|
+
if (key === 'ArrowLeft') { if (pages.length) choose(shown.find((item) => item.back)); return; }
|
|
256
|
+
if (key === 'ArrowRight') { const item = itemOf(document.activeElement); if (item?.more) choose(item); return; }
|
|
257
|
+
if (key === 'Enter' || key === ' ') { choose(itemOf(document.activeElement)); return; }
|
|
258
|
+
if (key.length === 1) {
|
|
259
|
+
// A letter goes to the next item that starts with what was typed.
|
|
260
|
+
const now = Date.now();
|
|
261
|
+
typed = now - typedAt > 600 ? key.toLowerCase() : typed + key.toLowerCase();
|
|
262
|
+
typedAt = now;
|
|
263
|
+
const from = typed.length > 1 ? Math.max(0, index) : index + 1;
|
|
264
|
+
for (let i = 0; i < list.length; i++) {
|
|
265
|
+
const node = list[(from + i) % list.length];
|
|
266
|
+
if ((itemOf(node)?.label || '').toLowerCase().startsWith(typed)) { node.focus({ preventScroll: true }); break; }
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
},
|
|
270
|
+
});
|
|
271
|
+
draw(items, { focusFirst: keyboard });
|
|
272
|
+
return handle;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
/**
|
|
276
|
+
* Open the card of a file that is not text: its picture when it is one, what it is, and what can be done with it.
|
|
277
|
+
* @param {object} o
|
|
278
|
+
* @param {HTMLElement} o.panel @param {HTMLElement | null} o.row @param {string} o.label its accessible name
|
|
279
|
+
* @param {string} o.name @param {string} o.detail what it is and how large
|
|
280
|
+
* @param {string | null} [o.imageUrl] @param {string} [o.imageAlt] @param {string} [o.imageFailed] said when the picture does not load
|
|
281
|
+
* @param {{ label: string, run: () => void, keep?: boolean }[]} o.actions `keep`: the card stays open after it
|
|
282
|
+
* @param {() => void} [o.onClose]
|
|
283
|
+
*/
|
|
284
|
+
export function openPreviewCard({ panel, row, label, name, detail, imageUrl = null, imageAlt = '', imageFailed = '', actions, onClose }) {
|
|
285
|
+
const flyout = document.createElement('div');
|
|
286
|
+
flyout.className = 'fe-ctx-flyout fe-preview';
|
|
287
|
+
const arrow = document.createElement('div');
|
|
288
|
+
arrow.className = 'fe-ctx-arrow';
|
|
289
|
+
const card = document.createElement('div');
|
|
290
|
+
card.className = 'fe-ctx-menu fe-preview-card';
|
|
291
|
+
card.setAttribute('role', 'dialog');
|
|
292
|
+
card.setAttribute('aria-label', label);
|
|
293
|
+
card.tabIndex = -1;
|
|
294
|
+
const at = row ? (() => { const box = row.getBoundingClientRect(); return box.top + box.height / 2; })() : window.innerHeight / 2;
|
|
295
|
+
const title = document.createElement('div');
|
|
296
|
+
title.className = 'fe-preview-name';
|
|
297
|
+
title.textContent = name;
|
|
298
|
+
title.title = name;
|
|
299
|
+
const meta = document.createElement('div');
|
|
300
|
+
meta.className = 'fe-preview-detail';
|
|
301
|
+
meta.textContent = detail;
|
|
302
|
+
card.append(title);
|
|
303
|
+
if (imageUrl) {
|
|
304
|
+
const frame = document.createElement('div');
|
|
305
|
+
frame.className = 'fe-preview-frame';
|
|
306
|
+
const image = document.createElement('img');
|
|
307
|
+
image.alt = imageAlt;
|
|
308
|
+
image.decoding = 'async';
|
|
309
|
+
image.addEventListener('load', () => {
|
|
310
|
+
if (image.naturalWidth) meta.textContent = `${detail} · ${image.naturalWidth} × ${image.naturalHeight}`;
|
|
311
|
+
dock(flyout, arrow, { panel, y: at });
|
|
312
|
+
});
|
|
313
|
+
image.addEventListener('error', () => { frame.textContent = imageFailed; frame.classList.add('fe-preview-frame--failed'); dock(flyout, arrow, { panel, y: at }); });
|
|
314
|
+
image.src = imageUrl;
|
|
315
|
+
frame.append(image);
|
|
316
|
+
card.append(frame);
|
|
317
|
+
}
|
|
318
|
+
card.append(meta);
|
|
319
|
+
const bar = document.createElement('div');
|
|
320
|
+
bar.className = 'fe-preview-actions';
|
|
321
|
+
for (const action of actions) {
|
|
322
|
+
const button = document.createElement('button');
|
|
323
|
+
button.type = 'button';
|
|
324
|
+
button.className = 'fe-modal-btn fe-modal-cancel';
|
|
325
|
+
button.textContent = action.label;
|
|
326
|
+
button.addEventListener('click', () => { if (!action.keep) handle.close(); action.run(); });
|
|
327
|
+
bar.append(button);
|
|
328
|
+
}
|
|
329
|
+
card.append(bar);
|
|
330
|
+
flyout.append(arrow, card);
|
|
331
|
+
const buttons = () => [...bar.querySelectorAll('button')];
|
|
332
|
+
// Whatever makes the card another size (the picture arriving, a long name wrapping), it is put inside the
|
|
333
|
+
// window again, with the margin the menu keeps.
|
|
334
|
+
const sized = typeof ResizeObserver === 'function' ? new ResizeObserver(() => dock(flyout, arrow, { panel, y: at })) : null;
|
|
335
|
+
const handle = mount(flyout, {
|
|
336
|
+
row,
|
|
337
|
+
onClose() { sized?.disconnect(); onClose?.(); },
|
|
338
|
+
// Enter and Space are the focused button's own; with no button focused they would act on nothing.
|
|
339
|
+
owns: (event) => plain(event) && (CARD_KEYS.has(event.key) || ((event.key === 'Enter' || event.key === ' ') && !buttons().includes(document.activeElement))),
|
|
340
|
+
onKey(event) {
|
|
341
|
+
const { key } = event;
|
|
342
|
+
event.preventDefault();
|
|
343
|
+
if (key === 'Escape') { handle.close(); return; }
|
|
344
|
+
if (key === 'Enter' || key === ' ') return;
|
|
345
|
+
const list = buttons();
|
|
346
|
+
const index = list.indexOf(document.activeElement);
|
|
347
|
+
const step = key === 'ArrowLeft' || (key === 'Tab' && event.shiftKey) ? -1 : 1;
|
|
348
|
+
list[(index + step + list.length) % list.length]?.focus();
|
|
349
|
+
},
|
|
350
|
+
});
|
|
351
|
+
dock(flyout, arrow, { panel, y: at });
|
|
352
|
+
sized?.observe(card);
|
|
353
|
+
buttons()[0]?.focus({ preventScroll: true });
|
|
354
|
+
return handle;
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
/**
|
|
358
|
+
* Ask before something that cannot simply be taken back.
|
|
359
|
+
* @param {object} o
|
|
360
|
+
* @param {string} o.title @param {string[]} o.lines what will happen, a sentence per line
|
|
361
|
+
* @param {string} o.confirmLabel @param {string} o.cancelLabel
|
|
362
|
+
* @param {string} [o.altLabel] a third answer between the two
|
|
363
|
+
* @param {boolean} [o.danger] the confirm button removes something: it is not where the keyboard starts
|
|
364
|
+
* @returns {Promise<'confirm' | 'alt' | null>} null: given up (also when another question took its place)
|
|
365
|
+
*/
|
|
366
|
+
export function confirmDialog({ title, lines, confirmLabel, cancelLabel, altLabel = '', danger = false }) {
|
|
367
|
+
return new Promise((resolve) => {
|
|
368
|
+
closeFlyout({ refocus: false });
|
|
369
|
+
// A question that is still open is given up through its own way out: its listeners go and its caller hears
|
|
370
|
+
// "no". The keyboard goes back, in the end, to where it was before the first of them.
|
|
371
|
+
const earlier = _dialog;
|
|
372
|
+
earlier?.giveUp();
|
|
373
|
+
// One that was answered a moment ago may still be fading out: it goes at once (two would share an id).
|
|
374
|
+
document.getElementById('fe-dialog')?.remove();
|
|
375
|
+
const before = earlier?.before || document.activeElement;
|
|
376
|
+
const overlay = document.createElement('div');
|
|
377
|
+
overlay.id = 'fe-dialog';
|
|
378
|
+
overlay.className = 'fe-modal-overlay';
|
|
379
|
+
const modal = document.createElement('div');
|
|
380
|
+
modal.className = `fe-modal${danger ? '' : ' fe-modal--plain'}`;
|
|
381
|
+
modal.setAttribute('role', 'alertdialog');
|
|
382
|
+
modal.setAttribute('aria-modal', 'true');
|
|
383
|
+
modal.setAttribute('aria-labelledby', 'fe-dialog-title');
|
|
384
|
+
modal.setAttribute('aria-describedby', 'fe-dialog-body');
|
|
385
|
+
const heading = document.createElement('div');
|
|
386
|
+
heading.className = 'fe-modal-title';
|
|
387
|
+
heading.id = 'fe-dialog-title';
|
|
388
|
+
heading.textContent = title;
|
|
389
|
+
const body = document.createElement('div');
|
|
390
|
+
body.className = 'fe-modal-body';
|
|
391
|
+
body.id = 'fe-dialog-body';
|
|
392
|
+
for (const line of lines.filter(Boolean)) { const p = document.createElement('p'); p.textContent = line; body.append(p); }
|
|
393
|
+
const bar = document.createElement('div');
|
|
394
|
+
bar.className = 'fe-modal-actions';
|
|
395
|
+
const button = (label, className, answer) => {
|
|
396
|
+
const node = document.createElement('button');
|
|
397
|
+
node.type = 'button';
|
|
398
|
+
node.className = `fe-modal-btn ${className}`;
|
|
399
|
+
node.textContent = label;
|
|
400
|
+
node.addEventListener('click', () => done(answer));
|
|
401
|
+
bar.append(node);
|
|
402
|
+
return node;
|
|
403
|
+
};
|
|
404
|
+
const cancel = button(cancelLabel, 'fe-modal-cancel', null);
|
|
405
|
+
if (altLabel) button(altLabel, 'fe-modal-cancel', 'alt');
|
|
406
|
+
const confirm = button(confirmLabel, 'fe-modal-confirm', 'confirm');
|
|
407
|
+
modal.append(heading, body, bar);
|
|
408
|
+
overlay.append(modal);
|
|
409
|
+
|
|
410
|
+
// No key reaches the page under the question; the ones it acts on (and only those) are cancelled.
|
|
411
|
+
const keys = (event) => {
|
|
412
|
+
event.stopPropagation();
|
|
413
|
+
if (event.type !== 'keydown' || !plain(event)) return;
|
|
414
|
+
const list = [...bar.querySelectorAll('button')];
|
|
415
|
+
const index = list.indexOf(document.activeElement);
|
|
416
|
+
if (event.key === 'Escape') { event.preventDefault(); done(null); return; }
|
|
417
|
+
if (event.key === 'Tab' || event.key === 'ArrowRight' || event.key === 'ArrowLeft') {
|
|
418
|
+
event.preventDefault();
|
|
419
|
+
const step = event.key === 'ArrowLeft' || (event.key === 'Tab' && event.shiftKey) ? -1 : 1;
|
|
420
|
+
list[(index + step + list.length) % list.length].focus();
|
|
421
|
+
return;
|
|
422
|
+
}
|
|
423
|
+
// Enter and Space are the focused button's own; with no button focused they would act on nothing.
|
|
424
|
+
if ((event.key === 'Enter' || event.key === ' ') && index < 0) event.preventDefault();
|
|
425
|
+
};
|
|
426
|
+
let answered = false;
|
|
427
|
+
function done(answer, { replaced = false } = {}) {
|
|
428
|
+
if (answered) return;
|
|
429
|
+
answered = true;
|
|
430
|
+
if (_dialog === handle) _dialog = null;
|
|
431
|
+
for (const type of KEY_EVENTS) window.removeEventListener(type, keys, true);
|
|
432
|
+
overlay.classList.remove('fe-modal-overlay--in');
|
|
433
|
+
// Another question is taking its place: it goes at once (two of them would share an id for a moment).
|
|
434
|
+
if (replaced) overlay.remove();
|
|
435
|
+
else {
|
|
436
|
+
setTimeout(() => overlay.remove(), 150);
|
|
437
|
+
if (before?.isConnected) before.focus({ preventScroll: true });
|
|
438
|
+
}
|
|
439
|
+
resolve(answer);
|
|
440
|
+
}
|
|
441
|
+
const handle = { before, giveUp: () => done(null, { replaced: true }) };
|
|
442
|
+
_dialog = handle;
|
|
443
|
+
overlay.addEventListener('pointerdown', (event) => { if (event.target === overlay) done(null); });
|
|
444
|
+
for (const type of KEY_EVENTS) window.addEventListener(type, keys, true);
|
|
445
|
+
document.body.appendChild(overlay);
|
|
446
|
+
requestAnimationFrame(() => overlay.classList.add('fe-modal-overlay--in'));
|
|
447
|
+
(danger ? cancel : confirm).focus({ preventScroll: true });
|
|
448
|
+
});
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
/**
|
|
452
|
+
* The line at the foot of the panel: what happened, and what can be done about it.
|
|
453
|
+
* @param {HTMLElement} root #fe-notice @param {{ dismiss: string }} words
|
|
454
|
+
*/
|
|
455
|
+
export function createNotice(root, words) {
|
|
456
|
+
let timer = 0;
|
|
457
|
+
const text = root.querySelector('.fe-notice-text');
|
|
458
|
+
const action = root.querySelector('.fe-notice-action');
|
|
459
|
+
const close = root.querySelector('.fe-notice-close');
|
|
460
|
+
let run = null;
|
|
461
|
+
function clear() {
|
|
462
|
+
clearTimeout(timer);
|
|
463
|
+
root.hidden = true;
|
|
464
|
+
run = null;
|
|
465
|
+
}
|
|
466
|
+
action.addEventListener('click', () => { const todo = run; clear(); todo?.(); });
|
|
467
|
+
close.addEventListener('click', clear);
|
|
468
|
+
close.setAttribute('aria-label', words.dismiss);
|
|
469
|
+
return {
|
|
470
|
+
/**
|
|
471
|
+
* @param {string} message
|
|
472
|
+
* @param {{ tone?: 'info' | 'ok' | 'error', action?: { label: string, run: () => void }, ms?: number }} [o] an error
|
|
473
|
+
* stays until it is dismissed or something else is said
|
|
474
|
+
*/
|
|
475
|
+
show(message, { tone = 'info', action: todo = null, ms = tone === 'error' ? 0 : 6000 } = {}) {
|
|
476
|
+
clearTimeout(timer);
|
|
477
|
+
root.dataset.tone = tone;
|
|
478
|
+
root.setAttribute('role', tone === 'error' ? 'alert' : 'status');
|
|
479
|
+
text.textContent = message;
|
|
480
|
+
text.title = message;
|
|
481
|
+
run = todo?.run || null;
|
|
482
|
+
action.hidden = !todo;
|
|
483
|
+
action.textContent = todo?.label || '';
|
|
484
|
+
root.hidden = false;
|
|
485
|
+
if (ms > 0) timer = setTimeout(clear, ms);
|
|
486
|
+
},
|
|
487
|
+
clear,
|
|
488
|
+
text: () => (root.hidden ? '' : text.textContent),
|
|
489
|
+
};
|
|
490
|
+
}
|