@songsid/agend 2.3.0-beta.9 → 2.3.1
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/README.md +16 -8
- package/README.zh-TW.md +16 -8
- package/dist/adapter-world.js.map +1 -1
- package/dist/agend-home.d.ts +5 -0
- package/dist/agend-home.js +7 -0
- package/dist/agend-home.js.map +1 -0
- package/dist/agent-cli-instructions.md +7 -1
- package/dist/agent-cli.js +41 -0
- package/dist/agent-cli.js.map +1 -1
- package/dist/agent-endpoint.d.ts +9 -0
- package/dist/agent-endpoint.js +23 -0
- package/dist/agent-endpoint.js.map +1 -1
- package/dist/agent-file-read.js +1 -1
- package/dist/agent-file-read.js.map +1 -1
- package/dist/agent-whats-new.d.ts +16 -0
- package/dist/agent-whats-new.js +53 -12
- package/dist/agent-whats-new.js.map +1 -1
- package/dist/apply-job.js.map +1 -1
- package/dist/atomic-write.js.map +1 -1
- package/dist/auth-api.d.ts +12 -0
- package/dist/auth-api.js +77 -22
- package/dist/auth-api.js.map +1 -1
- package/dist/authority/authority-state.d.ts +4 -0
- package/dist/authority/authority-state.js +17 -8
- package/dist/authority/authority-state.js.map +1 -1
- package/dist/authority/session-authorizations.js.map +1 -1
- package/dist/authority/web-authority.d.ts +16 -8
- package/dist/authority/web-authority.js +19 -5
- package/dist/authority/web-authority.js.map +1 -1
- package/dist/backend/antigravity.d.ts +2 -2
- package/dist/backend/antigravity.js +2 -1
- package/dist/backend/antigravity.js.map +1 -1
- package/dist/backend/claude-code.js +13 -4
- package/dist/backend/claude-code.js.map +1 -1
- package/dist/backend/codex-metadata.d.ts +8 -0
- package/dist/backend/codex-metadata.js +74 -17
- package/dist/backend/codex-metadata.js.map +1 -1
- package/dist/backend/codex-package.d.ts +30 -0
- package/dist/backend/codex-package.js +133 -0
- package/dist/backend/codex-package.js.map +1 -0
- package/dist/backend/codex-session-lookup.js.map +1 -1
- package/dist/backend/codex.d.ts +2 -0
- package/dist/backend/codex.js +25 -5
- package/dist/backend/codex.js.map +1 -1
- package/dist/backend/credential-profile.d.ts +2 -0
- package/dist/backend/credential-profile.js +2 -0
- package/dist/backend/credential-profile.js.map +1 -1
- package/dist/backend/effort-metadata.js.map +1 -1
- package/dist/backend/grok.js +58 -3
- package/dist/backend/grok.js.map +1 -1
- package/dist/backend/kiro-engine-ledger.js.map +1 -1
- package/dist/backend/kiro-kas-store.js.map +1 -1
- package/dist/backend/kiro-v2-store.js.map +1 -1
- package/dist/backend/kiro-v3-identity.js.map +1 -1
- package/dist/backend/kiro.d.ts +9 -1
- package/dist/backend/kiro.js +102 -1
- package/dist/backend/kiro.js.map +1 -1
- package/dist/backend/launch-effort.js.map +1 -1
- package/dist/backend/mock.js.map +1 -1
- package/dist/backend/muse.d.ts +20 -2
- package/dist/backend/muse.js +154 -16
- package/dist/backend/muse.js.map +1 -1
- package/dist/backend/opencode-permission.js +1 -1
- package/dist/backend/opencode-permission.js.map +1 -1
- package/dist/backend/opencode.js +2 -1
- package/dist/backend/opencode.js.map +1 -1
- package/dist/backend/session-signals.js.map +1 -1
- package/dist/backend-availability.js.map +1 -1
- package/dist/backend-command.d.ts +2 -0
- package/dist/backend-command.js +3 -1
- package/dist/backend-command.js.map +1 -1
- package/dist/backend-outage.js.map +1 -1
- package/dist/binary-probe.js +1 -1
- package/dist/binary-probe.js.map +1 -1
- package/dist/bug-report-cli.d.ts +30 -0
- package/dist/bug-report-cli.js +139 -0
- package/dist/bug-report-cli.js.map +1 -0
- package/dist/bug-report.d.ts +47 -0
- package/dist/bug-report.js +137 -0
- package/dist/bug-report.js.map +1 -0
- package/dist/bundled-skills.d.ts +18 -0
- package/dist/bundled-skills.js +38 -0
- package/dist/bundled-skills.js.map +1 -0
- package/dist/cache-service.js +1 -1
- package/dist/cache-service.js.map +1 -1
- package/dist/channel/adapters/discord-client-retirement.d.ts +5 -0
- package/dist/channel/adapters/discord-client-retirement.js +57 -0
- package/dist/channel/adapters/discord-client-retirement.js.map +1 -0
- package/dist/channel/adapters/discord.d.ts +4 -0
- package/dist/channel/adapters/discord.js +57 -12
- package/dist/channel/adapters/discord.js.map +1 -1
- package/dist/channel/adapters/telegram.d.ts +7 -1
- package/dist/channel/adapters/telegram.js +57 -49
- package/dist/channel/adapters/telegram.js.map +1 -1
- package/dist/channel/mcp-server.js +21 -4
- package/dist/channel/mcp-server.js.map +1 -1
- package/dist/channel/mcp-tools.d.ts +3 -1
- package/dist/channel/mcp-tools.js +41 -4
- package/dist/channel/mcp-tools.js.map +1 -1
- package/dist/channel/message-queue.js.map +1 -1
- package/dist/channel/sticker-download.js.map +1 -1
- package/dist/channel/tool-router.js.map +1 -1
- package/dist/child-process.d.ts +21 -0
- package/dist/child-process.js +54 -0
- package/dist/child-process.js.map +1 -0
- package/dist/classic-channel-manager.js +3 -2
- package/dist/classic-channel-manager.js.map +1 -1
- package/dist/cli-entry.js.map +1 -1
- package/dist/cli-flags.js.map +1 -1
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +448 -313
- package/dist/cli.js.map +1 -1
- package/dist/command-table.d.ts +1 -1
- package/dist/command-table.js +1 -1
- package/dist/command-table.js.map +1 -1
- package/dist/compact-watch.js.map +1 -1
- package/dist/completion-install.js.map +1 -1
- package/dist/completion.js.map +1 -1
- package/dist/config-validator.d.ts +6 -0
- package/dist/config-validator.js +81 -8
- package/dist/config-validator.js.map +1 -1
- package/dist/config.d.ts +15 -3
- package/dist/config.js +41 -10
- package/dist/config.js.map +1 -1
- package/dist/connection-secrets.js.map +1 -1
- package/dist/context-guardian.js.map +1 -1
- package/dist/coredump-filter.js.map +1 -1
- package/dist/cpu-profile.js.map +1 -1
- package/dist/cross-instance-envelope.js.map +1 -1
- package/dist/cross-instance-notice.d.ts +7 -0
- package/dist/cross-instance-notice.js +10 -0
- package/dist/cross-instance-notice.js.map +1 -1
- package/dist/daemon.d.ts +52 -1
- package/dist/daemon.js +385 -27
- package/dist/daemon.js.map +1 -1
- package/dist/daily-summary.js.map +1 -1
- package/dist/darwin-memory.js +1 -1
- package/dist/darwin-memory.js.map +1 -1
- package/dist/dashboard-live.d.ts +8 -0
- package/dist/dashboard-live.js +35 -0
- package/dist/dashboard-live.js.map +1 -0
- package/dist/dashboard-permissions.d.ts +69 -0
- package/dist/dashboard-permissions.js +138 -0
- package/dist/dashboard-permissions.js.map +1 -0
- package/dist/decision-list.js.map +1 -1
- package/dist/delivery-consumed-watch.js.map +1 -1
- package/dist/delivery-outbox.d.ts +11 -0
- package/dist/delivery-outbox.js +52 -2
- package/dist/delivery-outbox.js.map +1 -1
- package/dist/delivery-outbox.test.js.map +1 -1
- package/dist/delivery-reconciliation.test.js.map +1 -1
- package/dist/diagnostic-redaction.d.ts +10 -0
- package/dist/diagnostic-redaction.js +167 -0
- package/dist/diagnostic-redaction.js.map +1 -0
- package/dist/doctor.d.ts +8 -0
- package/dist/doctor.js +52 -0
- package/dist/doctor.js.map +1 -1
- package/dist/e2e-isolation.js.map +1 -1
- package/dist/event-log.js.map +1 -1
- package/dist/export-import.js +7 -5
- package/dist/export-import.js.map +1 -1
- package/dist/fleet-context.d.ts +1 -1
- package/dist/fleet-control-audit.js.map +1 -1
- package/dist/fleet-label.js.map +1 -1
- package/dist/fleet-level-config.d.ts +1 -1
- package/dist/fleet-level-config.js +2 -0
- package/dist/fleet-level-config.js.map +1 -1
- package/dist/fleet-lock.d.ts +6 -0
- package/dist/fleet-lock.js +22 -2
- package/dist/fleet-lock.js.map +1 -1
- package/dist/fleet-manager.d.ts +283 -15
- package/dist/fleet-manager.js +1825 -331
- package/dist/fleet-manager.js.map +1 -1
- package/dist/fleet-process-handlers.d.ts +18 -0
- package/dist/fleet-process-handlers.js +70 -0
- package/dist/fleet-process-handlers.js.map +1 -0
- package/dist/fleet-startup.d.ts +14 -0
- package/dist/fleet-startup.js +30 -0
- package/dist/fleet-startup.js.map +1 -0
- package/dist/fleet-traffic.js.map +1 -1
- package/dist/forged-envelope-scan.d.ts +0 -20
- package/dist/forged-envelope-scan.js +3 -2
- package/dist/forged-envelope-scan.js.map +1 -1
- package/dist/full-restart.d.ts +5 -5
- package/dist/full-restart.js +5 -5
- package/dist/full-restart.js.map +1 -1
- package/dist/general-knowledge/skills/bug-report/SKILL.md +31 -0
- package/dist/general-knowledge/skills/credential-profiles/SKILL.md +52 -11
- package/dist/general-knowledge/skills/cross-instance-messaging/SKILL.md +5 -1
- package/dist/general-knowledge/skills/fleet-config/SKILL.md +3 -1
- package/dist/general-knowledge/skills/fleet-restart/SKILL.md +8 -9
- package/dist/general-knowledge/skills/instance-lifecycle/SKILL.md +11 -0
- package/dist/general-knowledge/skills/multi-channel/SKILL.md +1 -1
- package/dist/general-knowledge/skills/persona-emoji/SKILL.md +1 -1
- package/dist/general-knowledge/skills/scheduling/SKILL.md +4 -0
- package/dist/general-knowledge/skills/web-appearance/SKILL.canvas-worker-off.md +321 -0
- package/dist/general-knowledge/skills/web-appearance/SKILL.md +24 -4
- package/dist/general-knowledge/skills/web-dashboard/SKILL.canvas-worker-off.md +554 -0
- package/dist/general-knowledge/skills/web-dashboard/SKILL.md +156 -32
- package/dist/general-knowledge/skills/worker-collaboration/SKILL.md +1 -1
- package/dist/general-knowledge/steering/core-rules.md +1 -1
- package/dist/host-memory.js.map +1 -1
- package/dist/idle-gate-log.d.ts +19 -0
- package/dist/idle-gate-log.js +15 -0
- package/dist/idle-gate-log.js.map +1 -0
- package/dist/instance-config-impact.js +25 -4
- package/dist/instance-config-impact.js.map +1 -1
- package/dist/instance-lifecycle.d.ts +11 -1
- package/dist/instance-lifecycle.js +97 -25
- package/dist/instance-lifecycle.js.map +1 -1
- package/dist/instructions.d.ts +12 -6
- package/dist/instructions.js +19 -10
- package/dist/instructions.js.map +1 -1
- package/dist/kiro-compact-pane.js.map +1 -1
- package/dist/kiro-db-reader.js.map +1 -1
- package/dist/kiro-engine-status.js.map +1 -1
- package/dist/kiro-log-gate.js.map +1 -1
- package/dist/kiro-transcript-lane.js.map +1 -1
- package/dist/locale.js +450 -20
- package/dist/locale.js.map +1 -1
- package/dist/log-service.js +1 -0
- package/dist/log-service.js.map +1 -1
- package/dist/log-steps.d.ts +1 -0
- package/dist/log-steps.js +15 -0
- package/dist/log-steps.js.map +1 -1
- package/dist/logger.js +3 -2
- package/dist/logger.js.map +1 -1
- package/dist/login-controller.d.ts +45 -0
- package/dist/login-controller.js +101 -5
- package/dist/login-controller.js.map +1 -1
- package/dist/login-flows.js +1 -1
- package/dist/login-manager.js.map +1 -1
- package/dist/login-shell.js +3 -1
- package/dist/login-shell.js.map +1 -1
- package/dist/ls-attach-resolve.d.ts +96 -0
- package/dist/ls-attach-resolve.js +180 -0
- package/dist/ls-attach-resolve.js.map +1 -0
- package/dist/ls-rows.d.ts +12 -0
- package/dist/ls-rows.js +83 -4
- package/dist/ls-rows.js.map +1 -1
- package/dist/memory-pressure.js.map +1 -1
- package/dist/muse-usage-relay.js.map +1 -1
- package/dist/needs-you-hub.d.ts +98 -1
- package/dist/needs-you-hub.js +313 -44
- package/dist/needs-you-hub.js.map +1 -1
- package/dist/needs-you-live.d.ts +5 -2
- package/dist/needs-you-live.js +6 -3
- package/dist/needs-you-live.js.map +1 -1
- package/dist/needs-you.d.ts +68 -3
- package/dist/needs-you.js +39 -4
- package/dist/needs-you.js.map +1 -1
- package/dist/network-family.js.map +1 -1
- package/dist/operator-control-paths.js +3 -4
- package/dist/operator-control-paths.js.map +1 -1
- package/dist/outbound-handlers.d.ts +5 -2
- package/dist/outbound-handlers.js +81 -24
- package/dist/outbound-handlers.js.map +1 -1
- package/dist/outbound-schemas.d.ts +106 -60
- package/dist/outbound-schemas.js +33 -10
- package/dist/outbound-schemas.js.map +1 -1
- package/dist/outbox-open-error.js.map +1 -1
- package/dist/pane-context-cache.js.map +1 -1
- package/dist/paths.js.map +1 -1
- package/dist/private-dir.js.map +1 -1
- package/dist/probe-worker-pool.js.map +1 -1
- package/dist/profile-control.js.map +1 -1
- package/dist/prompt-file-ref.js.map +1 -1
- package/dist/provider-probe.js.map +1 -1
- package/dist/provider-secret-registry.d.ts +5 -3
- package/dist/provider-secret-registry.js +8 -2
- package/dist/provider-secret-registry.js.map +1 -1
- package/dist/public-link-auto-open.d.ts +32 -0
- package/dist/public-link-auto-open.js +86 -0
- package/dist/public-link-auto-open.js.map +1 -0
- package/dist/public-link-owner.d.ts +10 -0
- package/dist/public-link-owner.js +36 -0
- package/dist/public-link-owner.js.map +1 -0
- package/dist/public-link-progress.d.ts +12 -1
- package/dist/public-link-progress.js +5 -10
- package/dist/public-link-progress.js.map +1 -1
- package/dist/public-preview.js.map +1 -1
- package/dist/public-tunnel-config.d.ts +11 -0
- package/dist/public-tunnel-config.js +68 -0
- package/dist/public-tunnel-config.js.map +1 -0
- package/dist/public-web-gateway.d.ts +2 -0
- package/dist/public-web-gateway.js +19 -6
- package/dist/public-web-gateway.js.map +1 -1
- package/dist/public-web-link.d.ts +48 -3
- package/dist/public-web-link.js +177 -51
- package/dist/public-web-link.js.map +1 -1
- package/dist/quickstart-api.d.ts +9 -0
- package/dist/quickstart-api.js +82 -0
- package/dist/quickstart-api.js.map +1 -1
- package/dist/quickstart.d.ts +5 -0
- package/dist/quickstart.js +40 -25
- package/dist/quickstart.js.map +1 -1
- package/dist/reply-buttons-controller.js.map +1 -1
- package/dist/reply-dedup.js.map +1 -1
- package/dist/resource-report.js +1 -1
- package/dist/resource-report.js.map +1 -1
- package/dist/restart-guard.js.map +1 -1
- package/dist/restart-progress.d.ts +80 -1
- package/dist/restart-progress.js +306 -21
- package/dist/restart-progress.js.map +1 -1
- package/dist/rollout-index.js.map +1 -1
- package/dist/runtime-cpu-profile.js.map +1 -1
- package/dist/scheduler/db.d.ts +40 -13
- package/dist/scheduler/db.js +123 -89
- package/dist/scheduler/db.js.map +1 -1
- package/dist/scheduler/scheduler.js.map +1 -1
- package/dist/scheduler/task-board-text.d.ts +26 -0
- package/dist/scheduler/task-board-text.js +140 -0
- package/dist/scheduler/task-board-text.js.map +1 -0
- package/dist/scheduler/task-board.d.ts +730 -0
- package/dist/scheduler/task-board.js +2179 -0
- package/dist/scheduler/task-board.js.map +1 -0
- package/dist/scheduler/types.d.ts +6 -1
- package/dist/sd-notify.js +2 -1
- package/dist/sd-notify.js.map +1 -1
- package/dist/search/search-docs.d.ts +58 -0
- package/dist/search/search-docs.js +63 -0
- package/dist/search/search-docs.js.map +1 -0
- package/dist/search/search-index.d.ts +149 -0
- package/dist/search/search-index.js +453 -0
- package/dist/search/search-index.js.map +1 -0
- package/dist/search/search-store.d.ts +122 -0
- package/dist/search/search-store.js +236 -0
- package/dist/search/search-store.js.map +1 -0
- package/dist/search/search-worker.d.ts +1 -0
- package/dist/search/search-worker.js +163 -0
- package/dist/search/search-worker.js.map +1 -0
- package/dist/secret-file.js.map +1 -1
- package/dist/secret-store.d.ts +2 -0
- package/dist/secret-store.js +21 -0
- package/dist/secret-store.js.map +1 -1
- package/dist/service-installer.d.ts +98 -1
- package/dist/service-installer.js +246 -11
- package/dist/service-installer.js.map +1 -1
- package/dist/service-owner.d.ts +44 -0
- package/dist/service-owner.js +48 -0
- package/dist/service-owner.js.map +1 -0
- package/dist/service-plan.d.ts +39 -2
- package/dist/service-plan.js +188 -6
- package/dist/service-plan.js.map +1 -1
- package/dist/session-oom-stop.d.ts +45 -0
- package/dist/session-oom-stop.js +259 -0
- package/dist/session-oom-stop.js.map +1 -0
- package/dist/session-oom.d.ts +34 -0
- package/dist/session-oom.js +251 -0
- package/dist/session-oom.js.map +1 -0
- package/dist/settings-api.d.ts +14 -1
- package/dist/settings-api.js +74 -13
- package/dist/settings-api.js.map +1 -1
- package/dist/settings-baseline.js.map +1 -1
- package/dist/settings-change.d.ts +5 -0
- package/dist/settings-change.js +167 -6
- package/dist/settings-change.js.map +1 -1
- package/dist/settings-confirmation.d.ts +9 -1
- package/dist/settings-confirmation.js +18 -8
- package/dist/settings-confirmation.js.map +1 -1
- package/dist/settings-control.js +2 -1
- package/dist/settings-control.js.map +1 -1
- package/dist/settings-effect.js +29 -4
- package/dist/settings-effect.js.map +1 -1
- package/dist/settings-http-confirmation.d.ts +13 -0
- package/dist/settings-http-confirmation.js +15 -3
- package/dist/settings-http-confirmation.js.map +1 -1
- package/dist/settings-instance-patch.js +13 -1
- package/dist/settings-instance-patch.js.map +1 -1
- package/dist/settings-request-capability.d.ts +4 -1
- package/dist/settings-request-capability.js +11 -3
- package/dist/settings-request-capability.js.map +1 -1
- package/dist/settings-transaction.js.map +1 -1
- package/dist/setup-auth.d.ts +1 -0
- package/dist/setup-auth.js +2 -1
- package/dist/setup-auth.js.map +1 -1
- package/dist/setup-form.d.ts +1 -1
- package/dist/setup-form.js +23 -7
- package/dist/setup-form.js.map +1 -1
- package/dist/setup-host.d.ts +6 -1
- package/dist/setup-host.js +15 -4
- package/dist/setup-host.js.map +1 -1
- package/dist/setup-wizard.js +6 -3
- package/dist/setup-wizard.js.map +1 -1
- package/dist/spawn-gate.d.ts +7 -0
- package/dist/spawn-gate.js +45 -12
- package/dist/spawn-gate.js.map +1 -1
- package/dist/start-attempt.d.ts +11 -0
- package/dist/start-attempt.js +29 -0
- package/dist/start-attempt.js.map +1 -0
- package/dist/start-failure-chat.d.ts +37 -0
- package/dist/start-failure-chat.js +142 -0
- package/dist/start-failure-chat.js.map +1 -0
- package/dist/start-failure-notice.d.ts +4 -0
- package/dist/start-failure-notice.js +20 -0
- package/dist/start-failure-notice.js.map +1 -0
- package/dist/start-failure.d.ts +171 -0
- package/dist/start-failure.js +348 -0
- package/dist/start-failure.js.map +1 -0
- package/dist/startup-memory-deadline.d.ts +23 -0
- package/dist/startup-memory-deadline.js +55 -0
- package/dist/startup-memory-deadline.js.map +1 -0
- package/dist/statusline-watcher.js.map +1 -1
- package/dist/storm-window.js.map +1 -1
- package/dist/systemd-peer-bus.js.map +1 -1
- package/dist/target-queue-worker.js.map +1 -1
- package/dist/task-board-summary.d.ts +101 -0
- package/dist/task-board-summary.js +177 -0
- package/dist/task-board-summary.js.map +1 -0
- package/dist/task-board-tool.d.ts +25 -0
- package/dist/task-board-tool.js +155 -0
- package/dist/task-board-tool.js.map +1 -0
- package/dist/task-board-waker.d.ts +51 -0
- package/dist/task-board-waker.js +141 -0
- package/dist/task-board-waker.js.map +1 -0
- package/dist/telegram-api-root.d.ts +1 -0
- package/dist/telegram-api-root.js +40 -0
- package/dist/telegram-api-root.js.map +1 -0
- package/dist/tips.js.map +1 -1
- package/dist/tmux-control.js +1 -1
- package/dist/tmux-control.js.map +1 -1
- package/dist/tmux-exec-helper.d.ts +104 -0
- package/dist/tmux-exec-helper.js +412 -0
- package/dist/tmux-exec-helper.js.map +1 -0
- package/dist/tmux-manager.d.ts +1 -0
- package/dist/tmux-manager.js +29 -13
- package/dist/tmux-manager.js.map +1 -1
- package/dist/tmux-read.js +2 -2
- package/dist/tmux-read.js.map +1 -1
- package/dist/tool-permissions.js.map +1 -1
- package/dist/tool-progress.js.map +1 -1
- package/dist/topic-archiver.js.map +1 -1
- package/dist/topic-commands.d.ts +13 -0
- package/dist/topic-commands.js +56 -33
- package/dist/topic-commands.js.map +1 -1
- package/dist/topic-link.js.map +1 -1
- package/dist/transcript-delta-reader.js.map +1 -1
- package/dist/transcript-jsonl.js.map +1 -1
- package/dist/transcript-monitor.js +5 -0
- package/dist/transcript-monitor.js.map +1 -1
- package/dist/transcript-sources.d.ts +2 -0
- package/dist/transcript-sources.js +4 -0
- package/dist/transcript-sources.js.map +1 -1
- package/dist/tunnel/cloudflared-install.js +3 -1
- package/dist/tunnel/cloudflared-install.js.map +1 -1
- package/dist/tunnel/cloudflared.d.ts +7 -16
- package/dist/tunnel/cloudflared.js +100 -13
- package/dist/tunnel/cloudflared.js.map +1 -1
- package/dist/tunnel/lease.js +3 -2
- package/dist/tunnel/lease.js.map +1 -1
- package/dist/tunnel/manager.js.map +1 -1
- package/dist/tunnel/named-diagnostics.d.ts +13 -0
- package/dist/tunnel/named-diagnostics.js +52 -0
- package/dist/tunnel/named-diagnostics.js.map +1 -0
- package/dist/tunnel/public-fetch.js.map +1 -1
- package/dist/tunnel/purpose-lane.js.map +1 -1
- package/dist/tunnel/types.d.ts +4 -1
- package/dist/tunnel/types.js.map +1 -1
- package/dist/types.d.ts +56 -2
- package/dist/ui/app-effects.js +23 -5
- package/dist/ui/app.html +2 -0
- package/dist/ui/backend-signin.js +297 -0
- package/dist/ui/chat-effects.js +10 -3
- package/dist/ui/chat-msg-actions.js +176 -0
- package/dist/ui/chat-render.js +62 -18
- package/dist/ui/chat-store.js +218 -48
- package/dist/ui/chat-strings.js +40 -8
- package/dist/ui/chat-thread.js +119 -10
- package/dist/ui/effect-trial-offer.js +2 -31
- package/dist/ui/effects-waker.js +2 -0
- package/dist/ui/fleet-canvas-layout.js +13 -4
- package/dist/ui/fleet-canvas.js +69 -28
- package/dist/ui/fleet-office-editor.js +149 -0
- package/dist/ui/fleet-office-model.js +358 -0
- package/dist/ui/fleet-office-pack.js +199 -0
- package/dist/ui/fleet-office-skin.js +474 -0
- package/dist/ui/fleet-office-store.js +49 -0
- package/dist/ui/fleet-office-zones.js +177 -0
- package/dist/ui/fleet-office.js +434 -0
- package/dist/ui/fleet-org.js +18 -5
- package/dist/ui/fleet-route-scene.js +272 -0
- package/dist/ui/fleet-route-worker.js +21 -0
- package/dist/ui/fleet-route.js +88 -0
- package/dist/ui/fleet-task-card.js +436 -0
- package/dist/ui/fleet-task-common.js +482 -0
- package/dist/ui/fleet-task-work.js +107 -0
- package/dist/ui/fleet-tasks.js +569 -0
- package/dist/ui/fleet-traffic.js +81 -18
- package/dist/ui/fleet-usage.js +36 -2
- package/dist/ui/guest-refused.html +41 -0
- package/dist/ui/icons/icon-192.png +0 -0
- package/dist/ui/icons/icon-512.png +0 -0
- package/dist/ui/image-lightbox.js +45 -1
- package/dist/ui/image-zoom.js +242 -0
- package/dist/ui/panel-chat.js +214 -57
- package/dist/ui/panel-details.js +2 -0
- package/dist/ui/panel-fleet.js +32 -71
- package/dist/ui/panel-log.js +3 -3
- package/dist/ui/panel-needs.js +23 -6
- package/dist/ui/panel-settings.js +166 -33
- package/dist/ui/preview-panel.js +2 -1
- package/dist/ui/search-dialog.js +350 -0
- package/dist/ui/settings-appearance.js +170 -10
- package/dist/ui/settings-apply.js +3 -3
- package/dist/ui/settings-confirm.js +7 -2
- package/dist/ui/settings-dialogs.js +46 -21
- package/dist/ui/settings-discord.js +6 -2
- package/dist/ui/settings-guests.js +2 -1
- package/dist/ui/settings-learn.js +1 -0
- package/dist/ui/settings-model.js +51 -18
- package/dist/ui/settings-mods.js +8 -3
- package/dist/ui/settings-owner-effects.js +6 -4
- package/dist/ui/settings-strings.js +186 -6
- package/dist/ui/settings-token.js +3 -1
- package/dist/ui/settings-web-authority.js +13 -5
- package/dist/ui/settings-wizard.js +13 -7
- package/dist/ui/shared/agend-auth.js +2 -0
- package/dist/ui/shared/app-i18n.js +12 -5
- package/dist/ui/shared/app-needs.js +2 -2
- package/dist/ui/shared/app-prefs.js +15 -0
- package/dist/ui/shared/app-route.js +18 -0
- package/dist/ui/shared/app-shell.js +189 -28
- package/dist/ui/shared/app-store.js +3 -2
- package/dist/ui/shared/app-stream.js +5 -1
- package/dist/ui/shared/app-themes.js +116 -1
- package/dist/ui/shared/app.css +668 -48
- package/dist/ui/shared/app.js +9 -1
- package/dist/ui/shared/card-strings.js +4 -0
- package/dist/ui/shared/effect-app-runtime.js +7 -5
- package/dist/ui/shared/effect-chat-runtime.js +2 -7
- package/dist/ui/shared/effect-cursor.js +3 -2
- package/dist/ui/shared/effect-display-metadata.js +39 -0
- package/dist/ui/shared/effect-owner-source.js +8 -1
- package/dist/ui/shared/effect-registry.js +14 -4
- package/dist/ui/shared/effect-runtime-loader.js +3 -1
- package/dist/ui/shared/effect-stock-trust.js +5 -5
- package/dist/ui/shared/feature-flags.js +127 -0
- package/dist/ui/shared/guest-refused.js +35 -0
- package/dist/ui/shared/header-tools.js +2 -2
- package/dist/ui/shared/identity-card.js +26 -8
- package/dist/ui/shared/identity-color.js +17 -10
- package/dist/ui/shared/instance-nav.js +183 -28
- package/dist/ui/shared/owner-css.js +30 -1
- package/dist/ui/shared/panel-view.js +14 -29
- package/dist/ui/shared/text-undo.js +210 -0
- package/dist/ui/shared/tokens.css +4 -0
- package/dist/ui/shared/ui-dialog.js +5 -4
- package/dist/ui/shared/ui-icons.js +4 -0
- package/dist/ui/shared/ui-menu.js +20 -7
- package/dist/ui/shared/ui-toast.js +16 -3
- package/dist/ui/shared/usage-dialog.js +216 -16
- package/dist/ui/shared/view-strings.js +70 -4
- package/dist/ui/shared/web-authorize.js +50 -1
- package/dist/ui/signin.html +2 -0
- package/dist/ui/web-terminal/terminal.html +10 -10
- package/dist/ui/web-terminal/terminal.js +58 -12
- package/dist/update-check.js.map +1 -1
- package/dist/update-dispatch.js +1 -1
- package/dist/update-dispatch.js.map +1 -1
- package/dist/update-install.js.map +1 -1
- package/dist/update-launch.d.ts +10 -1
- package/dist/update-launch.js +39 -14
- package/dist/update-launch.js.map +1 -1
- package/dist/update-marker.js.map +1 -1
- package/dist/update-preflight.js +1 -1
- package/dist/update-preflight.js.map +1 -1
- package/dist/update-progress.d.ts +3 -1
- package/dist/update-progress.js +3 -1
- package/dist/update-progress.js.map +1 -1
- package/dist/usage/format-rich.js +6 -3
- package/dist/usage/format-rich.js.map +1 -1
- package/dist/usage/i18n-keys.d.ts +1 -1
- package/dist/usage/i18n-keys.js +2 -1
- package/dist/usage/i18n-keys.js.map +1 -1
- package/dist/usage/i18n.d.ts +13 -1
- package/dist/usage/i18n.js +29 -0
- package/dist/usage/i18n.js.map +1 -1
- package/dist/usage/providers.d.ts +89 -2
- package/dist/usage/providers.js +247 -41
- package/dist/usage/providers.js.map +1 -1
- package/dist/usage/statusline-usage.js.map +1 -1
- package/dist/usage/usage-api.d.ts +5 -1
- package/dist/usage/usage-api.js +185 -58
- package/dist/usage/usage-api.js.map +1 -1
- package/dist/usage-report.js.map +1 -1
- package/dist/usage-rollup.js +1 -1
- package/dist/usage-rollup.js.map +1 -1
- package/dist/user-systemd.d.ts +24 -0
- package/dist/user-systemd.js +75 -0
- package/dist/user-systemd.js.map +1 -0
- package/dist/view-api.d.ts +7 -0
- package/dist/view-api.js +16 -4
- package/dist/view-api.js.map +1 -1
- package/dist/wake-coordinator.js.map +1 -1
- package/dist/web-ambient.js.map +1 -1
- package/dist/web-api.d.ts +58 -11
- package/dist/web-api.js +1104 -71
- package/dist/web-api.js.map +1 -1
- package/dist/web-app-icon-worker.d.ts +1 -0
- package/dist/web-app-icon-worker.js +16 -0
- package/dist/web-app-icon-worker.js.map +1 -0
- package/dist/web-app-icon.d.ts +109 -0
- package/dist/web-app-icon.js +257 -0
- package/dist/web-app-icon.js.map +1 -0
- package/dist/web-appearance-policy.js.map +1 -1
- package/dist/web-appearance-tools.d.ts +1 -0
- package/dist/web-appearance-tools.js +7 -3
- package/dist/web-appearance-tools.js.map +1 -1
- package/dist/web-appearance-view.js.map +1 -1
- package/dist/web-appearance.d.ts +4 -10
- package/dist/web-appearance.js +7 -4
- package/dist/web-appearance.js.map +1 -1
- package/dist/web-auth.d.ts +8 -4
- package/dist/web-auth.js +33 -21
- package/dist/web-auth.js.map +1 -1
- package/dist/web-backend-login.d.ts +130 -0
- package/dist/web-backend-login.js +225 -0
- package/dist/web-backend-login.js.map +1 -0
- package/dist/web-card.js.map +1 -1
- package/dist/web-channel-echo.js.map +1 -1
- package/dist/web-chat-history.d.ts +118 -8
- package/dist/web-chat-history.js +220 -25
- package/dist/web-chat-history.js.map +1 -1
- package/dist/web-commands.d.ts +5 -1
- package/dist/web-commands.js +1 -1
- package/dist/web-commands.js.map +1 -1
- package/dist/web-cookie-names.d.ts +5 -0
- package/dist/web-cookie-names.js +12 -0
- package/dist/web-cookie-names.js.map +1 -0
- package/dist/web-effect-bundle.d.ts +2 -2
- package/dist/web-effect-library.js.map +1 -1
- package/dist/web-effect-playback.js.map +1 -1
- package/dist/web-effect-profiles.d.ts +16 -16
- package/dist/web-effect-profiles.js +3 -2
- package/dist/web-effect-profiles.js.map +1 -1
- package/dist/web-effect-service.js.map +1 -1
- package/dist/web-effect-tools.js.map +1 -1
- package/dist/web-effect-trials.js.map +1 -1
- package/dist/web-effect-upload.js.map +1 -1
- package/dist/web-effect.d.ts +6 -6
- package/dist/web-effect.js.map +1 -1
- package/dist/web-experimental-http.d.ts +2 -0
- package/dist/web-experimental-http.js +12 -0
- package/dist/web-experimental-http.js.map +1 -0
- package/dist/web-experimental.d.ts +11 -0
- package/dist/web-experimental.js +49 -0
- package/dist/web-experimental.js.map +1 -0
- package/dist/web-guest-access.d.ts +4 -2
- package/dist/web-guest-access.js +22 -10
- package/dist/web-guest-access.js.map +1 -1
- package/dist/web-guest-rooms.js.map +1 -1
- package/dist/web-host-guard.d.ts +11 -0
- package/dist/web-host-guard.js +11 -0
- package/dist/web-host-guard.js.map +1 -1
- package/dist/web-icons.d.ts +33 -1
- package/dist/web-icons.js +83 -11
- package/dist/web-icons.js.map +1 -1
- package/dist/web-identity-color.js.map +1 -1
- package/dist/web-image-meta.d.ts +13 -0
- package/dist/web-image-meta.js +62 -0
- package/dist/web-image-meta.js.map +1 -1
- package/dist/web-images.js.map +1 -1
- package/dist/web-libavoid.d.ts +14 -0
- package/dist/web-libavoid.js +46 -0
- package/dist/web-libavoid.js.map +1 -0
- package/dist/web-login.js.map +1 -1
- package/dist/web-mentions.js.map +1 -1
- package/dist/web-mod-agent.js.map +1 -1
- package/dist/web-mod-store.d.ts +2 -0
- package/dist/web-mod-store.js +10 -0
- package/dist/web-mod-store.js.map +1 -1
- package/dist/web-office-store.d.ts +104 -0
- package/dist/web-office-store.js +236 -0
- package/dist/web-office-store.js.map +1 -0
- package/dist/web-owner-css-store.d.ts +44 -1
- package/dist/web-owner-css-store.js +70 -9
- package/dist/web-owner-css-store.js.map +1 -1
- package/dist/web-owner-css.js.map +1 -1
- package/dist/web-png.d.ts +33 -0
- package/dist/web-png.js +329 -0
- package/dist/web-png.js.map +1 -0
- package/dist/web-prefs.d.ts +6 -0
- package/dist/web-prefs.js +29 -3
- package/dist/web-prefs.js.map +1 -1
- package/dist/web-preview.d.ts +2 -0
- package/dist/web-preview.js +2 -0
- package/dist/web-preview.js.map +1 -1
- package/dist/web-profile.js.map +1 -1
- package/dist/web-reactions.d.ts +35 -0
- package/dist/web-reactions.js +47 -0
- package/dist/web-reactions.js.map +1 -0
- package/dist/web-request-context.d.ts +5 -0
- package/dist/web-request-context.js.map +1 -1
- package/dist/web-returning-device.d.ts +4 -2
- package/dist/web-returning-device.js +13 -6
- package/dist/web-returning-device.js.map +1 -1
- package/dist/web-schedules.js.map +1 -1
- package/dist/web-send-dedupe.d.ts +36 -0
- package/dist/web-send-dedupe.js +69 -0
- package/dist/web-send-dedupe.js.map +1 -0
- package/dist/web-session-authorization.js.map +1 -1
- package/dist/web-session.js +1 -1
- package/dist/web-session.js.map +1 -1
- package/dist/web-settings-patch.d.ts +3 -1
- package/dist/web-settings-patch.js +11 -5
- package/dist/web-settings-patch.js.map +1 -1
- package/dist/web-settings-tools.d.ts +6 -0
- package/dist/web-settings-tools.js +52 -17
- package/dist/web-settings-tools.js.map +1 -1
- package/dist/web-terminal-http.js +15 -2
- package/dist/web-terminal-http.js.map +1 -1
- package/dist/web-terminal.js +1 -1
- package/dist/web-terminal.js.map +1 -1
- package/dist/web-theme-store.d.ts +2 -2
- package/dist/web-theme-store.js +6 -3
- package/dist/web-theme-store.js.map +1 -1
- package/dist/web-theme.d.ts +29 -0
- package/dist/web-theme.js +89 -1
- package/dist/web-theme.js.map +1 -1
- package/dist/web-upload.d.ts +18 -1
- package/dist/web-upload.js +45 -7
- package/dist/web-upload.js.map +1 -1
- package/dist/web-widget.js.map +1 -1
- package/dist/webhook-emitter.js.map +1 -1
- package/dist/windows-shortcuts.d.ts +78 -0
- package/dist/windows-shortcuts.js +203 -0
- package/dist/windows-shortcuts.js.map +1 -0
- package/dist/workflow-templates/default.md +3 -0
- package/dist/working-state-journal.d.ts +68 -0
- package/dist/working-state-journal.js +93 -0
- package/dist/working-state-journal.js.map +1 -0
- package/dist/ws-server.js.map +1 -1
- package/dist/yaml-shared-nodes.d.ts +25 -0
- package/dist/yaml-shared-nodes.js +185 -0
- package/dist/yaml-shared-nodes.js.map +1 -0
- package/launcher/agend-install.cjs +828 -0
- package/launcher/install-admission.cjs +4 -39
- package/launcher/postinstall.cjs +14 -12
- package/launcher/private-home.cjs +43 -0
- package/package.json +16 -13
- package/scripts/preinstall-guard.cjs +2 -5
- package/templates/windows-shortcuts.ps1 +181 -0
- package/launcher/old-updater-note.cjs +0 -48
package/dist/ui/fleet-traffic.js
CHANGED
|
@@ -178,8 +178,17 @@ function endOf(L, name) {
|
|
|
178
178
|
|
|
179
179
|
// ── Routing: lanes the layout keeps empty ──
|
|
180
180
|
|
|
181
|
-
/**
|
|
182
|
-
|
|
181
|
+
/**
|
|
182
|
+
* A side lane's offset from the middle of the box's padding: at least 6 px from the box's border and from its cards.
|
|
183
|
+
* Negative: toward the border, on either side (#2329) — the first lines (o < 0) leave the cards a long last run for a head.
|
|
184
|
+
*/
|
|
185
|
+
const laneOffset = (o, PAD = OC.PAD) => Math.max(-PAD / 2 + 6, Math.min(PAD / 2 - 6, o * 0.5));
|
|
186
|
+
const leftLaneOf = (b, io, PAD) => b.x + PAD / 2 + io;
|
|
187
|
+
const rightLaneOf = (b, io, PAD) => b.x + b.w - PAD / 2 - io;
|
|
188
|
+
/** Where a line meets a card's side, from the side's middle (#2329): its full offset, a head's width from its neighbours. */
|
|
189
|
+
const sideOffset = (o) => Math.max(-OC.NH / 2 + 7, Math.min(OC.NH / 2 - 7, o));
|
|
190
|
+
/** The layout's own box padding and gutter (a phone's are narrower). */
|
|
191
|
+
const spacing = (L) => ({ PAD: L.pad ?? OC.PAD, GAP: L.gap ?? OC.GAP });
|
|
183
192
|
|
|
184
193
|
const firstBusOf = (L) => Math.min(...L.boxes.map((b) => b.busY));
|
|
185
194
|
/** From an end up to its row's bus lane: the points, and the lane's y. `o` spreads lines sharing a lane. */
|
|
@@ -194,17 +203,17 @@ function outward(L, end, o) {
|
|
|
194
203
|
const x = b.x + b.w / 2 + o * 1.5;
|
|
195
204
|
return { pts: [{ x, y: b.y }, { x, y: b.busY + o }], bus: b.busY + o };
|
|
196
205
|
}
|
|
197
|
-
const {
|
|
206
|
+
const { NW, NH } = OC, { PAD, GAP } = spacing(L);
|
|
198
207
|
const card = end.c, col = end.i % b.cols, rowI = Math.floor(end.i / b.cols), lines = Math.ceil(b.cells.length / b.cols);
|
|
199
|
-
const io = laneOffset(o);
|
|
200
|
-
const leftLane = b
|
|
208
|
+
const io = laneOffset(o, PAD);
|
|
209
|
+
const leftLane = leftLaneOf(b, io, PAD), rightLane = rightLaneOf(b, io, PAD);
|
|
201
210
|
let pts;
|
|
202
211
|
if (col === 0 || col === b.cols - 1) {
|
|
203
|
-
const left = col === 0, lane = left ? leftLane : rightLane, cy = card.y + NH / 2 +
|
|
212
|
+
const left = col === 0, lane = left ? leftLane : rightLane, cy = card.y + NH / 2 + sideOffset(o);
|
|
204
213
|
pts = [{ x: left ? card.x : card.x + NW, y: cy }, { x: lane, y: cy }, { x: lane, y: b.y }];
|
|
205
214
|
} else {
|
|
206
215
|
// A middle column: down into the gutter below its row, across to the nearer side lane.
|
|
207
|
-
const gy = card.y + NH + (rowI === lines - 1 ? PAD : GAP) / 2
|
|
216
|
+
const gy = card.y + NH + (rowI === lines - 1 ? PAD : GAP) / 2 - io / 2, cx = card.x + NW / 2 + o;
|
|
208
217
|
const lane = cx - b.x < b.w / 2 ? leftLane : rightLane;
|
|
209
218
|
pts = [{ x: cx, y: card.y + NH }, { x: cx, y: gy }, { x: lane, y: gy }, { x: lane, y: b.y }];
|
|
210
219
|
}
|
|
@@ -217,23 +226,23 @@ function outward(L, end, o) {
|
|
|
217
226
|
* else down into the gutter under its row (no card is there) and across. The last point is on the lane.
|
|
218
227
|
*/
|
|
219
228
|
function toLane(L, end, lane, o) {
|
|
220
|
-
const {
|
|
229
|
+
const { NW, NH } = OC, { PAD, GAP } = spacing(L);
|
|
221
230
|
const b = end.b, card = end.c, col = end.i % b.cols, rowI = Math.floor(end.i / b.cols), lines = Math.ceil(b.cells.length / b.cols);
|
|
222
|
-
const io = laneOffset(o);
|
|
231
|
+
const io = laneOffset(o, PAD);
|
|
223
232
|
const left = lane < b.x + b.w / 2;
|
|
224
233
|
if ((left && col === 0) || (!left && col === b.cols - 1)) {
|
|
225
|
-
const cy = card.y + NH / 2 +
|
|
234
|
+
const cy = card.y + NH / 2 + sideOffset(o);
|
|
226
235
|
return [{ x: left ? card.x : card.x + NW, y: cy }, { x: lane, y: cy }];
|
|
227
236
|
}
|
|
228
|
-
const gy = card.y + NH + (rowI === lines - 1 ? PAD : GAP) / 2
|
|
237
|
+
const gy = card.y + NH + (rowI === lines - 1 ? PAD : GAP) / 2 - io / 2, cx = card.x + NW / 2 + o;
|
|
229
238
|
return [{ x: cx, y: card.y + NH }, { x: cx, y: gy }, { x: lane, y: gy }];
|
|
230
239
|
}
|
|
231
240
|
/** Two cards in one open box: joined inside it, through the side lane nearer to them both (never out to the bus). */
|
|
232
241
|
function inBox(L, a, z, o) {
|
|
233
|
-
const b = a.b, { PAD } =
|
|
234
|
-
const io = laneOffset(o);
|
|
242
|
+
const b = a.b, { PAD } = spacing(L);
|
|
243
|
+
const io = laneOffset(o, PAD);
|
|
235
244
|
const mid = ((a.i % b.cols) + (z.i % b.cols)) / 2;
|
|
236
|
-
const lane = mid <= (b.cols - 1) / 2 ? b
|
|
245
|
+
const lane = mid <= (b.cols - 1) / 2 ? leftLaneOf(b, io, PAD) : rightLaneOf(b, io, PAD);
|
|
237
246
|
return [...toLane(L, a, lane, o), ...toLane(L, z, lane, o).reverse()];
|
|
238
247
|
}
|
|
239
248
|
/** Two of General's cards: under them, through the gap above the first row (or the canvas's empty space below). */
|
|
@@ -278,6 +287,42 @@ export function badgeAt(pts) {
|
|
|
278
287
|
return best;
|
|
279
288
|
}
|
|
280
289
|
|
|
290
|
+
// ── Arrowheads (#2329) ──
|
|
291
|
+
|
|
292
|
+
/** A line's stroke width (px) by its kind and phase: the work's and its report's are heavier, a team pair's the most. */
|
|
293
|
+
export const strokeOf = (hue, phase) => (phase === "work" || phase === "return" ? 3 : hue === "team" ? 3.5 : 2.5);
|
|
294
|
+
/**
|
|
295
|
+
* The arrowhead and the ends. In stroke widths (the markers' units, fleet-canvas.js): a head LEN long and WIDE across,
|
|
296
|
+
* the line's own stroke ending BACK inside it (its round cap under the head, never out past the tip); the source dot
|
|
297
|
+
* DOT across. In px: the tip GAP short of the card, the dot's centre START off its card; a head only on a last straight
|
|
298
|
+
* run that holds it and ROOM of line before it — else the line ends GAP short with none (its dot and pulse show the way).
|
|
299
|
+
*/
|
|
300
|
+
export const HEAD = { LEN: 2.8, WIDE: 2.3, BACK: 1.4, DOT: 2, GAP: 3.5, START: 5, ROOM: 1 };
|
|
301
|
+
/** Whether a line of stroke `w` px whose last straight run is `run` px long gets a head. */
|
|
302
|
+
export const headFits = (run, w) => run >= HEAD.GAP + HEAD.LEN * w + HEAD.ROOM;
|
|
303
|
+
/** An orthogonal polyline without repeated points or points in the middle of a straight run. */
|
|
304
|
+
function corners(pts) {
|
|
305
|
+
const q = pts.filter((pt, i) => !i || pt.x !== pts[i - 1].x || pt.y !== pts[i - 1].y).map((pt) => ({ x: pt.x, y: pt.y }));
|
|
306
|
+
return q.filter((pt, i) => i === 0 || i === q.length - 1 || !((q[i - 1].x === pt.x && pt.x === q[i + 1].x) || (q[i - 1].y === pt.y && pt.y === q[i + 1].y)));
|
|
307
|
+
}
|
|
308
|
+
/** Route `pts` as drawn: its start moved `a` px along its first run, its end `b` px back along its last (each keeps 1 px). */
|
|
309
|
+
export function trimEnds(pts, a, b) {
|
|
310
|
+
const q = corners(pts);
|
|
311
|
+
if (q.length < 2) return q;
|
|
312
|
+
const pull = (i, j, by) => {
|
|
313
|
+
const l = Math.hypot(q[j].x - q[i].x, q[j].y - q[i].y), t = Math.max(0, Math.min(by, l - 1)) / l;
|
|
314
|
+
q[i] = { x: q[i].x + (q[j].x - q[i].x) * t, y: q[i].y + (q[j].y - q[i].y) * t };
|
|
315
|
+
};
|
|
316
|
+
pull(0, 1, a);
|
|
317
|
+
pull(q.length - 1, q.length - 2, b);
|
|
318
|
+
return q;
|
|
319
|
+
}
|
|
320
|
+
/** The length of a route's last straight run. */
|
|
321
|
+
export function lastRun(pts) {
|
|
322
|
+
const q = corners(pts);
|
|
323
|
+
return q.length < 2 ? 0 : Math.hypot(q[q.length - 1].x - q[q.length - 2].x, q[q.length - 1].y - q[q.length - 2].y);
|
|
324
|
+
}
|
|
325
|
+
|
|
281
326
|
// ── Edges ──
|
|
282
327
|
|
|
283
328
|
/** The colour family of a kind: task, report, broadcast, and the rest (send, query, update). */
|
|
@@ -289,9 +334,14 @@ const settled = (phase) => (phase === "fading" || phase === "stale" ? "fading" :
|
|
|
289
334
|
|
|
290
335
|
/**
|
|
291
336
|
* The lines to draw from the store's items on layout `L`. `far`: zoomed out (one line per team pair). Each edge:
|
|
292
|
-
* { key, hue, phase, n, from, to, d, pts, badge } — `from`/`to` the instance (or team) names it
|
|
337
|
+
* { key, hue, phase, n, from, to, d, pts, w, head, badge, a, z, routed } — `from`/`to` the instance (or team) names it
|
|
338
|
+
* joins, for search; `pts` its route, end to end (on the cards); `d` the path drawn, its ends trimmed off the cards for
|
|
339
|
+
* the dot and the head (#2329); `w` its stroke width; `head` whether it has a head (headFits);
|
|
340
|
+
* `a`/`z` its two ends (fleet-route-scene.js routes the ones on cards). `routed` (#2182): the router's points by
|
|
341
|
+
* line key (fleet-route.js); a line it has none for keeps its lane route — or, while a new answer is on its way, the
|
|
342
|
+
* points the same two ends and kind had in the last one (a line changing phase keeps its place).
|
|
293
343
|
*/
|
|
294
|
-
export function trafficEdges(items, L, { far = false, max = MAX_EDGES } = {}) {
|
|
344
|
+
export function trafficEdges(items, L, { far = false, max = MAX_EDGES, routed = null } = {}) {
|
|
295
345
|
if (!L.boxes.length && L.tops.length < 2) return [];
|
|
296
346
|
// A top's way out is the first row's bus: with no box there is none, so only top ↔ top lines (betweenTops) are drawn.
|
|
297
347
|
const groups = new Map();
|
|
@@ -321,10 +371,23 @@ export function trafficEdges(items, L, { far = false, max = MAX_EDGES } = {}) {
|
|
|
321
371
|
add(`${a.key}>${z.key}|${hue(it.kind)}|${phase}`, { a, z, hue: hue(it.kind), phase, n: 1, names });
|
|
322
372
|
}
|
|
323
373
|
const list = [...groups.entries()].sort((x, y) => RANK[x[1].phase] - RANK[y[1].phase]).slice(0, max);
|
|
374
|
+
const lent = new Set();
|
|
375
|
+
const routedBy = (key) => {
|
|
376
|
+
if (!routed || far) return null;
|
|
377
|
+
const exact = routed.get(key);
|
|
378
|
+
if (exact) return exact;
|
|
379
|
+
const kin = key.slice(0, key.lastIndexOf("|") + 1);
|
|
380
|
+
for (const [k, pts] of routed) if (k.startsWith(kin) && !groups.has(k) && !lent.has(k)) { lent.add(k); return pts; }
|
|
381
|
+
return null;
|
|
382
|
+
};
|
|
324
383
|
return list.map(([key, g], i) => {
|
|
325
384
|
const o = ((i % 7) - 3) * 7; // neighbours 7 px apart (within the ±22 px bus lane)
|
|
326
|
-
const
|
|
327
|
-
|
|
385
|
+
const viaRouter = routedBy(key);
|
|
386
|
+
const pts = viaRouter ?? route(L, g.a, g.z, o);
|
|
387
|
+
const w = strokeOf(g.hue, g.phase), head = headFits(lastRun(pts), w);
|
|
388
|
+
const d = pathOf(trimEnds(pts, HEAD.START, HEAD.GAP + (head ? HEAD.BACK * w : 0)));
|
|
389
|
+
return { key, hue: g.hue, phase: g.phase, n: g.n, names: [...new Set(g.names)], d, pts, w, head, badge: g.n > 1 ? badgeAt(pts) : null,
|
|
390
|
+
a: g.a, z: g.z, routed: !!viaRouter };
|
|
328
391
|
});
|
|
329
392
|
}
|
|
330
393
|
|
package/dist/ui/fleet-usage.js
CHANGED
|
@@ -183,6 +183,38 @@ export function zonesChanged(report) {
|
|
|
183
183
|
return zones.length > 1 || (zones.length === 1 && zones[0] !== report.zone) ? zones : null;
|
|
184
184
|
}
|
|
185
185
|
|
|
186
|
+
/** Estimated pixel width of one character in the 9 px `.us-axis` label. */
|
|
187
|
+
const LABEL_CHAR_W = 5.5;
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* The 0-based indices of dates that should receive an x-axis label.
|
|
191
|
+
* Keeps the first (0) and last (n-1) when they are far enough apart not to
|
|
192
|
+
* overlap. When first and last are too close (n-1 < step), only the last
|
|
193
|
+
* (most recent) is shown — overlap-free takes precedence over keeping both
|
|
194
|
+
* ends. Intermediate labels are thinned so that no two consecutive shown
|
|
195
|
+
* labels are closer than `labelWidthPx` pixels apart.
|
|
196
|
+
*
|
|
197
|
+
* @param {number} n Number of data points (dates).
|
|
198
|
+
* @param {number} slotPx Pixels per slot (bar + gap).
|
|
199
|
+
* @param {number} labelWidthPx Estimated rendered width of one label.
|
|
200
|
+
* @returns {Set<number>}
|
|
201
|
+
*/
|
|
202
|
+
export function thinXLabels(n, slotPx, labelWidthPx) {
|
|
203
|
+
if (n <= 0) return new Set();
|
|
204
|
+
if (n === 1) return new Set([0]);
|
|
205
|
+
// Minimum slot steps between label centres (+ 2 px breathing room).
|
|
206
|
+
const step = Math.max(1, Math.ceil((labelWidthPx + 2) / slotPx));
|
|
207
|
+
// If first and last are too close to both be shown without overlap, keep
|
|
208
|
+
// only the last (most recent). The caller's "always first+last" contract
|
|
209
|
+
// is relaxed here: overlap-free takes precedence.
|
|
210
|
+
if (n - 1 < step) return new Set([n - 1]);
|
|
211
|
+
const idx = [0];
|
|
212
|
+
// Add intermediates that are at least `step` slots away from n-1.
|
|
213
|
+
for (let i = step; i < n - step; i += step) idx.push(i);
|
|
214
|
+
idx.push(n - 1); // always include last
|
|
215
|
+
return new Set(idx);
|
|
216
|
+
}
|
|
217
|
+
|
|
186
218
|
/** The trend's series: the top `TOP` agents by the measure over the range, then "others"; one value per date. */
|
|
187
219
|
export function trendSeries(report, dates) {
|
|
188
220
|
const priced = report.totals && report.totals.amount !== null && report.totals.amount > 0;
|
|
@@ -230,6 +262,8 @@ function Trend({ report, dates }) {
|
|
|
230
262
|
const max = Math.max(...sums, 0) || 1;
|
|
231
263
|
const what = s.priced ? tu("amount") : tu("trendTokens"), others = s.names.length > TOP;
|
|
232
264
|
const label = others ? tu("trendLabel", what, TOP, dates[0], dates[dates.length - 1]) : tu("trendLabelAll", what, s.names.length, dates[0], dates[dates.length - 1]);
|
|
265
|
+
// Thin x-axis labels: "MM-DD" (5 chars) × LABEL_CHAR_W ≈ 27.5 px at 9 px sans-serif.
|
|
266
|
+
const shownLabels = thinXLabels(dates.length, slot, 5 * LABEL_CHAR_W);
|
|
233
267
|
return html`<section class="us-trend card" aria-labelledby="us-trend-h">
|
|
234
268
|
<h3 id="us-trend-h">${tu("trend")} <span class="note">· ${s.priced ? tu("amount") : tu("trendTokens")}</span></h3>
|
|
235
269
|
<p class="note us-scale">${tu("scale", fmtMeasure(s.priced, max))}</p>
|
|
@@ -243,7 +277,7 @@ function Trend({ report, dates }) {
|
|
|
243
277
|
const h = v[i] > 0 ? Math.max(1, ((base - top) * v[i]) / max) : 0;
|
|
244
278
|
y -= h;
|
|
245
279
|
return h ? html`<rect key=${k} class=${`us-s${k}`} x=${left + i * slot + 2} y=${y} width=${slot - 4} height=${h}><title>${`${date} · ${s.names[k]}: ${fmtMeasure(s.priced, v[i])}`}</title></rect>` : null;
|
|
246
|
-
})}${(
|
|
280
|
+
})}${shownLabels.has(i) ? html`<text class="us-axis us-xlabel" x=${left + i * slot + slot / 2} y=${H - 6} text-anchor="middle">${date.slice(5)}</text>` : null}</g>`;
|
|
247
281
|
})}
|
|
248
282
|
</svg></div>
|
|
249
283
|
<${EarlierHint} wide=${wide} />
|
|
@@ -287,7 +321,7 @@ function Row({ r, group, max, measure }) {
|
|
|
287
321
|
const meta = [r.backends.join(", "), r.models.join(", "), r.profiles.join(", ")].filter(Boolean).join(" · ");
|
|
288
322
|
return html`<tr class="us-row">
|
|
289
323
|
<th scope="row" class="us-name"><span class="mono">${keyText(group, r.key)}</span>
|
|
290
|
-
<svg class="us-bar"
|
|
324
|
+
<svg class="us-bar" aria-hidden="true"><rect class="us-bar-bg" width="100%" height="6" rx="3" /><rect class="us-bar-fg" width=${`${Math.round(share * 100)}%`} height="6" rx="3" /></svg>
|
|
291
325
|
${group !== "instance" || meta ? html`<span class="us-meta">${meta}</span>` : null}
|
|
292
326
|
${r.flags.length ? html`<span class="us-flags">${r.flags.map((f) => html`<span key=${f} class="ch-tag">${flagText(f, r)}</span>`)}</span>` : null}</th>
|
|
293
327
|
<td data-label=${tu("amount")} class=${`num${r.amount === null ? " muted" : ""}`}>${amountText(r)}</td>
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
6
|
+
<meta name="referrer" content="no-referrer">
|
|
7
|
+
<meta name="robots" content="noindex">
|
|
8
|
+
<title>AgEnD</title>
|
|
9
|
+
<!--app-icon-->
|
|
10
|
+
<link rel="icon" href="/favicon.ico" sizes="16x16 32x32 64x64">
|
|
11
|
+
<link rel="icon" href="/favicon.svg" type="image/svg+xml" sizes="any">
|
|
12
|
+
<link rel="apple-touch-icon" href="/apple-touch-icon.png" sizes="180x180">
|
|
13
|
+
<!--/app-icon-->
|
|
14
|
+
<script src="/assets/theme.js"></script>
|
|
15
|
+
<link rel="stylesheet" href="/assets/tokens.css">
|
|
16
|
+
<style>
|
|
17
|
+
/* What a guest's browser gets for a page it cannot open (#2399 smoke: it was the gate's bare JSON). The sign-in
|
|
18
|
+
page's look: the app's tokens, light and dark (theme.js before paint). */
|
|
19
|
+
* { box-sizing: border-box; }
|
|
20
|
+
body { margin: 0; min-height: 100vh; display: flex; align-items: center; justify-content: center; padding: 16px;
|
|
21
|
+
background: var(--bg); color: var(--text); font: var(--fw-regular) var(--fs-md)/var(--lh-md) var(--font-ui); -webkit-font-smoothing: antialiased; }
|
|
22
|
+
main { width: 100%; max-width: 380px; background: var(--surface); border: 1px solid var(--border); border-radius: var(--r-lg); padding: 28px 24px; }
|
|
23
|
+
.brand { margin: 0 0 18px; font-size: var(--fs-lg); font-weight: var(--fw-bold); }
|
|
24
|
+
h1 { margin: 0 0 6px; font-size: var(--fs-xl); font-weight: var(--fw-semibold); }
|
|
25
|
+
p { margin: 0 0 18px; color: var(--text-2); }
|
|
26
|
+
a.go { display: block; width: 100%; padding: 12px; text-align: center; text-decoration: none; font: var(--fw-semibold) var(--fs-md) var(--font-ui);
|
|
27
|
+
color: var(--on-accent); background: var(--accent-fill); border-radius: var(--r-full); }
|
|
28
|
+
a.go:hover { opacity: .88; }
|
|
29
|
+
a.go:focus-visible { outline: none; box-shadow: 0 0 0 3px var(--accent-dim); }
|
|
30
|
+
</style>
|
|
31
|
+
</head>
|
|
32
|
+
<body>
|
|
33
|
+
<main id="refused" data-why="__WHY__">
|
|
34
|
+
<div class="brand">AgEnD</div>
|
|
35
|
+
<h1 id="title">This page is not open to guests</h1>
|
|
36
|
+
<p id="lead">This page is for the fleet's members. As a guest you can use the chats shared with you.</p>
|
|
37
|
+
<a id="go" class="go" href="/ui">Open your chats</a>
|
|
38
|
+
</main>
|
|
39
|
+
<script src="/assets/guest-refused.js"></script>
|
|
40
|
+
</body>
|
|
41
|
+
</html>
|
|
Binary file
|
|
Binary file
|
|
@@ -5,10 +5,14 @@
|
|
|
5
5
|
//
|
|
6
6
|
// The native <dialog> opened with showModal(): the browser traps focus and makes the rest inert. No style attribute
|
|
7
7
|
// (#1300): classes only. Only a file URL the chat itself rendered (/ui/file/<32 hex>) is ever shown.
|
|
8
|
+
//
|
|
9
|
+
// #2333: zoom and pan (image-zoom.js). The toolbar has −, the level (it toggles fit ↔ 100%), + and Fit width; the keys
|
|
10
|
+
// + / − / 0 (fit) / 1 (100%) / W (fit width). Each image opens fitted; stepping to another fits it again.
|
|
8
11
|
import { html, useEffect, useLayoutEffect, useRef, useState } from "/assets/app-html.js";
|
|
9
12
|
import { t } from "/assets/app-i18n.js";
|
|
10
13
|
import { createStore, useStore } from "/assets/app-store.js";
|
|
11
14
|
import { Icon } from "/assets/ui-icons.js";
|
|
15
|
+
import { createZoom, STEP } from "./image-zoom.js";
|
|
12
16
|
|
|
13
17
|
const FILE_URL = /^\/ui\/file\/[0-9a-f]{32}$/;
|
|
14
18
|
/** What is shown: { items: [{ src, name }], index, opener } or null. One per page; the chat that shows it owns it. */
|
|
@@ -54,6 +58,10 @@ function ImageLightbox({ items, index, opener, onClose }) {
|
|
|
54
58
|
const [at, setAt] = useState(index);
|
|
55
59
|
const ref = useRef(null);
|
|
56
60
|
const close = useRef(null);
|
|
61
|
+
const stage = useRef(null);
|
|
62
|
+
const img = useRef(null);
|
|
63
|
+
const zoom = useRef(null);
|
|
64
|
+
const [level, setLevel] = useState(null); // { scale, fit, mode } once the picture has loaded
|
|
57
65
|
const item = items[at] || items[0];
|
|
58
66
|
const many = items.length > 1;
|
|
59
67
|
const step = (d) => setAt((n) => (n + d + items.length) % items.length);
|
|
@@ -69,6 +77,25 @@ function ImageLightbox({ items, index, opener, onClose }) {
|
|
|
69
77
|
if (opener && typeof opener.focus === "function" && document.contains?.(opener)) opener.focus();
|
|
70
78
|
};
|
|
71
79
|
}, []);
|
|
80
|
+
useEffect(() => {
|
|
81
|
+
if (!stage.current || !img.current) return undefined;
|
|
82
|
+
const z = createZoom({ stage: stage.current, img: img.current, onChange: setLevel });
|
|
83
|
+
zoom.current = z;
|
|
84
|
+
return () => { z.destroy(); zoom.current = null; };
|
|
85
|
+
}, []);
|
|
86
|
+
// Another image: hidden until it has loaded and been fitted (its load resets the zoom). A layout effect — in the same
|
|
87
|
+
// commit that set the new src, before any load can arrive: a picture already in the cache (every thumbnail above it
|
|
88
|
+
// has loaded) fires its load before a passive effect would run, and such an effect then hid it for good with the
|
|
89
|
+
// level at "…" (the report on beta.24: Next "flashes once, then the next image is not visible"). A picture that is
|
|
90
|
+
// already complete here (the same file again: no load comes) is fitted at once.
|
|
91
|
+
const first = useRef(true);
|
|
92
|
+
useLayoutEffect(() => {
|
|
93
|
+
if (first.current) { first.current = false; return; }
|
|
94
|
+
const el = img.current;
|
|
95
|
+
if (el) el.classList.remove("lb-ready");
|
|
96
|
+
setLevel(null);
|
|
97
|
+
if (el && el.complete && el.naturalWidth > 0 && zoom.current) zoom.current.reset();
|
|
98
|
+
}, [at]);
|
|
72
99
|
useEffect(() => {
|
|
73
100
|
const d = ref.current;
|
|
74
101
|
if (!d) return undefined;
|
|
@@ -80,6 +107,12 @@ function ImageLightbox({ items, index, opener, onClose }) {
|
|
|
80
107
|
// The dark area (the dialog itself, or the stage around the image) closes it; the image and the controls do not.
|
|
81
108
|
const onClick = (e) => { if (e.target === d || (e.target && e.target.classList && e.target.classList.contains("lb-stage"))) onClose(); };
|
|
82
109
|
const onKey = (e) => {
|
|
110
|
+
const z = zoom.current;
|
|
111
|
+
if (z && !e.ctrlKey && !e.metaKey && !e.altKey) {
|
|
112
|
+
const act = { "+": () => z.zoomBy(STEP), "=": () => z.zoomBy(STEP), "-": () => z.zoomBy(1 / STEP), "_": () => z.zoomBy(1 / STEP),
|
|
113
|
+
"0": () => z.fit(), "1": () => z.actual(), w: () => z.fitWidth(), W: () => z.fitWidth() }[e.key];
|
|
114
|
+
if (act) { e.preventDefault(); act(); return; }
|
|
115
|
+
}
|
|
83
116
|
if (!many) return;
|
|
84
117
|
if (e.key === "ArrowRight") { e.preventDefault(); step(1); } else if (e.key === "ArrowLeft") { e.preventDefault(); step(-1); }
|
|
85
118
|
};
|
|
@@ -90,14 +123,25 @@ function ImageLightbox({ items, index, opener, onClose }) {
|
|
|
90
123
|
return () => { d.removeEventListener("cancel", onCancel); d.removeEventListener("close", onNativeClose); d.removeEventListener("click", onClick); d.removeEventListener("keydown", onKey); };
|
|
91
124
|
}, [onClose, many, items.length]);
|
|
92
125
|
const label = item.name ? t("chat.lbLabel", item.name) : t("chat.lbLabelNoName");
|
|
126
|
+
const pct = level ? Math.round(level.scale * 100) : null;
|
|
93
127
|
return html`<dialog data-trust="lightbox" ref=${ref} class="lb" role="dialog" aria-modal="true" aria-label=${label}>
|
|
94
128
|
<div class="lb-bar">
|
|
95
129
|
<span class="lb-name" title=${item.name}>${item.name}</span>
|
|
96
130
|
${many ? html`<span class="lb-count">${t("chat.lbCount", at + 1, items.length)}</span>` : null}
|
|
131
|
+
<span class="lb-zoom" role="group">
|
|
132
|
+
<button type="button" class="icon-btn lb-zoom-out" disabled=${!level} aria-label=${t("chat.lbZoomOut")} title=${t("chat.lbZoomOut")}
|
|
133
|
+
onClick=${() => zoom.current && zoom.current.zoomBy(1 / STEP)}><${Icon} name="minus" /></button>
|
|
134
|
+
<button type="button" class="btn btn-sm lb-level" disabled=${!level} aria-label=${t("chat.lbZoomLevel", pct ?? "")} title=${t("chat.lbZoomLevel", pct ?? "")}
|
|
135
|
+
onClick=${() => zoom.current && zoom.current.toggle()}>${pct === null ? "…" : `${pct}%`}</button>
|
|
136
|
+
<button type="button" class="icon-btn lb-zoom-in" disabled=${!level} aria-label=${t("chat.lbZoomIn")} title=${t("chat.lbZoomIn")}
|
|
137
|
+
onClick=${() => zoom.current && zoom.current.zoomBy(STEP)}><${Icon} name="plus" /></button>
|
|
138
|
+
<button type="button" class=${`icon-btn lb-fit-width${level && level.mode === "width" ? " on" : ""}`} disabled=${!level} aria-pressed=${level && level.mode === "width" ? "true" : "false"}
|
|
139
|
+
aria-label=${t("chat.lbFitWidth")} title=${t("chat.lbFitWidth")} onClick=${() => zoom.current && zoom.current.fitWidth()}><${Icon} name="fitwidth" /></button>
|
|
140
|
+
</span>
|
|
97
141
|
<a class="btn btn-sm lb-orig" href=${item.src} target="_blank" rel="noopener noreferrer"><${Icon} name="external" size=${14} /><span class="lbl">${t("chat.lbOpenOriginal")}</span></a>
|
|
98
142
|
<button type="button" class="icon-btn lb-close" ref=${close} aria-label=${t("chat.lbClose")} title=${t("chat.lbClose")} onClick=${onClose}><${Icon} name="close" /></button>
|
|
99
143
|
</div>
|
|
100
|
-
<div class="lb-stage"><img class="lb-img" src=${item.src} alt=${item.name} /></div>
|
|
144
|
+
<div class="lb-stage" ref=${stage}><img class="lb-img" ref=${img} src=${item.src} alt=${item.name} draggable=${false} /></div>
|
|
101
145
|
${many ? html`<button type="button" class="icon-btn lb-nav lb-prev" aria-label=${t("chat.lbPrev")} title=${t("chat.lbPrev")} onClick=${() => step(-1)}><${Icon} name="back" /></button>
|
|
102
146
|
<button type="button" class="icon-btn lb-nav lb-next" aria-label=${t("chat.lbNext")} title=${t("chat.lbNext")} onClick=${() => step(1)}><${Icon} name="forward" /></button>` : null}
|
|
103
147
|
</dialog>`;
|
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
// #2333: zoom and pan for the chat image lightbox. The math is pure (a view box, the image's natural size, a scale and
|
|
2
|
+
// an offset) and unit-tested; `createZoom` applies it to one <img> in one stage as a CSSOM transform — translate then
|
|
3
|
+
// scale from the top-left corner, so the browser never re-decodes the picture, and no style attribute is written
|
|
4
|
+
// (#1300: the CSSOM is not governed by the page's style-src).
|
|
5
|
+
//
|
|
6
|
+
// Gestures: wheel and ctrl/⌘+wheel (a trackpad pinch arrives as ctrl+wheel) zoom around the cursor; once the image is
|
|
7
|
+
// larger than the view a plain wheel scrolls it instead, so a tall sheet reads like a page. A mouse click on the image
|
|
8
|
+
// toggles fit ↔ 100% at that point; a drag pans. Touch: two fingers pinch (and pan), one finger pans, a double tap
|
|
9
|
+
// toggles fit ↔ 100%. A gesture that moved is never also a click (the lightbox closes on a click on the dark area).
|
|
10
|
+
|
|
11
|
+
/** The largest zoom: 8× the picture's own pixels. */
|
|
12
|
+
export const MAX_SCALE = 8;
|
|
13
|
+
/** One step of +/−, the keys or the toolbar. */
|
|
14
|
+
export const STEP = 1.25;
|
|
15
|
+
/** A pointer that moved less than this (CSS px) clicked or tapped; more, it dragged. */
|
|
16
|
+
export const SLOP = 4;
|
|
17
|
+
/** Two taps within this long and this close are a double tap. */
|
|
18
|
+
export const DOUBLE_TAP_MS = 300;
|
|
19
|
+
export const DOUBLE_TAP_PX = 24;
|
|
20
|
+
|
|
21
|
+
/** The whole picture in the view, never enlarged. view = { x, y, w, h } (stage coordinates); img = { w, h } natural. */
|
|
22
|
+
export function fitScale(view, img) {
|
|
23
|
+
return Math.min(1, view.w / img.w, view.h / img.h);
|
|
24
|
+
}
|
|
25
|
+
/** The picture's width across the view, never enlarged: a tall sheet then scrolls. */
|
|
26
|
+
export function fitWidthScale(view, img) {
|
|
27
|
+
return Math.min(1, view.w / img.w);
|
|
28
|
+
}
|
|
29
|
+
/** The scales a gesture may reach: down to fit (or 100%, for a picture smaller than the view), up to MAX_SCALE. */
|
|
30
|
+
export function scaleLimits(view, img) {
|
|
31
|
+
return { min: fitScale(view, img), max: Math.max(MAX_SCALE, fitScale(view, img)) };
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Keep the picture in view: an axis it does not fill is centred; an axis it overflows has no gap at either edge.
|
|
36
|
+
* z = { s, tx, ty } (tx, ty: where the picture's top-left corner is, in stage coordinates). Returns a new z.
|
|
37
|
+
*/
|
|
38
|
+
export function clampPan(z, view, img) {
|
|
39
|
+
const axis = (t, size, start, len) => size <= len ? start + (len - size) / 2 : Math.min(start, Math.max(start + len - size, t));
|
|
40
|
+
return { s: z.s, tx: axis(z.tx, img.w * z.s, view.x, view.w), ty: axis(z.ty, img.h * z.s, view.y, view.h) };
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/** Scale to `s` (within the limits) keeping the picture point under (px, py) where it is. */
|
|
44
|
+
export function zoomAt(z, s, px, py, view, img) {
|
|
45
|
+
const { min, max } = scaleLimits(view, img);
|
|
46
|
+
const to = Math.min(max, Math.max(min, s));
|
|
47
|
+
const k = to / z.s;
|
|
48
|
+
return clampPan({ s: to, tx: px - (px - z.tx) * k, ty: py - (py - z.ty) * k }, view, img);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/** Fit: the whole picture, centred. */
|
|
52
|
+
export function fitView(view, img) {
|
|
53
|
+
return clampPan({ s: fitScale(view, img), tx: 0, ty: 0 }, view, img);
|
|
54
|
+
}
|
|
55
|
+
/** Fit width: the picture across the view, from its top. */
|
|
56
|
+
export function fitWidthView(view, img) {
|
|
57
|
+
return clampPan({ s: fitWidthScale(view, img), tx: 0, ty: view.y }, view, img);
|
|
58
|
+
}
|
|
59
|
+
/** Whether the picture is larger than the view on either axis (then a plain wheel scrolls it, and a drag pans it). */
|
|
60
|
+
export function overflows(z, view, img) {
|
|
61
|
+
return img.w * z.s > view.w + 0.5 || img.h * z.s > view.h + 0.5;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/** Wheel distance in CSS px (a line is 16, a page the view's height). */
|
|
65
|
+
function wheelPx(e, view) {
|
|
66
|
+
const unit = e.deltaMode === 1 ? 16 : e.deltaMode === 2 ? view.h : 1;
|
|
67
|
+
return { dx: (e.deltaX || 0) * unit, dy: (e.deltaY || 0) * unit };
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Drive one picture in one stage. `onChange({ scale, fit, mode })` after every change (mode: "fit" | "width" | "free").
|
|
72
|
+
* Returns { reset, fit, fitWidth, actual, zoomBy, toggle, destroy }. Nothing happens until the picture has loaded.
|
|
73
|
+
*/
|
|
74
|
+
export function createZoom({ stage, img, onChange }) {
|
|
75
|
+
let nat = null; // { w, h } once loaded
|
|
76
|
+
let z = { s: 1, tx: 0, ty: 0 };
|
|
77
|
+
let mode = "fit";
|
|
78
|
+
const pointers = new Map(); // pointerId → { x, y }
|
|
79
|
+
let gesture = null; // { kind: "drag" | "pinch", ... }
|
|
80
|
+
let moved = false; // the gesture now ending moved: its click is not a click
|
|
81
|
+
let lastTap = null; // { t, x, y } of the last touch tap
|
|
82
|
+
let lastType = "mouse"; // the pointer type of the last press: a touch tap's click never toggles (double tap does);
|
|
83
|
+
// a mouse or a pen click does, and neither runs the double-tap logic
|
|
84
|
+
|
|
85
|
+
const view = () => {
|
|
86
|
+
const r = stage.getBoundingClientRect();
|
|
87
|
+
const cs = typeof getComputedStyle === "function" ? getComputedStyle(stage) : null;
|
|
88
|
+
const px = (k) => (cs ? parseFloat(cs[k]) || 0 : 0);
|
|
89
|
+
const x = px("paddingLeft"), y = px("paddingTop");
|
|
90
|
+
return { x, y, w: Math.max(1, r.width - x - px("paddingRight")), h: Math.max(1, r.height - y - px("paddingBottom")) };
|
|
91
|
+
};
|
|
92
|
+
const at = (e) => { const r = stage.getBoundingClientRect(); return { x: e.clientX - r.left, y: e.clientY - r.top }; };
|
|
93
|
+
const apply = () => {
|
|
94
|
+
img.style.transform = `translate(${z.tx}px, ${z.ty}px) scale(${z.s})`;
|
|
95
|
+
stage.classList.toggle("lb-zoomed", !!nat && overflows(z, view(), nat));
|
|
96
|
+
if (onChange) onChange({ scale: z.s, fit: nat ? fitScale(view(), nat) : 1, mode });
|
|
97
|
+
};
|
|
98
|
+
const set = (next, m = "free") => { if (!nat) return; z = next; mode = m; apply(); };
|
|
99
|
+
|
|
100
|
+
const api = {
|
|
101
|
+
/** A new picture (or the first): read its size and fit it. */
|
|
102
|
+
reset() {
|
|
103
|
+
nat = img.naturalWidth > 0 && img.naturalHeight > 0 ? { w: img.naturalWidth, h: img.naturalHeight } : null;
|
|
104
|
+
img.classList.toggle("lb-ready", !!nat);
|
|
105
|
+
img.classList.remove("lb-err");
|
|
106
|
+
if (nat) set(fitView(view(), nat), "fit");
|
|
107
|
+
},
|
|
108
|
+
fit() { if (nat) set(fitView(view(), nat), "fit"); },
|
|
109
|
+
fitWidth() { if (nat) set(fitWidthView(view(), nat), "width"); },
|
|
110
|
+
/** 100% at a point (default: the middle of the view). */
|
|
111
|
+
actual(px, py) {
|
|
112
|
+
if (!nat) return;
|
|
113
|
+
const v = view();
|
|
114
|
+
set(zoomAt(z, 1, px ?? v.x + v.w / 2, py ?? v.y + v.h / 2, v, nat));
|
|
115
|
+
},
|
|
116
|
+
zoomBy(factor, px, py) {
|
|
117
|
+
if (!nat) return;
|
|
118
|
+
const v = view();
|
|
119
|
+
set(zoomAt(z, z.s * factor, px ?? v.x + v.w / 2, py ?? v.y + v.h / 2, v, nat));
|
|
120
|
+
},
|
|
121
|
+
/** Fit ↔ 100%: anything but fit goes back to fit. */
|
|
122
|
+
toggle(px, py) {
|
|
123
|
+
if (!nat) return;
|
|
124
|
+
if (mode === "fit" && Math.abs(z.s - 1) > 1e-6) api.actual(px, py); else api.fit();
|
|
125
|
+
},
|
|
126
|
+
destroy() {
|
|
127
|
+
stage.removeEventListener("wheel", onWheel);
|
|
128
|
+
stage.removeEventListener("pointerdown", onDown);
|
|
129
|
+
stage.removeEventListener("pointermove", onMove);
|
|
130
|
+
stage.removeEventListener("pointerup", onUp);
|
|
131
|
+
stage.removeEventListener("pointercancel", onUp);
|
|
132
|
+
stage.removeEventListener("click", onClick, true);
|
|
133
|
+
img.removeEventListener("load", api.reset);
|
|
134
|
+
img.removeEventListener("error", onError);
|
|
135
|
+
if (typeof window !== "undefined") window.removeEventListener("resize", onResize);
|
|
136
|
+
},
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
function onWheel(e) {
|
|
140
|
+
if (!nat) return;
|
|
141
|
+
e.preventDefault();
|
|
142
|
+
const v = view();
|
|
143
|
+
const { dx, dy } = wheelPx(e, v);
|
|
144
|
+
if (e.ctrlKey || e.metaKey || !overflows(z, v, nat)) {
|
|
145
|
+
const p = at(e);
|
|
146
|
+
// A pinch on a trackpad sends small ctrl+wheel steps; a wheel notch is ~100 px.
|
|
147
|
+
set(zoomAt(z, z.s * Math.exp(-dy * (e.ctrlKey ? 0.01 : 0.002)), p.x, p.y, v, nat));
|
|
148
|
+
} else {
|
|
149
|
+
// Shift+wheel on a mouse scrolls sideways.
|
|
150
|
+
const sx = e.shiftKey && !dx ? dy : dx, sy = e.shiftKey && !dx ? 0 : dy;
|
|
151
|
+
set(clampPan({ s: z.s, tx: z.tx - sx, ty: z.ty - sy }, v, nat), mode === "width" ? "width" : "free");
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
function onDown(e) {
|
|
155
|
+
if (!nat || (e.pointerType === "mouse" && e.button !== 0)) return;
|
|
156
|
+
lastType = e.pointerType || "mouse";
|
|
157
|
+
pointers.set(e.pointerId, at(e));
|
|
158
|
+
// No pointer capture yet: it would retarget the click to the stage, and a click on the picture is not one on the
|
|
159
|
+
// dark area. Captured only once the pointer moves (capture()).
|
|
160
|
+
if (pointers.size === 1) { moved = false; gesture = { kind: "drag", from: at(e), z0: z }; }
|
|
161
|
+
else if (pointers.size === 2) {
|
|
162
|
+
const [a, b] = [...pointers.values()];
|
|
163
|
+
gesture = { kind: "pinch", d0: Math.hypot(b.x - a.x, b.y - a.y) || 1, m0: { x: (a.x + b.x) / 2, y: (a.y + b.y) / 2 }, z0: z };
|
|
164
|
+
moved = true; // two fingers: never a tap
|
|
165
|
+
for (const id of pointers.keys()) capture(id);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
function onMove(e) {
|
|
169
|
+
if (!gesture || !pointers.has(e.pointerId)) return;
|
|
170
|
+
pointers.set(e.pointerId, at(e));
|
|
171
|
+
const v = view();
|
|
172
|
+
if (gesture.kind === "pinch" && pointers.size >= 2) {
|
|
173
|
+
const [a, b] = [...pointers.values()];
|
|
174
|
+
const m = { x: (a.x + b.x) / 2, y: (a.y + b.y) / 2 };
|
|
175
|
+
const { z0, m0, d0 } = gesture;
|
|
176
|
+
const s = z0.s * (Math.hypot(b.x - a.x, b.y - a.y) / d0);
|
|
177
|
+
// The picture point under the fingers' first midpoint follows the midpoint (zoom and pan in one).
|
|
178
|
+
const zoomed = zoomAt(z0, s, m0.x, m0.y, v, nat);
|
|
179
|
+
set(clampPan({ s: zoomed.s, tx: zoomed.tx + (m.x - m0.x), ty: zoomed.ty + (m.y - m0.y) }, v, nat));
|
|
180
|
+
} else if (gesture.kind === "drag") {
|
|
181
|
+
const p = pointers.get(e.pointerId);
|
|
182
|
+
const dx = p.x - gesture.from.x, dy = p.y - gesture.from.y;
|
|
183
|
+
if (!moved && Math.hypot(dx, dy) < SLOP) return;
|
|
184
|
+
if (!moved) capture(e.pointerId);
|
|
185
|
+
moved = true;
|
|
186
|
+
if (overflows(gesture.z0, v, nat)) set(clampPan({ s: gesture.z0.s, tx: gesture.z0.tx + dx, ty: gesture.z0.ty + dy }, v, nat), mode === "width" ? "width" : "free");
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
function onUp(e) {
|
|
190
|
+
if (!pointers.has(e.pointerId)) return;
|
|
191
|
+
const p = pointers.get(e.pointerId);
|
|
192
|
+
pointers.delete(e.pointerId);
|
|
193
|
+
if (pointers.size === 1 && gesture && gesture.kind === "pinch") {
|
|
194
|
+
// One finger left after a pinch: it pans on from here.
|
|
195
|
+
const [rest] = [...pointers.values()];
|
|
196
|
+
gesture = { kind: "drag", from: rest, z0: z };
|
|
197
|
+
return;
|
|
198
|
+
}
|
|
199
|
+
if (pointers.size > 0) return;
|
|
200
|
+
const tapped = !moved && gesture && gesture.kind === "drag";
|
|
201
|
+
gesture = null;
|
|
202
|
+
// Only a finger double-taps: a mouse or a pen clicks (onClick toggles), so a pen tap must not also count here.
|
|
203
|
+
if (!tapped || e.pointerType !== "touch") return;
|
|
204
|
+
// A touch tap: a second one soon and near is a double tap — fit ↔ 100% there.
|
|
205
|
+
const now = typeof performance !== "undefined" ? performance.now() : Date.now();
|
|
206
|
+
if (lastTap && now - lastTap.t < DOUBLE_TAP_MS && Math.hypot(p.x - lastTap.x, p.y - lastTap.y) < DOUBLE_TAP_PX) {
|
|
207
|
+
lastTap = null;
|
|
208
|
+
moved = true; // its click neither closes nor toggles again
|
|
209
|
+
api.toggle(p.x, p.y);
|
|
210
|
+
} else lastTap = { t: now, x: p.x, y: p.y };
|
|
211
|
+
}
|
|
212
|
+
/** A drag or pinch keeps its pointers even off the stage. */
|
|
213
|
+
function capture(id) {
|
|
214
|
+
if (stage.setPointerCapture) { try { stage.setPointerCapture(id); } catch { /* already gone */ } }
|
|
215
|
+
}
|
|
216
|
+
// Capture phase: before the lightbox's own click (which closes on the dark area) can see it. A click that ended a
|
|
217
|
+
// drag or a double tap is swallowed; a mouse click on the picture toggles fit ↔ 100% there. A touch tap's click on
|
|
218
|
+
// the picture does nothing (a single tap waits for a double tap); on the dark area it still closes.
|
|
219
|
+
function onClick(e) {
|
|
220
|
+
if (moved) { moved = false; e.stopPropagation(); e.preventDefault(); return; }
|
|
221
|
+
if (e.target === img && nat && lastType !== "touch") {
|
|
222
|
+
const p = at(e);
|
|
223
|
+
e.stopPropagation();
|
|
224
|
+
api.toggle(p.x, p.y);
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
// A picture that does not load is shown as the browser shows a broken one, where the old lightbox put it.
|
|
228
|
+
const onError = () => { nat = null; img.classList.add("lb-err"); img.classList.remove("lb-ready"); if (onChange) onChange(null); };
|
|
229
|
+
const onResize = () => { if (!nat) return; if (mode === "fit") api.fit(); else if (mode === "width") api.fitWidth(); else set(clampPan(z, view(), nat)); };
|
|
230
|
+
|
|
231
|
+
stage.addEventListener("wheel", onWheel, { passive: false });
|
|
232
|
+
stage.addEventListener("pointerdown", onDown);
|
|
233
|
+
stage.addEventListener("pointermove", onMove);
|
|
234
|
+
stage.addEventListener("pointerup", onUp);
|
|
235
|
+
stage.addEventListener("pointercancel", onUp);
|
|
236
|
+
stage.addEventListener("click", onClick, true);
|
|
237
|
+
img.addEventListener("load", api.reset);
|
|
238
|
+
img.addEventListener("error", onError);
|
|
239
|
+
if (typeof window !== "undefined") window.addEventListener("resize", onResize);
|
|
240
|
+
if (img.complete) api.reset();
|
|
241
|
+
return api;
|
|
242
|
+
}
|