@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
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
// The Office view's "zones" layout: used when a skin names areas for states (its pack's `stateZones`,
|
|
2
|
+
// fleet-office-pack.js). Each instance goes to the area for its state now and takes a seat slot there; when its state
|
|
3
|
+
// changes it walks (along the pack's waypoints) to the new area. Pure — fleet-office.js draws it. Also here: what a
|
|
4
|
+
// character says in its speech bubble, and which furniture is active.
|
|
5
|
+
import { LOGICAL } from "./fleet-office-pack.js";
|
|
6
|
+
import { nameHash, pathLength, along, routeVia } from "./fleet-office-model.js";
|
|
7
|
+
|
|
8
|
+
/** The area for a state: the pack's own, else its "default", else its first area. */
|
|
9
|
+
export function zoneOf(pack, state) {
|
|
10
|
+
const z = pack.stateZones || {};
|
|
11
|
+
return z[state] || z.default || Object.keys(pack.areas)[0];
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/** How far apart overflow spots are, and how far they stay from the area's seats (logical units). */
|
|
15
|
+
export const OVERFLOW = { STEP: 56, CLEAR: 40, EDGE: 24 };
|
|
16
|
+
/** An area's overflow spots, for when its seats are taken: a grid inside its rect, clear of its seats, in reading order. */
|
|
17
|
+
export function overflowSpots(area) {
|
|
18
|
+
const out = [], { x, y, w, h } = area.rect;
|
|
19
|
+
for (let sy = y + OVERFLOW.EDGE + 30; sy <= y + h - OVERFLOW.EDGE; sy += OVERFLOW.STEP) {
|
|
20
|
+
for (let sx = x + OVERFLOW.EDGE; sx <= x + w - OVERFLOW.EDGE; sx += OVERFLOW.STEP) {
|
|
21
|
+
if (area.seats.every((s) => Math.hypot(s.x - sx, s.y - sy) >= OVERFLOW.CLEAR)) out.push({ x: sx, y: sy });
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
return out;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Seats for everyone: `wants` [{ name, area }] in the fleet's order; `prev` what the last call gave (name → { area,
|
|
29
|
+
* slot }). Someone still in the same area keeps their slot; the others take the first free seat, in the area's order,
|
|
30
|
+
* then an overflow spot (slot -1, -2, …). No two share one. Whoever finds neither is counted in `crowd` (area → n).
|
|
31
|
+
* Returns { seats: Map name → { area, slot, x, y }, crowd: Map }.
|
|
32
|
+
*/
|
|
33
|
+
export function assignSeats(pack, wants, prev = new Map()) {
|
|
34
|
+
const seats = new Map(), crowd = new Map(), taken = new Map(), spill = new Map();
|
|
35
|
+
const spotsOf = (area) => { if (!spill.has(area)) spill.set(area, overflowSpots(pack.areas[area])); return spill.get(area); };
|
|
36
|
+
const pos = (area, slot) => (slot >= 0 ? pack.areas[area].seats[slot] : spotsOf(area)[-slot - 1]);
|
|
37
|
+
const usedIn = (area) => { if (!taken.has(area)) taken.set(area, new Set()); return taken.get(area); };
|
|
38
|
+
const give = (name, area, slot) => { const p = pos(area, slot); usedIn(area).add(slot); seats.set(name, { area, slot, x: p.x, y: p.y }); };
|
|
39
|
+
for (const w of wants) {
|
|
40
|
+
const p = prev.get(w.name);
|
|
41
|
+
if (!p || p.area !== w.area || !pack.areas[w.area] || !pos(w.area, p.slot) || usedIn(w.area).has(p.slot)) continue;
|
|
42
|
+
give(w.name, w.area, p.slot);
|
|
43
|
+
}
|
|
44
|
+
for (const w of wants) {
|
|
45
|
+
if (seats.has(w.name) || !pack.areas[w.area]) continue;
|
|
46
|
+
const used = usedIn(w.area), area = pack.areas[w.area];
|
|
47
|
+
let slot = area.seats.findIndex((_, i) => !used.has(i));
|
|
48
|
+
if (slot < 0) { const j = spotsOf(w.area).findIndex((_, i) => !used.has(-i - 1)); slot = j < 0 ? null : -j - 1; }
|
|
49
|
+
if (slot === null) crowd.set(w.area, (crowd.get(w.area) || 0) + 1);
|
|
50
|
+
else give(w.name, w.area, slot);
|
|
51
|
+
}
|
|
52
|
+
return { seats, crowd };
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* The zones layout for `nodes` (buildOrgView's) on a pack: everyone but the missing at their seat. The same shape as
|
|
57
|
+
* the desks layout (rooms → desks with a seat), in logical units, plus `mode: "zones"`, the pack's areas and graph, the
|
|
58
|
+
* seats given (`seats`, the next call's `prev`) and `crowd`.
|
|
59
|
+
*/
|
|
60
|
+
export function zonesLayout(pack, nodes, prev = new Map()) {
|
|
61
|
+
const wants = nodes.filter((n) => n.state !== "missing").map((n) => ({ name: n.name, area: zoneOf(pack, n.state) }));
|
|
62
|
+
const { seats, crowd } = assignSeats(pack, wants, prev);
|
|
63
|
+
const desks = nodes.filter((n) => seats.has(n.name)).map((n) => {
|
|
64
|
+
const s = seats.get(n.name);
|
|
65
|
+
return { key: `zone/${n.name}`, node: n, x: s.x - 24, y: s.y - 52, w: 48, h: 66, seat: { x: s.x, y: s.y }, wp: null, area: s.area, slot: s.slot };
|
|
66
|
+
});
|
|
67
|
+
return { mode: "zones", rooms: [{ key: "\u0000zones", name: null, lobby: false, x: 0, y: 0, w: LOGICAL.w, h: LOGICAL.h, desks }], areas: pack.areas,
|
|
68
|
+
waypoints: pack.waypoints || [], edges: pack.edges || [], door: pack.door, width: LOGICAL.w, height: LOGICAL.h, seats, crowd };
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// ── Walking ──
|
|
72
|
+
|
|
73
|
+
/** Logical units per ms (crossing the canvas takes about six seconds). */
|
|
74
|
+
export const WALK = { SPEED: 0.2 };
|
|
75
|
+
/**
|
|
76
|
+
* Where each character is. `sync(targets, now, graph, still)`: targets name → { x, y }; someone new is already there;
|
|
77
|
+
* someone whose target moved walks from where they are now along the graph (routeVia) — or is there at once when
|
|
78
|
+
* `still` (reduced motion); someone gone is forgotten. `at(name, now)` → { x, y, walking }; `moving(now)`.
|
|
79
|
+
*/
|
|
80
|
+
export function createWalkers() {
|
|
81
|
+
const by = new Map();
|
|
82
|
+
const posAt = (w, now) => {
|
|
83
|
+
if (!w.pts) return { x: w.to.x, y: w.to.y, walking: false };
|
|
84
|
+
const t = (now - w.start) / w.ms;
|
|
85
|
+
if (t >= 1) return { x: w.to.x, y: w.to.y, walking: false };
|
|
86
|
+
const p = along(w.pts, Math.max(0, t));
|
|
87
|
+
return { x: p.x, y: p.y, walking: true };
|
|
88
|
+
};
|
|
89
|
+
return {
|
|
90
|
+
sync(targets, now, graph, still = false) {
|
|
91
|
+
for (const name of [...by.keys()]) if (!targets.has(name)) by.delete(name);
|
|
92
|
+
for (const [name, to] of targets) {
|
|
93
|
+
const w = by.get(name);
|
|
94
|
+
if (!w || still) { if (!w || w.to.x !== to.x || w.to.y !== to.y) by.set(name, { pts: null, to }); continue; }
|
|
95
|
+
if (w.to.x === to.x && w.to.y === to.y) continue;
|
|
96
|
+
const here = posAt(w, now), pts = routeVia(graph, { x: here.x, y: here.y }, { x: to.x, y: to.y });
|
|
97
|
+
by.set(name, { pts, start: now, ms: Math.max(1, pathLength(pts) / WALK.SPEED), to });
|
|
98
|
+
}
|
|
99
|
+
},
|
|
100
|
+
at(name, now) { const w = by.get(name); return w ? posAt(w, now) : null; },
|
|
101
|
+
moving(now) { for (const w of by.values()) if (posAt(w, now).walking) return true; return false; },
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* The person at (x, y) in layout px as the scene draws them (#2431 review): each desk's box moved to where its person is
|
|
107
|
+
* now (`posOf(desk)`: a walker's position; null: at the seat), the one drawn last (the nearest) tried first.
|
|
108
|
+
*/
|
|
109
|
+
export function personAt(desks, x, y, posOf) {
|
|
110
|
+
const people = desks.filter((d) => d.node).map((d) => { const at = posOf(d) || d.seat; return { d, at, dx: at.x - d.seat.x, dy: at.y - d.seat.y }; })
|
|
111
|
+
.sort((a, b) => a.at.y - b.at.y);
|
|
112
|
+
for (let i = people.length - 1; i >= 0; i--) {
|
|
113
|
+
const { d, dx, dy } = people[i];
|
|
114
|
+
if (x >= d.x + dx && x < d.x + dx + d.w && y >= d.y + dy && y < d.y + dy + d.h) return d;
|
|
115
|
+
}
|
|
116
|
+
return null;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// ── Speech bubbles ──
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* A round is PERIOD_MS; a character speaks in a round when it is their turn (one in SHARE, by name) — always when it
|
|
123
|
+
* calls for the person — for SHOW_MS, its line typed at CHAR_MS a character. Never message content: only the lines given.
|
|
124
|
+
*/
|
|
125
|
+
export const BUBBLE = { PERIOD_MS: 10000, SHOW_MS: 4500, CHAR_MS: 55, SHARE: 4 };
|
|
126
|
+
/** The states whose bubble always shows (the person is called). */
|
|
127
|
+
export const CALLING = new Set(["needs", "crashed", "stuck"]);
|
|
128
|
+
/**
|
|
129
|
+
* What `name` (in `state`) says at `ms`: { text, shown } (shown: how many characters are typed so far), or null.
|
|
130
|
+
* `ms` 0 is still (reduced motion): the line of round 0, all of it, at once.
|
|
131
|
+
*/
|
|
132
|
+
export function bubbleAt(name, state, ms, lines) {
|
|
133
|
+
if (!lines || !lines.length) return null;
|
|
134
|
+
const h = nameHash(`${name}\u0000${state}`), always = CALLING.has(state);
|
|
135
|
+
const t = Math.max(0, ms) + (h % BUBBLE.PERIOD_MS), round = Math.floor(t / BUBBLE.PERIOD_MS), into = t - round * BUBBLE.PERIOD_MS;
|
|
136
|
+
if (!always && ((h >>> 8) + round) % BUBBLE.SHARE !== 0) return null;
|
|
137
|
+
if (ms > 0 && !always && into > BUBBLE.SHOW_MS) return null;
|
|
138
|
+
const text = lines[(h + round) % lines.length], all = [...text].length;
|
|
139
|
+
return { text, shown: ms > 0 ? Math.min(all, 1 + Math.floor(into / BUBBLE.CHAR_MS)) : all };
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Where this frame's bubbles go so no two overlap (#2431: neighbours' bubbles covered each other on a phone). `items`:
|
|
143
|
+
* { key, calling, shown, box, tail? } with `box` the bubble's whole box ({ x, y, w, h }, for its whole line, so it never
|
|
144
|
+
* moves while typing out) and `tail` the point its tail reaches: the strip from the box down to it is kept clear too. The ones calling for the person go first (they always show), then the one that started earliest
|
|
145
|
+
* (the most typed out) — one already showing keeps its place. Each takes its spot, or one row up (`lift`) when that is
|
|
146
|
+
* free; else it waits for another round. Returns the placed ones, each with its `dy` (0 or -lift).
|
|
147
|
+
*/
|
|
148
|
+
export function placeBubbles(items, lift = 22) {
|
|
149
|
+
const order = [...items].sort((a, b) => (Number(b.calling) - Number(a.calling)) || (b.shown - a.shown) || (a.key < b.key ? -1 : a.key > b.key ? 1 : 0));
|
|
150
|
+
const taken = [], out = [];
|
|
151
|
+
const hits = (r) => taken.some((t) => r.x < t.x + t.w && t.x < r.x + r.w && r.y < t.y + t.h && t.y < r.y + r.h);
|
|
152
|
+
for (const it of order) {
|
|
153
|
+
for (const dy of [0, -lift]) {
|
|
154
|
+
const r = { x: it.box.x, y: it.box.y + dy, w: it.box.w, h: it.box.h };
|
|
155
|
+
const t = it.tail ? { x: it.tail.x - 1, y: r.y + r.h, w: 9, h: Math.max(0, it.tail.y - (r.y + r.h)) } : null;
|
|
156
|
+
if (hits(r) || (t && hits(t))) continue;
|
|
157
|
+
taken.push(r); if (t) taken.push(t); out.push({ ...it, dy });
|
|
158
|
+
break;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
return out;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/** The lines for a state: the skin's (in the page's language), else the page's own. */
|
|
165
|
+
export function linesFor(state, lang, pack, defaults) {
|
|
166
|
+
const own = pack && pack.bubbles && pack.bubbles[lang] && pack.bubbles[lang][state];
|
|
167
|
+
return own && own.length ? own : defaults[state] || [];
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// ── Furniture ──
|
|
171
|
+
|
|
172
|
+
/** The furniture active now: an item whose `activeWhen` names a state someone shown is in. */
|
|
173
|
+
export function activeFurniture(pack, states) {
|
|
174
|
+
const on = new Set();
|
|
175
|
+
for (const f of (pack && pack.furniture) || []) if (f.activeWhen && f.activeWhen.some((s) => states.has(s))) on.add(f.id);
|
|
176
|
+
return on;
|
|
177
|
+
}
|
|
@@ -0,0 +1,434 @@
|
|
|
1
|
+
// Fleet → Org chart → Office: the fleet as a small pixel office, the third view beside Canvas and List
|
|
2
|
+
// (fleet-canvas.js). The same data as theirs: the structure from /ui/org (buildOrgView), the live state from the app
|
|
3
|
+
// store, the traffic from fleet-traffic.js's store — nothing new is read but the offices people made (/ui/offices).
|
|
4
|
+
//
|
|
5
|
+
// - Two layouts, by skin. Desks (the built-in "pixel" skin, the default): each team is a room, each member a desk with a
|
|
6
|
+
// name tag; General and the instances in no team share the lobby; a grid that wraps at the stage's width
|
|
7
|
+
// (fleet-office-model.js autoLayout). Zones (a skin whose pack maps states to areas — the built-in "studio"): a
|
|
8
|
+
// 1280×720 scene scaled to fit; each instance takes a seat in the area for its state and walks there along the
|
|
9
|
+
// pack's waypoints when its state changes (fleet-office-zones.js).
|
|
10
|
+
// - Speech bubbles: now and then a short line for its state (typed out; at once with reduced motion) — the page's own
|
|
11
|
+
// lines or the skin's, never anything an agent said. Furniture can react: an item with `activeWhen` animates while
|
|
12
|
+
// anyone shown is in that state.
|
|
13
|
+
// - Each occupant is a little pixel person in a pose that loops a few frames: working (typing, the lamp on), idle (tea),
|
|
14
|
+
// paused or stopped (asleep, a closed sign), needs you / crashed / looks stuck (a hand up, a blinking light).
|
|
15
|
+
// - Traffic: each new message, delegation or report is an item carried from the sender's desk to the receiver's along
|
|
16
|
+
// the corridors (the layout's waypoint graph), in its kind's colour and shape; a delegation in progress is a folder on
|
|
17
|
+
// the worker's desk.
|
|
18
|
+
// - Hover (or a first tap) shows what the canvas's card says; a click (or a second tap) opens the chat, as a card does.
|
|
19
|
+
// The desks are a list for a screen reader and the keyboard too (shown while it has focus).
|
|
20
|
+
// - Who is shown: an office switcher (the whole fleet, or an office from the host), and for the whole fleet a team (or
|
|
21
|
+
// the lobby) and pinned people — kept per browser (fleet-office-store.js).
|
|
22
|
+
// - One requestAnimationFrame loop, drawing only when a frame changes; it stops while the tab is hidden and when the view
|
|
23
|
+
// goes. Reduced motion: still poses, nothing in the air. Colours are the dashboard's tokens (dark and light).
|
|
24
|
+
// - Drawing is the skin's (fleet-office-skin.js); a skin pack (fleet-office-pack.js) can replace any part later.
|
|
25
|
+
import { html, useEffect, useLayoutEffect, useMemo, useRef, useState } from "/assets/app-html.js";
|
|
26
|
+
import { appStore } from "/assets/app-store.js";
|
|
27
|
+
import { t, lang, register } from "/assets/app-i18n.js";
|
|
28
|
+
import { identityOf } from "/assets/identity-color.js";
|
|
29
|
+
import { nodeText } from "./fleet-org.js";
|
|
30
|
+
import { hits, needle } from "./fleet-canvas-layout.js";
|
|
31
|
+
import { AUTO_ID, LOBBY, autoLayout, backingScale, coatOf, createFlights, customLayout, deskAt, desksOf, filterOk, lampOf, layoutOk, lightOf, membersOf,
|
|
32
|
+
officePose, officeRooms, signOf, workOn } from "./fleet-office-model.js";
|
|
33
|
+
import { CHAR_DEPTH, DEFAULT_PAL, bubbleBox, modeOf, skinFor } from "./fleet-office-skin.js";
|
|
34
|
+
import { CALLING, activeFurniture, bubbleAt, createWalkers, linesFor, personAt, placeBubbles, zonesLayout } from "./fleet-office-zones.js";
|
|
35
|
+
import { createOfficeStore } from "./fleet-office-store.js";
|
|
36
|
+
import { OfficeEditor } from "./fleet-office-editor.js";
|
|
37
|
+
|
|
38
|
+
register("orgo", {
|
|
39
|
+
en: {
|
|
40
|
+
label: "Office: each team is a room and each instance a desk; General and the instances in no team are in the lobby.",
|
|
41
|
+
office: "Office", allOffice: "Whole fleet", show: "Show", allTeams: "All teams", lobby: "Lobby (General, no team)", lobbyRoom: "Lobby",
|
|
42
|
+
people: "People", peopleN: "People ({0})", findPerson: "Find a person", everyone: "Show everyone", nobody: "No one here matches this filter.",
|
|
43
|
+
unseated: "{0} without a seat", skinUnknown: "Its style “{0}” is not available here: drawn as Desks.", closed: "Closed", desks: "Desks", tapAgain: "Tap again to open the chat",
|
|
44
|
+
capWorking: "Typing away", capIdle: "Tea break", capPaused: "Asleep", capNeeds: "Calling for you", capAway: "Desk empty", capStarting: "Just arriving", capQueued: "Waiting to start",
|
|
45
|
+
styleLabel: "Style", style_pixel: "Desks", style_studio: "Studio", area_desks: "Work", area_lounge: "Lounge", area_alert: "Help desk", area_nap: "Nap corner",
|
|
46
|
+
b_working: "On it.|Typing…|Almost there|In the zone|One more test", b_idle: "Tea time|Ready when you are|Stretching|Anything for me?",
|
|
47
|
+
b_paused: "Zzz…|Back later", b_stopped: "Off duty|Zzz…", b_needs: "Need you!|A question for you|Over here!",
|
|
48
|
+
b_crashed: "Ouch, crashed|Help!", b_stuck: "Hmm, stuck?|Still thinking…",
|
|
49
|
+
},
|
|
50
|
+
"zh-TW": {
|
|
51
|
+
label: "辦公室:每個 team 是一間房間,每個 instance 一張桌子;General 和未加入 team 的 instance 在大廳。",
|
|
52
|
+
office: "辦公室", allOffice: "整個 fleet", show: "顯示", allTeams: "所有 team", lobby: "大廳(General、未加入 team)", lobbyRoom: "大廳",
|
|
53
|
+
people: "成員", peopleN: "成員({0})", findPerson: "尋找成員", everyone: "顯示全部", nobody: "沒有符合這個篩選的成員。",
|
|
54
|
+
unseated: "{0} 位沒有座位", skinUnknown: "這裡沒有它的風格「{0}」:以辦公桌顯示。", closed: "休息中", desks: "桌子", tapAgain: "再點一下開啟聊天",
|
|
55
|
+
capWorking: "埋頭打字中", capIdle: "喝茶休息", capPaused: "睡著了", capNeeds: "在呼叫你", capAway: "空位", capStarting: "剛到座位", capQueued: "等待啟動",
|
|
56
|
+
styleLabel: "風格", style_pixel: "辦公桌", style_studio: "工作室", area_desks: "工作區", area_lounge: "休息區", area_alert: "求救區", area_nap: "午睡角",
|
|
57
|
+
b_working: "處理中|打字中…|快好了|專心中|再測一次", b_idle: "喝茶中|隨時可以開工|伸個懶腰|有事找我嗎?",
|
|
58
|
+
b_paused: "Zzz…|晚點回來", b_stopped: "下班了|Zzz…", b_needs: "需要你!|有個問題|在這裡!",
|
|
59
|
+
b_crashed: "糟糕,當掉了|救命!", b_stuck: "嗯,卡住了?|還在想…",
|
|
60
|
+
},
|
|
61
|
+
});
|
|
62
|
+
const to = (k, ...v) => t(`orgo.${k}`, ...v);
|
|
63
|
+
const CAPTION = { working: "capWorking", idle: "capIdle", paused: "capPaused", needs: "capNeeds", walk: "capIdle", away: "capAway" };
|
|
64
|
+
/** States whose pose is shared but whose words are their own (a queued instance is not asleep). */
|
|
65
|
+
const CAPTION_OF_STATE = { starting: "capStarting", queued: "capQueued" };
|
|
66
|
+
/** What a pose says in words (the tooltip, the desk list); `state`, when its words differ from its pose's. */
|
|
67
|
+
export const caption = (pose, state) => to(CAPTION_OF_STATE[state] || CAPTION[pose] || "capIdle");
|
|
68
|
+
|
|
69
|
+
const reducedMotion = () => typeof matchMedia === "function" && matchMedia("(prefers-reduced-motion: reduce)").matches;
|
|
70
|
+
const now = () => (typeof performance !== "undefined" && typeof performance.now === "function" ? performance.now() : Date.now());
|
|
71
|
+
/** How often the picture can change when nothing is in the air (the poses loop at 4 fps; a bubble types faster). */
|
|
72
|
+
const FPS = 12;
|
|
73
|
+
const STYLES = ["pixel", "studio"];
|
|
74
|
+
const BUBBLE_STATES = ["working", "idle", "paused", "stopped", "needs", "crashed", "stuck"];
|
|
75
|
+
/** The page's own bubble lines, by state, in its language (short, never message content). */
|
|
76
|
+
export const bubbleDefaults = () => Object.fromEntries(BUBBLE_STATES.map((s) => [s, to(`b_${s}`).split("|")]));
|
|
77
|
+
|
|
78
|
+
/** The dashboard's tokens on `el`, for the canvas (the defaults are the dark theme's). */
|
|
79
|
+
function readPal(el) {
|
|
80
|
+
let cs = null;
|
|
81
|
+
try { cs = el && typeof getComputedStyle === "function" ? getComputedStyle(el) : null; } catch { cs = null; }
|
|
82
|
+
const v = (k, d) => { const s = cs && typeof cs.getPropertyValue === "function" ? cs.getPropertyValue(k).trim() : ""; return s || d; };
|
|
83
|
+
const D = DEFAULT_PAL;
|
|
84
|
+
return {
|
|
85
|
+
bg: v("--bg", D.bg), surface: v("--surface", D.surface), surface2: v("--surface-2", D.surface2), border: v("--border", D.border),
|
|
86
|
+
text: v("--text", D.text), text2: v("--text-2", D.text2), text3: v("--text-3", D.text3), ok: v("--ok", D.ok), warn: v("--warn", D.warn),
|
|
87
|
+
danger: v("--danger", D.danger), focus: v("--focus", D.focus),
|
|
88
|
+
fx: { task: v("--fx-task", D.fx.task), send: v("--fx-send", D.fx.send), report: v("--fx-report", D.fx.report), broadcast: v("--fx-broadcast", D.fx.broadcast) },
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/** The team picker's choices: option values are indexes (a team's name can be anything). */
|
|
93
|
+
function teamOptions(view) {
|
|
94
|
+
return [{ value: "all", team: "", label: to("allTeams") }, { value: "lobby", team: LOBBY, label: to("lobby") },
|
|
95
|
+
...view.teams.map((tm, i) => ({ value: `t${i}`, team: tm.name, label: tm.name }))];
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function People({ nodes, pinned, onChange }) {
|
|
99
|
+
const [open, setOpen] = useState(false);
|
|
100
|
+
const [find, setFind] = useState("");
|
|
101
|
+
const box = useRef(null);
|
|
102
|
+
useEffect(() => {
|
|
103
|
+
if (!open) return undefined;
|
|
104
|
+
const away = (e) => { if (box.current && !box.current.contains(e.target)) setOpen(false); };
|
|
105
|
+
const esc = (e) => { if (e.key === "Escape") setOpen(false); };
|
|
106
|
+
document.addEventListener("pointerdown", away);
|
|
107
|
+
document.addEventListener("keydown", esc);
|
|
108
|
+
return () => { document.removeEventListener("pointerdown", away); document.removeEventListener("keydown", esc); };
|
|
109
|
+
}, [open]);
|
|
110
|
+
const q = needle(find), shown = q ? nodes.filter((n) => hits(n, q)) : nodes;
|
|
111
|
+
const toggle = (name) => onChange(pinned.includes(name) ? pinned.filter((x) => x !== name) : [...pinned, name]);
|
|
112
|
+
return html`<div class="oo-people" ref=${box}>
|
|
113
|
+
<button type="button" class=${`btn btn-sm${pinned.length ? " on" : ""}`} aria-expanded=${open ? "true" : "false"} onClick=${() => setOpen((v) => !v)}>
|
|
114
|
+
${pinned.length ? to("peopleN", pinned.length) : to("people")}</button>
|
|
115
|
+
${open ? html`<div class="oo-people-pop" role="group" aria-label=${to("people")}>
|
|
116
|
+
<label class="s-search"><span class="sr-only">${to("findPerson")}</span>
|
|
117
|
+
<input type="search" value=${find} placeholder=${to("findPerson")} autocomplete="off" onInput=${(e) => setFind(e.target.value)} /></label>
|
|
118
|
+
<ul class="oo-people-list">${shown.map((n) => html`<li key=${n.name}><label>
|
|
119
|
+
<input type="checkbox" checked=${pinned.includes(n.name)} onChange=${() => toggle(n.name)} /><span>${nodeText(n).primary}</span></label></li>`)}</ul>
|
|
120
|
+
${pinned.length ? html`<button type="button" class="btn btn-sm" onClick=${() => onChange([])}>${to("everyone")}</button>` : null}
|
|
121
|
+
</div>` : null}
|
|
122
|
+
</div>`;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* The view: `view` buildOrgView's answer; `traffic` the live items (null: Live is off); `q` the Find box's needle;
|
|
127
|
+
* `lease` the tab's (its reads); `onOpen(name)` what a desk opens (the canvas's: the chat).
|
|
128
|
+
*/
|
|
129
|
+
export function Office({ view, traffic, q = "", lease, onOpen }) {
|
|
130
|
+
const store = useMemo(() => createOfficeStore(lease ? { fetch: (path, o) => lease.fetch(path, o) } : {}), [lease]);
|
|
131
|
+
const [stored, setStored] = useState([]);
|
|
132
|
+
const [storedRev, setStoredRev] = useState(null); // the rev the list was read at (a save sends it)
|
|
133
|
+
const [canEdit, setCanEdit] = useState(false); // an admin session (never a guest or the public link)
|
|
134
|
+
const [editing, setEditing] = useState(false);
|
|
135
|
+
const [choice, setChoice] = useState(() => store.choice());
|
|
136
|
+
const [filter, setFilter] = useState(() => store.filter());
|
|
137
|
+
const [style, setStyle] = useState(() => store.skin());
|
|
138
|
+
const [width, setWidth] = useState(960);
|
|
139
|
+
const [stageH, setStageH] = useState(0); // the stage's height (0: not known — fit the width only)
|
|
140
|
+
const [themeRev, setThemeRev] = useState(0);
|
|
141
|
+
const [pal, setPal] = useState(DEFAULT_PAL); // the tokens in force (read once the stage is there)
|
|
142
|
+
const [tip, setTip] = useState(null); // the desk key the tooltip is for
|
|
143
|
+
const stageRef = useRef(null), canvasRef = useRef(null), tipRef = useRef(null), lastType = useRef("mouse");
|
|
144
|
+
const flights = useMemo(() => createFlights(), []);
|
|
145
|
+
const walkers = useMemo(() => createWalkers(), []);
|
|
146
|
+
const seatsRef = useRef(new Map()); // the zones layout's last seats: kept where they can be
|
|
147
|
+
const still = useMemo(reducedMotion, []);
|
|
148
|
+
|
|
149
|
+
// The host's offices: on opening, and again whenever the stream says they changed. Answers can arrive out of order
|
|
150
|
+
// (#2431 review): one with an older rev than the list shown is dropped when it was asked for before that list
|
|
151
|
+
// arrived; one asked for after it is newer whatever its rev (a host whose store was reset starts again at 0).
|
|
152
|
+
// The editor's save and its 409 re-read are stamped when sent (`ask`), never when answered; `adopt` returns the
|
|
153
|
+
// snapshot in force, so a re-read the stream overtook leaves the editor's draft on the newer one.
|
|
154
|
+
const asked = useRef(0), shown = useRef(null); // a counter; shown: { rev, offices, arrived }
|
|
155
|
+
const ask = () => ++asked.current;
|
|
156
|
+
const adopt = (d, askedAt) => {
|
|
157
|
+
const cur = shown.current;
|
|
158
|
+
if (!d) return cur;
|
|
159
|
+
if (cur && d.rev < cur.rev && askedAt <= cur.arrived) return cur;
|
|
160
|
+
shown.current = { rev: d.rev, offices: d.offices, arrived: ++asked.current };
|
|
161
|
+
setStored(d.offices); setStoredRev(d.rev);
|
|
162
|
+
return shown.current;
|
|
163
|
+
};
|
|
164
|
+
useEffect(() => {
|
|
165
|
+
if (!lease) return undefined;
|
|
166
|
+
const read = async () => { const at = ask(); const d = await store.offices(); if (lease.current()) adopt(d, at); };
|
|
167
|
+
void read();
|
|
168
|
+
const sub = appStore.get().streamOn;
|
|
169
|
+
const off = sub ? lease.hold(sub("offices", () => { void read(); })) : null;
|
|
170
|
+
return () => { if (off) off(); };
|
|
171
|
+
}, [lease, store]);
|
|
172
|
+
// Whether this page may edit the offices: an admin session (the host asks again on every save), never a guest page or
|
|
173
|
+
// the public link (whose sessions the host refuses outright).
|
|
174
|
+
useEffect(() => {
|
|
175
|
+
const body = globalThis.document?.body;
|
|
176
|
+
if (!body || body.dataset.tier === "guest" || body.dataset.publicLink === "1") return undefined;
|
|
177
|
+
let live = true;
|
|
178
|
+
const auth = globalThis.AgendAuth;
|
|
179
|
+
if (auth && typeof auth.canWrite === "function") Promise.resolve(auth.canWrite()).then((v) => { if (live) setCanEdit(v === true); }, () => {});
|
|
180
|
+
return () => { live = false; };
|
|
181
|
+
}, []);
|
|
182
|
+
// The stage's width (the grid wraps at it), and the theme (the tokens the canvas reads).
|
|
183
|
+
useEffect(() => {
|
|
184
|
+
const el = stageRef.current;
|
|
185
|
+
const measure = () => {
|
|
186
|
+
const w = el && el.clientWidth, h = el && el.clientHeight;
|
|
187
|
+
if (w > 0) setWidth((cur) => (Math.abs(cur - w) >= 1 ? Math.floor(w) : cur));
|
|
188
|
+
if (h > 0) setStageH((cur) => (Math.abs(cur - h) >= 1 ? Math.floor(h) : cur));
|
|
189
|
+
};
|
|
190
|
+
measure();
|
|
191
|
+
const ro = typeof ResizeObserver === "function" ? new ResizeObserver(measure) : null;
|
|
192
|
+
if (ro && el) ro.observe(el); else globalThis.addEventListener?.("resize", measure);
|
|
193
|
+
const bump = () => setThemeRev((n) => n + 1);
|
|
194
|
+
let mq = null;
|
|
195
|
+
try { mq = typeof matchMedia === "function" ? matchMedia("(prefers-color-scheme: light)") : null; } catch { mq = null; }
|
|
196
|
+
mq?.addEventListener?.("change", bump);
|
|
197
|
+
const mo = typeof MutationObserver === "function" ? new MutationObserver(bump) : null;
|
|
198
|
+
if (mo && globalThis.document?.documentElement) mo.observe(document.documentElement, { attributes: true, attributeFilter: ["data-theme", "class"] });
|
|
199
|
+
return () => { if (ro) ro.disconnect(); else globalThis.removeEventListener?.("resize", measure); mq?.removeEventListener?.("change", bump); mo?.disconnect(); };
|
|
200
|
+
}, []);
|
|
201
|
+
|
|
202
|
+
useLayoutEffect(() => {
|
|
203
|
+
const next = readPal(stageRef.current);
|
|
204
|
+
setPal((cur) => (JSON.stringify(cur) === JSON.stringify(next) ? cur : next));
|
|
205
|
+
}, [themeRev]);
|
|
206
|
+
const mode = modeOf(pal);
|
|
207
|
+
|
|
208
|
+
const custom = choice !== AUTO_ID ? stored.find((o) => o.id === choice) || null : null;
|
|
209
|
+
const skin = skinFor(custom ? custom.skin : STYLES.includes(style) ? style : "pixel");
|
|
210
|
+
const zones = !!(skin.pack && skin.pack.stateZones);
|
|
211
|
+
const L = useMemo(() => {
|
|
212
|
+
const nodes = custom ? membersOf(custom, view) : [...new Map(officeRooms(view, filter).flatMap((r) => r.nodes).map((n) => [n.name, n])).values()];
|
|
213
|
+
if (zones) { const z = zonesLayout(skin.pack, nodes, seatsRef.current); seatsRef.current = z.seats; return z; }
|
|
214
|
+
if (!custom) return autoLayout(officeRooms(view, filter), { width });
|
|
215
|
+
const lay = custom.layout || layoutOk(skin.layout);
|
|
216
|
+
return lay ? customLayout({ ...custom, layout: lay }, nodes) : autoLayout([{ key: custom.id, name: custom.name, lobby: false, nodes }], { width });
|
|
217
|
+
}, [view, filter, custom, width, skin]);
|
|
218
|
+
/** Layout units → CSS px: the zones scene fits the stage (16:9, whole); the desks are drawn at their size. */
|
|
219
|
+
const k = zones ? Math.max(0.2, Math.min(1.25, (width - 2) / L.width, stageH > 0 ? (stageH - 2) / L.height : Infinity)) : 1;
|
|
220
|
+
const looks = useMemo(() => {
|
|
221
|
+
const work = workOn(traffic);
|
|
222
|
+
return new Map(L.rooms.flatMap((r) => r.desks).map((d) => {
|
|
223
|
+
const n = d.node;
|
|
224
|
+
if (!n) return [d.key, { name: "", label: "", state: "missing", pose: "away", light: null, lamp: false, sign: null, coat: "", work: 0, dim: !!q, match: false, free: zones }];
|
|
225
|
+
const match = !!q && hits(n, q);
|
|
226
|
+
return [d.key, { name: n.name, label: nodeText(n).primary, state: n.state, pose: officePose(n.state), light: lightOf(n.state), lamp: lampOf(n.state),
|
|
227
|
+
sign: zones ? null : signOf(n.state), coat: coatOf(n.i, n.i ? identityOf(n.i) : null, mode), work: work.get(n.name) || 0, dim: !!q && !match, match, free: zones }];
|
|
228
|
+
}));
|
|
229
|
+
}, [L, traffic, q, mode, themeRev]);
|
|
230
|
+
useEffect(() => { flights.sync(traffic || [], now(), still); }, [traffic]);
|
|
231
|
+
// Zones: everyone's seat is where they walk to.
|
|
232
|
+
useEffect(() => {
|
|
233
|
+
if (!zones) return;
|
|
234
|
+
walkers.sync(new Map(desksOf(L).map((d) => [d.node.name, d.seat])), now(), L, still);
|
|
235
|
+
loop.current?.wake();
|
|
236
|
+
}, [L]);
|
|
237
|
+
|
|
238
|
+
// ── Drawing: the back and front layers are pictures, remade when the office changes; each frame draws the people
|
|
239
|
+
// and the items in the air between them ──
|
|
240
|
+
const scene = useRef(null);
|
|
241
|
+
const loop = useRef(null);
|
|
242
|
+
useLayoutEffect(() => {
|
|
243
|
+
const cv = canvasRef.current;
|
|
244
|
+
const ctx = cv && typeof cv.getContext === "function" ? cv.getContext("2d") : null;
|
|
245
|
+
if (!ctx) { scene.current = null; return; }
|
|
246
|
+
const W = Math.ceil(L.width * k), H = Math.max(1, Math.ceil(L.height * k));
|
|
247
|
+
// The device's ratio, unless the bitmap would be too big to paint (backingScale): then fewer px per CSS px.
|
|
248
|
+
const dpr = backingScale(W, H, Math.max(1, Math.min(3, globalThis.devicePixelRatio || 1)));
|
|
249
|
+
const BW = Math.max(1, Math.floor(W * dpr)), BH = Math.max(1, Math.floor(H * dpr));
|
|
250
|
+
if (cv.width !== BW || cv.height !== BH) { cv.width = BW; cv.height = BH; }
|
|
251
|
+
cv.style.setProperty("width", `${W}px`);
|
|
252
|
+
cv.style.setProperty("height", `${H}px`);
|
|
253
|
+
const env = { pal, mode, labels: { lobby: to("lobbyRoom"), closed: to("closed"), areas: { desks: to("area_desks"), lounge: to("area_lounge"), alert: to("area_alert"), nap: to("area_nap") } } };
|
|
254
|
+
const scale = dpr * k;
|
|
255
|
+
const layer = (paint) => {
|
|
256
|
+
const c = document.createElement("canvas");
|
|
257
|
+
c.width = BW; c.height = BH;
|
|
258
|
+
const g = c.getContext && c.getContext("2d");
|
|
259
|
+
if (!g) return null;
|
|
260
|
+
g.setTransform(scale, 0, 0, scale, 0, 0);
|
|
261
|
+
g.imageSmoothingEnabled = false;
|
|
262
|
+
paint(g);
|
|
263
|
+
return c;
|
|
264
|
+
};
|
|
265
|
+
const all = L.rooms.flatMap((r) => r.desks);
|
|
266
|
+
const back = layer((g) => {
|
|
267
|
+
g.fillStyle = env.pal.bg; g.fillRect(0, 0, L.width, L.height);
|
|
268
|
+
if (zones) { skin.drawBackground(g, L, env); return; }
|
|
269
|
+
skin.drawFloor(g, L, env);
|
|
270
|
+
for (const r of L.rooms) skin.drawRoom(g, r, env);
|
|
271
|
+
for (const d of all) skin.drawDeskBack(g, d, looks.get(d.key), env);
|
|
272
|
+
});
|
|
273
|
+
const front = zones ? null : layer((g) => { for (const d of all) skin.drawDeskFront(g, d, looks.get(d.key), env); });
|
|
274
|
+
const shown = new Set([...looks.values()].map((l) => l.state));
|
|
275
|
+
scene.current = { ctx, scale, BW, BH, back, front, env, desks: all.filter((d) => d.node), looks, L, skin, zones, dirty: true,
|
|
276
|
+
active: zones ? activeFurniture(skin.pack, shown) : new Set(), lines: bubbleDefaults(), lang: lang() };
|
|
277
|
+
if (still) draw(0); else loop.current?.wake();
|
|
278
|
+
}, [L, looks, skin, pal, k]);
|
|
279
|
+
|
|
280
|
+
/** One frame at `ms` (0: still); whether anything is moving (in the air, or someone walking). */
|
|
281
|
+
function draw(ms) {
|
|
282
|
+
const s = scene.current;
|
|
283
|
+
if (!s) return false;
|
|
284
|
+
const { ctx, scale, BW, BH, env, skin: sk } = s;
|
|
285
|
+
ctx.setTransform(1, 0, 0, 1, 0, 0);
|
|
286
|
+
ctx.clearRect(0, 0, BW, BH);
|
|
287
|
+
if (s.back) ctx.drawImage(s.back, 0, 0);
|
|
288
|
+
ctx.setTransform(scale, 0, 0, scale, 0, 0);
|
|
289
|
+
ctx.imageSmoothingEnabled = false;
|
|
290
|
+
const speakers = [];
|
|
291
|
+
let walking = false;
|
|
292
|
+
if (s.zones) {
|
|
293
|
+
// Furniture under the people, the people (nearest last), furniture over them, then their name tags.
|
|
294
|
+
const t = ms > 0 ? ms : now();
|
|
295
|
+
const people = s.desks.map((d) => {
|
|
296
|
+
const at = walkers.at(d.node.name, t) || { ...d.seat, walking: false };
|
|
297
|
+
if (at.walking) walking = true;
|
|
298
|
+
return { d, at, look: s.looks.get(d.key) };
|
|
299
|
+
}).sort((a, b) => a.at.y - b.at.y);
|
|
300
|
+
const furniture = s.skin.pack.furniture;
|
|
301
|
+
for (const f of furniture) if (f.depth < CHAR_DEPTH) sk.drawFurniture(ctx, f, s.active.has(f.id), ms, env);
|
|
302
|
+
for (const p of people) {
|
|
303
|
+
const box = { ...p.d, x: p.at.x - 44, y: p.at.y - 53, w: 88, h: 53, seat: { x: p.at.x, y: p.at.y } };
|
|
304
|
+
sk.drawCharacter(ctx, box, p.at.walking ? { ...p.look, pose: "walk" } : p.look, ms, env);
|
|
305
|
+
if (!p.at.walking) speakers.push({ look: p.look, x: p.at.x + 10, y: p.at.y - 50 });
|
|
306
|
+
}
|
|
307
|
+
for (const f of furniture) if (f.depth >= CHAR_DEPTH) sk.drawFurniture(ctx, f, s.active.has(f.id), ms, env);
|
|
308
|
+
for (const p of people) sk.drawTag(ctx, p.at.x, p.at.y, p.look, env);
|
|
309
|
+
} else {
|
|
310
|
+
for (const d of s.desks) { const look = s.looks.get(d.key); sk.drawCharacter(ctx, d, look, ms, env); speakers.push({ look, x: d.x + 58, y: d.y + 12 }); }
|
|
311
|
+
ctx.setTransform(1, 0, 0, 1, 0, 0);
|
|
312
|
+
if (s.front) ctx.drawImage(s.front, 0, 0);
|
|
313
|
+
ctx.setTransform(scale, 0, 0, scale, 0, 0);
|
|
314
|
+
}
|
|
315
|
+
const up = still ? [] : flights.frame(ms, s.L);
|
|
316
|
+
for (const it of up) sk.drawProp(ctx, it, ms, env);
|
|
317
|
+
// Speech bubbles: a short line for the state, now and then (typed out; whole at once when still), placed so no two
|
|
318
|
+
// overlap (placeBubbles; #2431).
|
|
319
|
+
const bubbles = [];
|
|
320
|
+
for (const sp of speakers) {
|
|
321
|
+
if (sp.look.dim || sp.look.pose === "away") continue;
|
|
322
|
+
const b = bubbleAt(sp.look.name, sp.look.state, ms, linesFor(sp.look.state, s.lang, sk.pack, s.lines));
|
|
323
|
+
if (b) bubbles.push({ key: sp.look.name, calling: CALLING.has(sp.look.state), shown: b.shown, box: bubbleBox(ctx, sp.x, sp.y, b.text), tail: { x: sp.x, y: sp.y }, sp, b });
|
|
324
|
+
}
|
|
325
|
+
for (const { sp, b, dy } of placeBubbles(bubbles)) sk.drawBubble(ctx, sp.x, sp.y, [...b.text].slice(0, b.shown).join(""), env, -dy);
|
|
326
|
+
return walking || up.length > 0 || flights.size() > 0;
|
|
327
|
+
}
|
|
328
|
+
// The loop: drawing only when the frame (FPS) changed or something is in the air; stopped while the tab is hidden,
|
|
329
|
+
// gone with the view. Office review: while something walks or flies, one requestAnimationFrame per display frame (the
|
|
330
|
+
// motion is smooth); when nothing moves, the poses change at most FPS times a second, so the loop sleeps (a timer)
|
|
331
|
+
// until the next frame's start instead of waking at the display's rate only to find nothing to draw.
|
|
332
|
+
useEffect(() => {
|
|
333
|
+
if (still || typeof requestAnimationFrame !== "function") return undefined;
|
|
334
|
+
let raf = 0, timer = 0, last = -1, air = true, gone = false;
|
|
335
|
+
const tick = () => {
|
|
336
|
+
raf = 0;
|
|
337
|
+
if (gone || globalThis.document?.hidden || !scene.current) return;
|
|
338
|
+
const ms = now(), frame = Math.floor((ms / 1000) * FPS); // the clock the trips started on
|
|
339
|
+
if (scene.current.dirty || frame !== last || air) { scene.current.dirty = false; last = frame; air = draw(ms); }
|
|
340
|
+
if (air) { raf = requestAnimationFrame(tick); return; }
|
|
341
|
+
const wait = Math.max(1, Math.ceil(((frame + 1) * 1000) / FPS - ms)); // until the next frame starts
|
|
342
|
+
timer = setTimeout(() => { timer = 0; if (!gone && !raf) raf = requestAnimationFrame(tick); }, wait);
|
|
343
|
+
};
|
|
344
|
+
const wake = () => {
|
|
345
|
+
air = true;
|
|
346
|
+
if (timer) { clearTimeout(timer); timer = 0; }
|
|
347
|
+
if (!raf && !gone && !globalThis.document?.hidden) raf = requestAnimationFrame(tick);
|
|
348
|
+
};
|
|
349
|
+
loop.current = { wake };
|
|
350
|
+
const vis = () => { if (!globalThis.document?.hidden) wake(); };
|
|
351
|
+
globalThis.document?.addEventListener?.("visibilitychange", vis);
|
|
352
|
+
wake();
|
|
353
|
+
return () => {
|
|
354
|
+
gone = true; loop.current = null;
|
|
355
|
+
if (raf) cancelAnimationFrame(raf);
|
|
356
|
+
if (timer) clearTimeout(timer);
|
|
357
|
+
globalThis.document?.removeEventListener?.("visibilitychange", vis);
|
|
358
|
+
};
|
|
359
|
+
}, []);
|
|
360
|
+
useEffect(() => { loop.current?.wake(); }, [traffic]);
|
|
361
|
+
|
|
362
|
+
// ── The tooltip: what the canvas's card says, above the desk (below it at the top edge) ──
|
|
363
|
+
const tipDesk = tip ? desksOf(L).find((d) => d.key === tip) || null : null;
|
|
364
|
+
useLayoutEffect(() => {
|
|
365
|
+
const el = tipRef.current;
|
|
366
|
+
if (!el || !tipDesk || !el.style) return;
|
|
367
|
+
const dx = tipDesk.x * k, dy = tipDesk.y * k, x = Math.max(4, Math.min(dx, L.width * k - 244)), above = dy > 90;
|
|
368
|
+
el.style.setProperty("transform", above ? `translate(${x}px, ${dy + 6}px) translateY(-100%)` : `translate(${x}px, ${dy + tipDesk.h * k}px)`);
|
|
369
|
+
});
|
|
370
|
+
const deskOf = (e) => {
|
|
371
|
+
const r = canvasRef.current ? canvasRef.current.getBoundingClientRect() : null;
|
|
372
|
+
if (!r) return null;
|
|
373
|
+
const x = (e.clientX - r.left) / k, y = (e.clientY - r.top) / k;
|
|
374
|
+
// The zones scene draws people where they walk: they are hit there, not at the seat they are heading for.
|
|
375
|
+
return zones ? personAt(L.rooms.flatMap((rm) => rm.desks), x, y, (d) => walkers.at(d.node.name, now())) : deskAt(L, x, y);
|
|
376
|
+
};
|
|
377
|
+
const onMove = (e) => { if (e.pointerType === "touch") return; const d = deskOf(e); const k = d ? d.key : null; if (k !== tip) setTip(k); };
|
|
378
|
+
const onClick = (e) => {
|
|
379
|
+
const d = deskOf(e);
|
|
380
|
+
if (!d || !d.node.i) { setTip(null); return; }
|
|
381
|
+
if (lastType.current === "touch" && tip !== d.key) { setTip(d.key); return; } // a first tap shows; a second opens
|
|
382
|
+
onOpen(d.node.name);
|
|
383
|
+
};
|
|
384
|
+
|
|
385
|
+
const auto = !custom;
|
|
386
|
+
const f = filterOk(filter);
|
|
387
|
+
const opts = teamOptions(view);
|
|
388
|
+
const teamValue = (opts.find((o) => o.team === f.team) || opts[0]).value;
|
|
389
|
+
const setF = (next) => { const v = filterOk(next); store.saveFilter(v); setFilter(v); };
|
|
390
|
+
const pick = (id) => { store.saveChoice(id); setChoice(id); setTip(null); };
|
|
391
|
+
const everyone = useMemo(() => { const m = new Map(); for (const n of [...view.top, ...view.teams.flatMap((tm) => tm.nodes), ...view.rest.nodes]) if (!m.has(n.name)) m.set(n.name, n); return [...m.values()]; }, [view]);
|
|
392
|
+
const desks = desksOf(L);
|
|
393
|
+
const tipText = tipDesk ? nodeText(tipDesk.node) : null;
|
|
394
|
+
return html`<div class="oo">
|
|
395
|
+
<div class="oo-bar">
|
|
396
|
+
<label class="oo-pick"><span class="oo-pick-label">${to("office")}</span>
|
|
397
|
+
<select class="tb-select" value=${custom ? custom.id : AUTO_ID} onChange=${(e) => pick(e.target.value)}>
|
|
398
|
+
<option value=${AUTO_ID}>${to("allOffice")}</option>
|
|
399
|
+
${stored.map((o) => html`<option key=${o.id} value=${o.id}>${o.name}</option>`)}
|
|
400
|
+
</select></label>
|
|
401
|
+
${auto ? html`<label class="oo-pick"><span class="oo-pick-label">${to("styleLabel")}</span>
|
|
402
|
+
<select class="tb-select" value=${skin.id} onChange=${(e) => { store.saveSkin(e.target.value); setStyle(e.target.value); setTip(null); }}>
|
|
403
|
+
${STYLES.map((id) => html`<option key=${id} value=${id}>${to(`style_${id}`)}</option>`)}
|
|
404
|
+
</select></label>` : null}
|
|
405
|
+
${auto ? html`<label class="oo-pick"><span class="oo-pick-label">${to("show")}</span>
|
|
406
|
+
<select class="tb-select" value=${teamValue} onChange=${(e) => { const o = opts.find((x) => x.value === e.target.value); setF({ ...f, team: o ? o.team : "" }); }}>
|
|
407
|
+
${opts.map((o) => html`<option key=${o.value} value=${o.value}>${o.label}</option>`)}
|
|
408
|
+
</select></label>
|
|
409
|
+
<${People} nodes=${everyone} pinned=${f.pinned} onChange=${(pinned) => setF({ ...f, pinned })} />` : null}
|
|
410
|
+
${custom && skin.id !== custom.skin ? html`<span class="muted oo-note">${to("skinUnknown", custom.skin)}</span>` : null}
|
|
411
|
+
${L.unseated ? html`<span class="muted oo-note">${to("unseated", L.unseated)}</span>` : null}
|
|
412
|
+
${canEdit && storedRev !== null ? html`<button type="button" class="btn btn-sm oo-edit" onClick=${() => setEditing(true)}>${t("orge.edit")}</button>` : null}
|
|
413
|
+
</div>
|
|
414
|
+
${editing ? html`<${OfficeEditor} offices=${stored} rev=${storedRev} view=${view} store=${store}
|
|
415
|
+
ask=${ask} current=${() => shown.current} onSaved=${adopt} onClose=${() => setEditing(false)} />` : null}
|
|
416
|
+
${desks.length || L.rooms.length ? null : html`<p class="note oo-empty">${to("nobody")} ${auto && (f.team || f.pinned.length) ? html`<button type="button" class="btn btn-sm" onClick=${() => setF({ team: "", pinned: [] })}>${to("everyone")}</button>` : null}</p>`}
|
|
417
|
+
<div class="oo-stage" ref=${stageRef}>
|
|
418
|
+
<canvas ref=${canvasRef} class=${`oo-canvas${tipDesk && tipDesk.node.i ? " hot" : ""}`} role="img" aria-label=${to("label")}
|
|
419
|
+
onPointerMove=${onMove} onPointerLeave=${() => setTip(null)} onPointerDown=${(e) => { lastType.current = e.pointerType || "mouse"; }} onClick=${onClick}></canvas>
|
|
420
|
+
${tipText ? html`<div ref=${tipRef} class="oo-tip" role="tooltip">
|
|
421
|
+
<strong>${tipText.primary}</strong>${tipText.secondary ? html` <span class="muted mono">${tipText.secondary}</span>` : null}
|
|
422
|
+
<div>${tipText.sub}</div><div class="muted">${caption(officePose(tipDesk.node.state), tipDesk.node.state)}</div>
|
|
423
|
+
${tipDesk.node.i && lastType.current === "touch" ? html`<div class="muted">${to("tapAgain")}</div>` : null}
|
|
424
|
+
</div>` : null}
|
|
425
|
+
<ul class="oo-desks" aria-label=${to("desks")}>${desks.map((d) => {
|
|
426
|
+
const nt = nodeText(d.node), cap = caption(officePose(d.node.state), d.node.state);
|
|
427
|
+
return html`<li key=${d.key}>${d.node.i
|
|
428
|
+
? html`<button type="button" class="oo-desk" data-name=${d.node.name} aria-label=${`${nt.full} · ${cap}. ${t("orgc.openChat")}`}
|
|
429
|
+
onFocus=${() => setTip(d.key)} onBlur=${() => setTip(null)} onClick=${() => onOpen(d.node.name)}>${nt.primary}</button>`
|
|
430
|
+
: html`<span class="oo-desk" data-name=${d.node.name}>${nt.full}</span>`}</li>`;
|
|
431
|
+
})}</ul>
|
|
432
|
+
</div>
|
|
433
|
+
</div>`;
|
|
434
|
+
}
|