@songsid/agend 2.3.0-beta.9 → 2.3.0
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.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 +6 -0
- package/dist/channel/adapters/telegram.js +54 -14
- 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 +443 -311
- 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 +39 -1
- package/dist/daemon.js +234 -15
- 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.js +5 -0
- 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-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.js +3 -2
- package/dist/fleet-lock.js.map +1 -1
- package/dist/fleet-manager.d.ts +249 -13
- package/dist/fleet-manager.js +1561 -292
- 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/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 +553 -0
- package/dist/general-knowledge/skills/web-dashboard/SKILL.md +154 -31
- 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 +10 -1
- package/dist/instance-lifecycle.js +86 -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 +428 -20
- package/dist/locale.js.map +1 -1
- 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 +38 -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 +3 -0
- package/dist/outbound-handlers.js +76 -19
- 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 +141 -0
- package/dist/search/search-index.js +440 -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 +75 -0
- package/dist/service-installer.js +206 -0
- 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.d.ts +21 -0
- package/dist/session-oom.js +139 -0
- package/dist/session-oom.js.map +1 -0
- package/dist/settings-api.d.ts +13 -0
- package/dist/settings-api.js +73 -12
- 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 +5 -0
- package/dist/spawn-gate.js +28 -8
- package/dist/spawn-gate.js.map +1 -1
- package/dist/start-attempt.d.ts +1 -0
- package/dist/start-attempt.js +18 -0
- package/dist/start-attempt.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 +147 -0
- package/dist/start-failure.js +281 -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/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 +55 -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 +200 -56
- package/dist/ui/panel-details.js +2 -0
- package/dist/ui/panel-fleet.js +32 -71
- package/dist/ui/panel-needs.js +23 -6
- package/dist/ui/panel-settings.js +158 -32
- 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 +180 -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 +172 -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 +662 -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 +206 -16
- package/dist/ui/shared/view-strings.js +68 -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 +57 -10
- package/dist/web-api.js +1103 -70
- 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/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
|
@@ -9,7 +9,7 @@ import { html, useEffect, useState } from "./app-html.js";
|
|
|
9
9
|
import { createStore, useStore } from "./app-store.js";
|
|
10
10
|
import { onPrefsChanged, prefInForce, savePref } from "./app-prefs.js";
|
|
11
11
|
import { register, t } from "./app-i18n.js";
|
|
12
|
-
import { themesShown, themesStore } from "./app-themes.js";
|
|
12
|
+
import { pageNow, themesShown, themesStore } from "./app-themes.js";
|
|
13
13
|
import { Dialog } from "./ui-dialog.js";
|
|
14
14
|
import { toast } from "./ui-toast.js";
|
|
15
15
|
|
|
@@ -63,16 +63,20 @@ export function contrast(a, b) {
|
|
|
63
63
|
const [l1, l2] = [luminance(a), luminance(b)].sort((x, y) => y - x);
|
|
64
64
|
return (l1 + 0.05) / (l2 + 0.05);
|
|
65
65
|
}
|
|
66
|
-
/**
|
|
67
|
-
|
|
68
|
-
|
|
66
|
+
/**
|
|
67
|
+
* The shade of `hex` name text uses: itself if readable, else the nearest step toward black (light) or white (dark).
|
|
68
|
+
* `page` (#2196): an agent's colours on the page now — names must read on its background and sidebar instead.
|
|
69
|
+
*/
|
|
70
|
+
export function readableShade(hex, mode, page = null) {
|
|
71
|
+
const backgrounds = page ? [page.bg, page.sidebar] : BG[mode], text = page ? page.text : TEXT[mode];
|
|
72
|
+
if (!HEX.test(hex)) return text;
|
|
69
73
|
const toward = mode === "light" ? 0 : 255;
|
|
70
74
|
const ch = [1, 3, 5].map((i) => parseInt(hex.slice(i, i + 2), 16));
|
|
71
75
|
for (let step = 0; step <= 20; step++) {
|
|
72
76
|
const shade = `#${ch.map((c) => Math.round(c + (toward - c) * (step / 20)).toString(16).padStart(2, "0")).join("")}`;
|
|
73
|
-
if (
|
|
77
|
+
if (backgrounds.every((bg) => contrast(shade, bg) >= 4.5)) return shade;
|
|
74
78
|
}
|
|
75
|
-
return
|
|
79
|
+
return text;
|
|
76
80
|
}
|
|
77
81
|
|
|
78
82
|
// ── Resolution ──
|
|
@@ -135,7 +139,7 @@ export function identityOf(it, opts = {}) {
|
|
|
135
139
|
const light = colorValue(c.color, "light"), dark = colorValue(c.color, "dark");
|
|
136
140
|
return {
|
|
137
141
|
color: c.color, source: c.source, show: p.show, showSource: p.source, off: opts.off ?? colorsOff(),
|
|
138
|
-
light, dark, nameLight: readableShade(light, "light"), nameDark: readableShade(dark, "dark"),
|
|
142
|
+
light, dark, nameLight: readableShade(light, "light", pageNow("light")), nameDark: readableShade(dark, "dark", pageNow("dark")),
|
|
139
143
|
};
|
|
140
144
|
}
|
|
141
145
|
/** The row's classes for its identity colour ("" when colours are off or shown nowhere). */
|
|
@@ -192,6 +196,8 @@ function ColorDialog({ name, it, onClose }) {
|
|
|
192
196
|
}, [name]);
|
|
193
197
|
// The preview: the instance as it would be drawn with the choice here.
|
|
194
198
|
const preview = identityOf({ ...(it || {}), color: { ...((it && it.color) || {}), person: color || undefined }, color_show: { ...((it && it.color_show) || {}), person: places || undefined } }, { off: false });
|
|
199
|
+
// #2342: a swatch (or "no pick") is a choice, not text: it starts the hex field's undo history afresh (undoing the
|
|
200
|
+
// text back to a typed colour would leave the box and the chosen colour apart — Save would send the other one).
|
|
195
201
|
const pick = (c) => { setColor(c); setHex(c && HEX.test(c) ? c : ""); setError(""); };
|
|
196
202
|
const togglePlace = (p) => {
|
|
197
203
|
const base = places || preview.show;
|
|
@@ -207,7 +213,8 @@ function ColorDialog({ name, it, onClose }) {
|
|
|
207
213
|
onClose();
|
|
208
214
|
} catch (err) { setError(t("color.failed", (err && err.message) || "network")); setBusy(false); }
|
|
209
215
|
}
|
|
210
|
-
|
|
216
|
+
// The text as typed (#2342: trimming it in the box would rewrite it, losing its undo); spaces around it are ignored.
|
|
217
|
+
const onHex = (v) => { setHex(v); const w = v.trim(), c = /^#[0-9a-fA-F]{6}$/.test(w) ? w.toLowerCase() : null; if (c) { setColor(c); setError(""); } };
|
|
211
218
|
const off = colorsOff();
|
|
212
219
|
return html`<${Dialog} title=${t("color.title", name)} onClose=${onClose} busy=${busy}
|
|
213
220
|
actions=${html`<button type="button" class="btn" disabled=${busy || !loaded} onClick=${() => save({ color: null, color_show: null })}>${t("color.reset")}</button>
|
|
@@ -222,8 +229,8 @@ function ColorDialog({ name, it, onClose }) {
|
|
|
222
229
|
ref=${(el) => { if (el && el.style) { el.style.setProperty("--sw-light", p.light); el.style.setProperty("--sw-dark", p.dark); } }}
|
|
223
230
|
onClick=${() => pick(`preset:${p.id}`)}></button>`)}
|
|
224
231
|
<label class="idc-custom">${t("color.custom")}
|
|
225
|
-
<input type="color" value=${HEX.test(hex) ? hex : "#808080"} onInput=${(e) => onHex(e.currentTarget.value)} aria-label=${t("color.custom")} />
|
|
226
|
-
<input type="text" class="idc-hex" inputmode="text" spellcheck=${false} maxlength="7" placeholder="#rrggbb" value=${hex} onInput=${(e) => onHex(e.currentTarget.value
|
|
232
|
+
<input type="color" value=${HEX.test(hex.trim()) ? hex.trim() : "#808080"} onInput=${(e) => onHex(e.currentTarget.value)} aria-label=${t("color.custom")} />
|
|
233
|
+
<input type="text" class="idc-hex" inputmode="text" spellcheck=${false} maxlength="7" placeholder="#rrggbb" value=${hex} onInput=${(e) => onHex(e.currentTarget.value)} aria-label=${t("color.hex")} /></label>
|
|
227
234
|
<button type="button" class="btn btn-sm" disabled=${!color} onClick=${() => pick(null)}>${t("color.noPick")}</button>
|
|
228
235
|
</fieldset>
|
|
229
236
|
<fieldset class="idc-places"><legend>${t("color.places")}</legend>
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
// Public (/assets/): an anonymous View reader gets the same list, fed by View's roster read instead of status frames.
|
|
10
10
|
import { html, useLayoutEffect, useRef, useState } from "./app-html.js";
|
|
11
11
|
import { t } from "./app-i18n.js";
|
|
12
|
+
import { recordEdit } from "./text-undo.js";
|
|
12
13
|
import { appStore, createStore, guestPage, useStore } from "./app-store.js";
|
|
13
14
|
import { chatPath, detailsPath, logPath, viewPath } from "./app-route.js";
|
|
14
15
|
import { Icon } from "./ui-icons.js";
|
|
@@ -30,14 +31,38 @@ const stored = (k) => { try { return localStorage.getItem(k); } catch { return n
|
|
|
30
31
|
const nameOf = (it) => (it && (it.name != null ? it.name : it.instance_name)) || "";
|
|
31
32
|
|
|
32
33
|
// ── Groups, labels, the filter ──
|
|
34
|
+
/** A team's group key: no tag can be it (a group name is never a control character), so a tag named like a team stays apart. */
|
|
35
|
+
export const TEAM_GROUP = "\u0001team:";
|
|
36
|
+
/**
|
|
37
|
+
* #2172: an agent in a team (fleet.yaml `teams.<name>.members`; `teams` on a status or roster row, in fleet.yaml's
|
|
38
|
+
* order) sits under its first team, whatever its tags say; one in no team under its first tag; a ClassicBot room under
|
|
39
|
+
* "Classic"; the rest under "Other".
|
|
40
|
+
*/
|
|
33
41
|
export function groupOf(it) {
|
|
34
42
|
const tags = Array.isArray(it.tags) ? it.tags : [];
|
|
35
43
|
if (tags.includes("classic")) return "Classic";
|
|
44
|
+
const teams = Array.isArray(it.teams) ? it.teams.filter((x) => typeof x === "string" && x) : [];
|
|
45
|
+
if (teams.length) return TEAM_GROUP + teams[0];
|
|
36
46
|
if (tags.length) return tags[0];
|
|
37
47
|
return "Other";
|
|
38
48
|
}
|
|
39
|
-
/**
|
|
40
|
-
export function
|
|
49
|
+
/** "team" for a team's group, "tag" for a tag's; null for Classic, Other and Favorites. */
|
|
50
|
+
export function groupKind(g) {
|
|
51
|
+
if (typeof g !== "string") return null;
|
|
52
|
+
if (g.startsWith(TEAM_GROUP)) return "team";
|
|
53
|
+
return g === "Other" || g === "Classic" || g.charCodeAt(0) < 32 ? null : "tag";
|
|
54
|
+
}
|
|
55
|
+
/** A group's heading: a team's name; the fallback group ("Other") in the page's language; a tag as it is. */
|
|
56
|
+
export function groupLabel(g) { return g === "Other" ? tn("groupOther") : groupKind(g) === "team" ? g.slice(TEAM_GROUP.length) : g; }
|
|
57
|
+
/** #2172: what a group is, said on its heading — a team's icon, or "#" before a tag — so the grouping can be read. */
|
|
58
|
+
export function GroupMark({ group }) {
|
|
59
|
+
const kind = groupKind(group);
|
|
60
|
+
if (!kind) return null;
|
|
61
|
+
const what = tn(kind === "team" ? "groupTeam" : "groupTag");
|
|
62
|
+
return kind === "team"
|
|
63
|
+
? html`<span class="v-group-kind team" title=${what}><${Icon} name="team" size=${13} /><span class="sr-only">${what}: </span></span>`
|
|
64
|
+
: html`<span class="v-group-kind tag" title=${what} aria-hidden="true">#</span>`;
|
|
65
|
+
}
|
|
41
66
|
/**
|
|
42
67
|
* #2050: the model as a person reads it. The server's resolver writes English for a model it has not resolved yet
|
|
43
68
|
* ("default (detected when it starts)") and for a CLI's own default ("x (default)"); those are shown in the page's language.
|
|
@@ -57,11 +82,12 @@ export function backendLabel(backend) { return BACKEND_LABELS[backend] || backen
|
|
|
57
82
|
export const KNOWN_BACKENDS = new Set(Object.keys(BACKEND_LABELS));
|
|
58
83
|
export const STATUS_KEYS = { running: "statusRunning", paused: "statusPaused", stopped: "statusStopped", crashed: "statusCrashed" };
|
|
59
84
|
|
|
60
|
-
/** The status chips, in order
|
|
61
|
-
export const STATUS_FACETS = ["working", "needs", "idle", "stopped", "crashed"];
|
|
85
|
+
/** The status chips, in order: what the row's dot says, including agents queued for this boot. */
|
|
86
|
+
export const STATUS_FACETS = ["working", "needs", "starting", "idle", "stopped", "crashed"];
|
|
62
87
|
/** Which status chip a row falls under: needs you first, then crashed, not running, working (or stuck), idle. */
|
|
63
88
|
export function statusFacet(it, exec, awaiting) {
|
|
64
89
|
if (awaiting != null) return "needs";
|
|
90
|
+
if (it?.startup_phase === "queued" || it?.startup_phase === "starting") return "starting";
|
|
65
91
|
if (!it || it.status === "crashed") return "crashed";
|
|
66
92
|
if (it.status !== "running") return "stopped";
|
|
67
93
|
return exec === "working" || exec === "stuck" ? "working" : "idle";
|
|
@@ -102,7 +128,8 @@ export function filterSidebar(groupNames, instByGroup, rosterByName, query, labe
|
|
|
102
128
|
}
|
|
103
129
|
export function instanceTooltip(it) {
|
|
104
130
|
const context = it.context_pct != null && Number.isFinite(it.context_pct) ? `${Math.round(Math.min(100, Math.max(0, it.context_pct)))}%` : tn("tooltipUnavailable");
|
|
105
|
-
const status =
|
|
131
|
+
const status = it.startup_phase === "queued" ? t("app.statusQueued") : it.startup_phase === "starting" ? t("app.statusStarting")
|
|
132
|
+
: STATUS_KEYS[it.status] ? tn(STATUS_KEYS[it.status]) : it.status || tn("statusUnknown");
|
|
106
133
|
const line = (key, value) => tn(key, { value });
|
|
107
134
|
const lines = [];
|
|
108
135
|
const name = nameOf(it);
|
|
@@ -124,6 +151,7 @@ const SIDEBAR_ORDER_KEY = "agend_view_sidebar_order"; // View's key since #999
|
|
|
124
151
|
const COLLAPSED_KEY = "agend_instance_collapsed";
|
|
125
152
|
const FILTER_KEY = "agend_instance_filter";
|
|
126
153
|
const FAVORITES_KEY = "agend_favorites"; // #1664
|
|
154
|
+
const SORT_KEY = "agend_nav_sort"; // #2179
|
|
127
155
|
/**
|
|
128
156
|
* A synced sidebar preference, as the JSON its reader parses: the value in force in memory (app-prefs.js — this
|
|
129
157
|
* page's own change, else the host's; a full or blocked storage never brings an old one back), else this browser's copy.
|
|
@@ -154,6 +182,13 @@ function readFilter() {
|
|
|
154
182
|
return { q: f && typeof f.q === "string" ? f.q : "", status: strings(f && f.status).filter((s) => STATUS_FACETS.includes(s)), cli: strings(f && f.cli) };
|
|
155
183
|
} catch { return { q: "", status: [], cli: [] }; }
|
|
156
184
|
}
|
|
185
|
+
/** #2179: how the list sorts — { mode: "custom" (the person's order; the default) | "recent", favs: "fixed" | "recent" }. */
|
|
186
|
+
function readSort() {
|
|
187
|
+
try {
|
|
188
|
+
const s = JSON.parse(inForce("nav.sort", SORT_KEY) || "{}");
|
|
189
|
+
return { mode: s && s.mode === "recent" ? "recent" : "custom", favs: s && s.favs === "recent" ? "recent" : "fixed" };
|
|
190
|
+
} catch { return { mode: "custom", favs: "fixed" }; }
|
|
191
|
+
}
|
|
157
192
|
function readFavorites() {
|
|
158
193
|
try { const a = JSON.parse(inForce("nav.favorites", FAVORITES_KEY) || "[]"); return Array.isArray(a) ? [...new Set(a.filter((x) => typeof x === "string"))] : []; } catch { return []; }
|
|
159
194
|
}
|
|
@@ -167,7 +202,9 @@ export function computeOrder(roster, order) {
|
|
|
167
202
|
if (ia !== ib) return ia - ib;
|
|
168
203
|
if (a === "Other") return 1;
|
|
169
204
|
if (b === "Other") return -1;
|
|
170
|
-
|
|
205
|
+
const ta = groupKind(a) === "team", tb = groupKind(b) === "team";
|
|
206
|
+
if (ta !== tb) return ta ? -1 : 1; // #2172: teams first, then tags
|
|
207
|
+
return groupLabel(a).localeCompare(groupLabel(b));
|
|
171
208
|
});
|
|
172
209
|
const instByGroup = new Map();
|
|
173
210
|
for (const [g, items] of groups) {
|
|
@@ -190,12 +227,18 @@ function moveIn(arr, moving, target, before) {
|
|
|
190
227
|
}
|
|
191
228
|
|
|
192
229
|
/** The list's state for the page's life: this browser's order, folded groups and filter (each also kept in storage). */
|
|
193
|
-
export const navStore = createStore({ order: readOrder(), collapsed: readCollapsed(), filter: readFilter(), favorites: readFavorites() });
|
|
230
|
+
export const navStore = createStore({ order: readOrder(), collapsed: readCollapsed(), filter: readFilter(), favorites: readFavorites(), sort: readSort() });
|
|
194
231
|
/** Read them again from this browser's storage — what a page load does (the store above is made that way). */
|
|
195
232
|
export function loadNavPrefs() {
|
|
196
|
-
navStore.set({ order: readOrder(), collapsed: readCollapsed(), filter: readFilter(), favorites: readFavorites() });
|
|
233
|
+
navStore.set({ order: readOrder(), collapsed: readCollapsed(), filter: readFilter(), favorites: readFavorites(), sort: readSort() });
|
|
197
234
|
namesStore.set({ primary: readNamesPrimary() });
|
|
198
235
|
}
|
|
236
|
+
/** #2179: change how the list sorts ({ mode?, favs? }): the same on every page and device. */
|
|
237
|
+
export function setNavSort(patch) {
|
|
238
|
+
const sort = { ...navStore.get().sort, ...patch };
|
|
239
|
+
navStore.set({ sort });
|
|
240
|
+
savePref("nav.sort", { mode: sort.mode, favs: sort.favs });
|
|
241
|
+
}
|
|
199
242
|
/** Change the filter ({ q?, status?, cli? }): the same on every page, and kept by this browser. */
|
|
200
243
|
export function setNavFilter(patch) {
|
|
201
244
|
const filter = { ...navStore.get().filter, ...patch };
|
|
@@ -204,7 +247,7 @@ export function setNavFilter(patch) {
|
|
|
204
247
|
}
|
|
205
248
|
function setCollapsed(collapsed) { navStore.set({ collapsed }); savePref("nav.collapsed", [...collapsed]); }
|
|
206
249
|
// Another device rearranged, folded or filtered: this page's list follows (its cache is already updated).
|
|
207
|
-
onPrefsChanged((keys) => { if (keys.has("nav.order") || keys.has("nav.collapsed") || keys.has("nav.filter") || keys.has("nav.favorites")) loadNavPrefs(); });
|
|
250
|
+
onPrefsChanged((keys) => { if (keys.has("nav.order") || keys.has("nav.collapsed") || keys.has("nav.filter") || keys.has("nav.favorites") || keys.has("nav.sort")) loadNavPrefs(); });
|
|
208
251
|
|
|
209
252
|
// ── #1664: favorites — starred instances, pinned at the top in the order they were starred (or moved) ──
|
|
210
253
|
export const MAX_FAVORITES = 50;
|
|
@@ -320,12 +363,15 @@ export function avatarOf(it) {
|
|
|
320
363
|
/** The status dot on the avatar: needs you, crashed, paused, stopped, looks stuck, working, idle — colour AND a label. */
|
|
321
364
|
export function dotState(it, exec, awaiting) {
|
|
322
365
|
if (awaiting != null) return "needs";
|
|
366
|
+
if (it?.startup_phase === "queued") return "queued";
|
|
367
|
+
if (it?.startup_phase === "starting") return "starting";
|
|
323
368
|
if (!it || it.status === "crashed") return "crashed";
|
|
324
369
|
if (it.status === "paused") return "paused";
|
|
325
370
|
if (it.status !== "running") return "stopped";
|
|
326
371
|
return exec === "stuck" ? "stuck" : exec === "working" ? "working" : "idle";
|
|
327
372
|
}
|
|
328
373
|
const DOT = {
|
|
374
|
+
queued: ["off", () => t("app.statusQueued")], starting: ["busy", () => t("app.statusStarting")],
|
|
329
375
|
needs: ["warn", () => t("app.needsYou")], crashed: ["bad", () => t("app.statusCrashed")], paused: ["off hollow", () => t("app.statusPaused")],
|
|
330
376
|
stopped: ["off", () => t("app.statusStopped")], stuck: ["warn", () => t("app.statusStuck")], working: ["busy", () => t("app.statusWorking")],
|
|
331
377
|
idle: ["ok", () => t("app.statusIdle")],
|
|
@@ -352,9 +398,74 @@ export function InstanceAvatar({ it, state, cli = false }) {
|
|
|
352
398
|
|
|
353
399
|
// ── The list ──
|
|
354
400
|
|
|
355
|
-
const statusText = (facet) => (facet === "needs" ? t("app.needsYou") : facet === "working" ? t("app.statusWorking") : facet === "idle" ? t("app.statusIdle")
|
|
401
|
+
const statusText = (facet) => (facet === "needs" ? t("app.needsYou") : facet === "starting" ? t("app.statusStarting") : facet === "working" ? t("app.statusWorking") : facet === "idle" ? t("app.statusIdle")
|
|
356
402
|
: facet === "crashed" ? t("app.statusCrashed") : t("app.statusStopped"));
|
|
357
403
|
|
|
404
|
+
// ── #2179: when each instance last replied to a person (the status frame's last_reply) ──
|
|
405
|
+
/** A reply this recent (minutes) is highlighted; the highlight fades to nothing over this time. */
|
|
406
|
+
export const REPLY_FRESH_MIN = 10;
|
|
407
|
+
/** Minutes since the instance last replied to a person, or null (none kept, or no time on this row). */
|
|
408
|
+
export function replyAge(it, now = Date.now()) {
|
|
409
|
+
const at = it && typeof it.last_reply === "string" ? Date.parse(it.last_reply) : NaN;
|
|
410
|
+
return Number.isFinite(at) ? Math.max(0, (now - at) / 60000) : null;
|
|
411
|
+
}
|
|
412
|
+
/** "just now", "2 min ago", "3 h ago", "yesterday", "4 d ago" — in the page's language. */
|
|
413
|
+
export function replyText(mins) {
|
|
414
|
+
if (mins < 1) return tn("replyJustNow");
|
|
415
|
+
if (mins < 60) return tn("replyMinutes", { n: Math.floor(mins) });
|
|
416
|
+
if (mins < 1440) return tn("replyHours", { n: Math.floor(mins / 60) });
|
|
417
|
+
if (mins < 2880) return tn("replyYesterday");
|
|
418
|
+
return tn("replyDays", { n: Math.floor(mins / 1440) });
|
|
419
|
+
}
|
|
420
|
+
/** How strong the highlight is: 1 just after a reply, down to 0 at REPLY_FRESH_MIN minutes. */
|
|
421
|
+
export function replyFade(mins) { return mins == null || mins >= REPLY_FRESH_MIN ? 0 : 1 - mins / REPLY_FRESH_MIN; }
|
|
422
|
+
/** The highlight's strength on the row, through the CSSOM (style-src has no 'unsafe-inline'). */
|
|
423
|
+
function paintReply(el, fade) {
|
|
424
|
+
if (!el) return;
|
|
425
|
+
if (fade > 0) el.style.setProperty("--reply-a", fade.toFixed(3)); else el.style.removeProperty("--reply-a");
|
|
426
|
+
}
|
|
427
|
+
/** Most recent reply first; rows with none keep their order after them (a stable sort). */
|
|
428
|
+
function byRecentReply(age) {
|
|
429
|
+
return (a, b) => {
|
|
430
|
+
const x = age(a), y = age(b);
|
|
431
|
+
if (x == null || y == null) return x == null ? (y == null ? 0 : 1) : -1;
|
|
432
|
+
return x - y;
|
|
433
|
+
};
|
|
434
|
+
}
|
|
435
|
+
/** The group that holds every non-favorite row in "recent" mode: no tag or team can be it, and it is never saved. */
|
|
436
|
+
const RECENT_GROUP = "\u0000recent";
|
|
437
|
+
|
|
438
|
+
/** #2179: the sort button and its menu — how the list sorts, said on the button. */
|
|
439
|
+
function SortMenu({ sort }) {
|
|
440
|
+
const [open, setOpen] = useState(false);
|
|
441
|
+
const box = useRef(null);
|
|
442
|
+
useLayoutEffect(() => {
|
|
443
|
+
if (!open) return undefined;
|
|
444
|
+
const doc = box.current && box.current.ownerDocument;
|
|
445
|
+
if (!doc) return undefined;
|
|
446
|
+
const away = (e) => { if (box.current && !box.current.contains(e.target)) setOpen(false); };
|
|
447
|
+
const esc = (e) => { if (e.key === "Escape") { e.preventDefault(); setOpen(false); } };
|
|
448
|
+
doc.addEventListener("pointerdown", away, true);
|
|
449
|
+
doc.addEventListener("keydown", esc, true);
|
|
450
|
+
return () => { doc.removeEventListener("pointerdown", away, true); doc.removeEventListener("keydown", esc, true); };
|
|
451
|
+
}, [open]);
|
|
452
|
+
const recent = sort.mode === "recent";
|
|
453
|
+
const radio = (group, value, label, hint) => html`<label class="nav-sort-opt"><input type="radio" name=${`nav-sort-${group}`} value=${value}
|
|
454
|
+
checked=${sort[group] === value} onChange=${() => setNavSort({ [group]: value })} /><span>${label}${hint ? html`<span class="nav-sort-hint">${hint}</span>` : null}</span></label>`;
|
|
455
|
+
return html`<div class="nav-sort" ref=${box}>
|
|
456
|
+
<button type="button" class=${`chip-btn nav-sort-btn${recent ? " on" : ""}`} aria-haspopup="dialog" aria-expanded=${open ? "true" : "false"}
|
|
457
|
+
title=${tn("sortTitle")} onClick=${() => setOpen(!open)}><${Icon} name="sort" size=${12} /><span>${tn(recent ? "sortRecent" : "sortCustom")}</span></button>
|
|
458
|
+
${open ? html`<div class="nav-sort-menu" role="dialog" aria-label=${tn("sortTitle")}>
|
|
459
|
+
<fieldset><legend>${tn("sortTitle")}</legend>
|
|
460
|
+
${radio("mode", "custom", tn("sortCustom"), tn("sortCustomHint"))}
|
|
461
|
+
${radio("mode", "recent", tn("sortRecent"), tn("sortRecentHint"))}</fieldset>
|
|
462
|
+
<fieldset><legend>${tn("sortFavs")}</legend>
|
|
463
|
+
${radio("favs", "fixed", tn("sortFavsFixed"), "")}
|
|
464
|
+
${radio("favs", "recent", tn("sortFavsRecent"), "")}</fieldset>
|
|
465
|
+
</div>` : null}
|
|
466
|
+
</div>`;
|
|
467
|
+
}
|
|
468
|
+
|
|
358
469
|
/**
|
|
359
470
|
* The sidebar's instance list. `route`: the page's (which instance is open, and which view a row opens). `items`: the
|
|
360
471
|
* rows (status frames' instances, or View's roster for an anonymous reader); `loaded` false shows a skeleton.
|
|
@@ -368,6 +479,9 @@ export function InstanceNav({ route, items, loaded = true, exec = {}, awaiting =
|
|
|
368
479
|
const filterRef = useRef(null);
|
|
369
480
|
const list = useRef(null);
|
|
370
481
|
const drag = useRef(null);
|
|
482
|
+
// #2179: the reply times and their highlight move on by themselves — the list is drawn again every 30 s.
|
|
483
|
+
const [, setTick] = useState(0);
|
|
484
|
+
useLayoutEffect(() => { const id = setInterval(() => setTick((n) => n + 1), 30000); return () => clearInterval(id); }, []);
|
|
371
485
|
// #1700: which facet groups are open — this device's (screen layout, not a synced preference), read when the list mounts.
|
|
372
486
|
const [facetOpen, setFacetOpen] = useState(readFacetsOpen);
|
|
373
487
|
const toggleFacet = (key) => setFacetOpen((cur) => { const next = { ...cur, [key]: !cur[key] }; writeFacetsOpen(next); return next; });
|
|
@@ -394,9 +508,26 @@ export function InstanceNav({ route, items, loaded = true, exec = {}, awaiting =
|
|
|
394
508
|
// #1920: a guest's list is to pick from — no reordering and no filters (one this device kept would hide rows with no
|
|
395
509
|
// way to clear it).
|
|
396
510
|
const guest = guestPage();
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
511
|
+
// #2179: when each row last replied (a guest's list has no times). "recent" mode (never a guest's): the favorites keep
|
|
512
|
+
// their order or are sorted too, and every other row is one list, the last reply first. "custom" (the default) is the
|
|
513
|
+
// person's own order — a reply never moves a row.
|
|
514
|
+
const now = Date.now();
|
|
515
|
+
// The anonymous View reader (viewOnly) has no reply times (/api/profiles carries none) and no way to change the sort:
|
|
516
|
+
// its list is always Custom, whatever this browser kept from a signed-in page (#2181 review).
|
|
517
|
+
const ageOf = (n) => (guest || viewOnly ? null : replyAge(byName.get(n), now));
|
|
518
|
+
const recent = !guest && !viewOnly && nav.sort.mode === "recent";
|
|
519
|
+
const favsSorted = recent && nav.sort.favs === "recent";
|
|
520
|
+
let shownGroups, shownInsts;
|
|
521
|
+
if (recent) {
|
|
522
|
+
const sortRecent = byRecentReply(ageOf);
|
|
523
|
+
const rest = groupNames.flatMap((g) => instByGroup.get(g) || []).filter((n) => !favSet.has(n)).sort(sortRecent);
|
|
524
|
+
shownGroups = [...(favs.length ? [FAV_GROUP] : []), ...(rest.length ? [RECENT_GROUP] : [])];
|
|
525
|
+
shownInsts = new Map([[FAV_GROUP, favsSorted ? [...favs].sort(sortRecent) : favs], [RECENT_GROUP, rest]]);
|
|
526
|
+
} else {
|
|
527
|
+
shownGroups = favs.length ? [FAV_GROUP, ...groupNames] : groupNames;
|
|
528
|
+
shownInsts = new Map([...instByGroup].map(([g, names]) => [g, names.filter((n) => !favSet.has(n))]));
|
|
529
|
+
if (favs.length) shownInsts.set(FAV_GROUP, favs);
|
|
530
|
+
}
|
|
400
531
|
const f = guest ? { q: "", status: [], cli: [] } : nav.filter;
|
|
401
532
|
const shown = filterSidebar(shownGroups, shownInsts, byName, f.q, backendLabel, { status: f.status, cli: f.cli, statusOf: facetOf });
|
|
402
533
|
// #1936: while a filter is on, the instance you have open stays on top, pinned and marked "Current", whether or not
|
|
@@ -411,7 +542,7 @@ export function InstanceNav({ route, items, loaded = true, exec = {}, awaiting =
|
|
|
411
542
|
const flip = (key, value) => { const cur = new Set(navStore.get().filter[key]); if (cur.has(value)) cur.delete(value); else cur.add(value); setNavFilter({ [key]: [...cur] }); };
|
|
412
543
|
// Drag to reorder: only the full list (a filtered subset would be ambiguous), groups among groups, instances within
|
|
413
544
|
// their group.
|
|
414
|
-
const dragProps = (item) => shown.active || guest ? {} : {
|
|
545
|
+
const dragProps = (item) => shown.active || guest || recent ? {} : { // #2179: not while sorted by reply
|
|
415
546
|
draggable: true,
|
|
416
547
|
onDragStart: (e) => { drag.current = item; e.currentTarget.classList.add("dragging"); try { e.dataTransfer.effectAllowed = "move"; e.dataTransfer.setData("text/plain", item.name); } catch { /* Firefox needs data */ } },
|
|
417
548
|
onDragEnd: (e) => { e.currentTarget.classList.remove("dragging"); drag.current = null; },
|
|
@@ -445,7 +576,7 @@ export function InstanceNav({ route, items, loaded = true, exec = {}, awaiting =
|
|
|
445
576
|
const sel = f[key];
|
|
446
577
|
const summary = !open && sel.length ? (sel.length <= 2 ? sel.map(text).join(", ") : tn("facetSelected", { n: sel.length })) : "";
|
|
447
578
|
const hint = tn(open ? "facetHide" : "facetShow", { group: label });
|
|
448
|
-
return html`<div class=${`nav-facet${open ? " open" : ""}`}>
|
|
579
|
+
return html`<div class=${`nav-facet${open ? " open" : ""}${summary ? " has-sum" : ""}`}>
|
|
449
580
|
<button type="button" class="nav-facet-head" aria-expanded=${open ? "true" : "false"} aria-controls=${`nav-facet-${key}`}
|
|
450
581
|
title=${summary ? `${hint} (${label} · ${summary})` : hint} onClick=${() => toggleFacet(key)}>
|
|
451
582
|
<${Icon} name="chevron" size=${12} cls=${open ? "caret" : "caret folded"} /><span class="nav-facet-label">${label}</span>
|
|
@@ -466,26 +597,37 @@ export function InstanceNav({ route, items, loaded = true, exec = {}, awaiting =
|
|
|
466
597
|
// #1958: its identity colour — the ring, a left bar and/or the name, as resolved; painted through the CSSOM.
|
|
467
598
|
const id = identityOf(it);
|
|
468
599
|
const full = !starred && favs.length >= MAX_FAVORITES;
|
|
469
|
-
|
|
600
|
+
// #2179 (B5, the user's pick): the % (and the ★ beside the row) on the name's line; the time of the last reply at the
|
|
601
|
+
// end of the second line, highlighted for REPLY_FRESH_MIN minutes, fading by itself (a reply under a minute pulses once).
|
|
602
|
+
const mins = ageOf(name), fade = replyFade(mins);
|
|
603
|
+
const two = !!secondary || mins != null;
|
|
604
|
+
return html`<li key=${name} class=${`v-li${two ? " two-line" : ""}`}><a class=${`inst v-inst${on ? " active" : ""}${fade > 0 ? " replied" : ""}${mins != null && mins < 1 ? " just" : ""} ${identityClasses(id)}`}
|
|
605
|
+
ref=${(el) => { paintIdentity(el, id); paintReply(el, fade); }} href=${rowPath(name)} title=${instanceTooltip(it)}
|
|
470
606
|
aria-current=${on ? "page" : undefined} onClick=${onPick} ...${fav ? {} : dragProps({ type: "instance", name, group: g })}>
|
|
471
607
|
<${InstanceAvatar} it=${it} state=${dotState(it, own(exec, name) ? exec[name] : null, waiting)} cli=${true} />
|
|
472
|
-
<span class="inst-text"><span class="inst-name" title=${label}>${label}</span>
|
|
473
|
-
|
|
608
|
+
<span class="inst-text"><span class="inst-line"><span class="inst-name" title=${label}>${label}</span>
|
|
609
|
+
<span class="v-meta">${it.context_pct != null ? `${Math.round(it.context_pct)}%` : ""}</span></span>
|
|
610
|
+
${two ? html`<span class="inst-line inst-line2"><span class="inst-alt" title=${secondary || undefined}>${secondary || ""}</span>
|
|
611
|
+
${mins != null ? html`<span class=${`inst-when${fade > 0 ? " fresh" : ""}`} title=${tn("replyTitle", { time: new Date(Date.parse(it.last_reply)).toLocaleString() })}>${replyText(mins)}</span>` : null}</span>` : null}
|
|
474
612
|
${facet === "needs" ? html`<span class="inst-sub"><span class="badge-await" title=${waiting || t("app.approxNote")}>${t("app.needsYou")}</span></span>` : null}
|
|
475
613
|
<span class="sr-only">${tn("tooltipBackend", { value: backendLabel(it.backend) })}</span></span>
|
|
476
|
-
<span class="v-meta">${it.context_pct != null ? `${Math.round(it.context_pct)}%` : ""}</span>
|
|
477
614
|
</a>${fav && editing && canStar ? html`<button type="button" class="icon-btn inst-move" aria-label=${tn("favUp", { name: label })}
|
|
478
|
-
title=${shown.active ? tn("favFiltered") : tn("favUp", { name: label })} disabled=${shown.active || i === 0} onClick=${() => { if (!shown.active) moveFavorite(name, -1, known); }}><${Icon} name="up" size=${14} /></button><button type="button" class="icon-btn inst-move"
|
|
479
|
-
aria-label=${tn("favDown", { name: label })} title=${shown.active ? tn("favFiltered") : tn("favDown", { name: label })} disabled=${shown.active || i === count - 1}
|
|
480
|
-
onClick=${() => { if (!shown.active) moveFavorite(name, 1, known); }}><${Icon} name="down" size=${14} /></button>` : null}${canStar
|
|
615
|
+
title=${shown.active ? tn("favFiltered") : favsSorted ? tn("favSorted") : tn("favUp", { name: label })} disabled=${shown.active || favsSorted || i === 0} onClick=${() => { if (!shown.active && !favsSorted) moveFavorite(name, -1, known); }}><${Icon} name="up" size=${14} /></button><button type="button" class="icon-btn inst-move"
|
|
616
|
+
aria-label=${tn("favDown", { name: label })} title=${shown.active ? tn("favFiltered") : favsSorted ? tn("favSorted") : tn("favDown", { name: label })} disabled=${shown.active || favsSorted || i === count - 1}
|
|
617
|
+
onClick=${() => { if (!shown.active && !favsSorted) moveFavorite(name, 1, known); }}><${Icon} name="down" size=${14} /></button>` : null}${canStar
|
|
481
618
|
? html`<button type="button" class=${`icon-btn inst-star${starred ? " on" : ""}`} aria-pressed=${starred ? "true" : "false"} disabled=${full}
|
|
482
619
|
aria-label=${tn(starred ? "favRemove" : "favAdd", { name: label })} title=${full ? tn("favFull", { max: MAX_FAVORITES }) : tn(starred ? "favRemove" : "favAdd", { name: label })}
|
|
483
620
|
onClick=${() => toggleFavorite(name, known)}><${Icon} name="star" size=${14} /></button>` : null}</li>`;
|
|
484
621
|
};
|
|
622
|
+
// #2401: an open list of two or more lines leaves room under the count for "shown" and Clear (stacked); otherwise compact.
|
|
623
|
+
const facetSet = f.status.length > 0 || f.cli.length > 0;
|
|
624
|
+
const stacked = (!viewOnly && facetOpen.status) || (facetOpen.cli && backends.length > 1);
|
|
485
625
|
// The list scrolls; the filter under it stays in view (#999), so it sits outside the scrolling section.
|
|
486
626
|
return html`<div class=${`view-roster inst-nav${editing && canStar ? " fav-editing" : ""}`}>
|
|
487
627
|
<div data-trust="navigation" class="side-section" id="instanceList" ref=${list}>
|
|
488
628
|
<div class="inst-head"><h2 class="side-label">${tn("instancesLabel")}</h2>
|
|
629
|
+
${canStar && items.length && !guest ? html`<${SortMenu} sort=${nav.sort} />`
|
|
630
|
+
: recent ? html`<span class="chip-btn nav-sort-btn on nav-sort-static"><${Icon} name="sort" size=${12} /><span>${tn("sortRecent")}</span></span>` : null}
|
|
489
631
|
${canStar && items.length ? html`<button type="button" class="chip-btn fav-edit" aria-pressed=${editing ? "true" : "false"} onClick=${() => setEditing(!editing)}>
|
|
490
632
|
${editing ? tn("favDone") : html`<${Icon} name="star" size=${12} />${tn("favEdit")}`}</button>` : null}</div>
|
|
491
633
|
${!loaded ? html`<${Skeleton} lines=${4} />` : !items.length ? html`<p class="side-empty">${t("app.noInstances")}</p>` : null}
|
|
@@ -493,6 +635,10 @@ export function InstanceNav({ route, items, loaded = true, exec = {}, awaiting =
|
|
|
493
635
|
<div class="v-group-head pinned-head"><span class="grow">${tn("pinnedCurrent")}</span></div>
|
|
494
636
|
<ul class="inst-list">${row(pinned, PINNED_GROUP, 0, 1)}</ul></div>` : null}
|
|
495
637
|
${listed.map(({ group: g, names }) => {
|
|
638
|
+
// #2179: in "recent" mode every other row is one list — open, not dragged, not saved as a group.
|
|
639
|
+
if (g === RECENT_GROUP) return html`<div key=${g} class="v-group v-recent" role="group" aria-label=${tn("recentOthers")}>
|
|
640
|
+
<div class="v-group-head recent-head"><${Icon} name="clock" size=${14} /><span class="grow">${tn("recentOthers")}</span><span class="count">${names.length}</span></div>
|
|
641
|
+
<ul class="inst-list">${names.map((name, i) => row(name, g, i, names.length))}</ul></div>`;
|
|
496
642
|
// #1664: Favorites — always open, not dragged (↑ ↓ in Edit), named in the page's language.
|
|
497
643
|
if (g === FAV_GROUP) return html`<div key=${g} class="v-group v-fav" role="group" aria-label=${tn("favorites")}>
|
|
498
644
|
<div class="v-group-head fav-head"><${Icon} name="star" size=${14} cls="fav-mark" /><span class="grow">${tn("favorites")}</span><span class="count">${names.length}</span></div>
|
|
@@ -501,7 +647,7 @@ export function InstanceNav({ route, items, loaded = true, exec = {}, awaiting =
|
|
|
501
647
|
return html`<div key=${g} class="v-group">
|
|
502
648
|
<button type="button" class="v-group-head" aria-expanded=${folded ? "false" : "true"} onClick=${() => toggle(g)}
|
|
503
649
|
title=${tn(folded ? "expandGroup" : "collapseGroup", { group: groupLabel(g) })} ...${dragProps({ type: "group", name: g })}>
|
|
504
|
-
<${Icon} name="chevron" size=${14} cls=${folded ? "caret folded" : "caret"} /><span class="grow">${groupLabel(g)}</span><span class="count">${names.length}</span></button>
|
|
650
|
+
<${Icon} name="chevron" size=${14} cls=${folded ? "caret folded" : "caret"} /><${GroupMark} group=${g} /><span class="grow">${groupLabel(g)}</span><span class="count">${names.length}</span></button>
|
|
505
651
|
${folded ? null : html`<ul class="inst-list">${names.map((name, i) => row(name, g, i, names.length))}</ul>`}
|
|
506
652
|
</div>`;
|
|
507
653
|
})}
|
|
@@ -511,17 +657,26 @@ export function InstanceNav({ route, items, loaded = true, exec = {}, awaiting =
|
|
|
511
657
|
<label class="v-filter-box"><${Icon} name="search" size=${14} /><span class="sr-only">${tn("filterPh")}</span>
|
|
512
658
|
<input ref=${filterRef} id="filterInput" type="text" autocomplete="off" spellcheck=${false} placeholder=${tn("filterPh")} value=${f.q}
|
|
513
659
|
onInput=${(e) => setNavFilter({ q: e.target.value })}
|
|
514
|
-
onKeyDown=${(e) => { if (e.key === "Escape" && f.q) { e.preventDefault(); e.stopPropagation(); setNavFilter({ q: "" }); } }} />
|
|
660
|
+
onKeyDown=${(e) => { if (e.key === "Escape" && f.q) { e.preventDefault(); e.stopPropagation(); recordEdit(e.target); setNavFilter({ q: "" }); } }} />
|
|
515
661
|
${f.q ? html`<button type="button" class="icon-btn v-filter-clear" aria-label=${tn("filterClear")} title=${tn("filterClear")}
|
|
516
|
-
onClick=${() => { setNavFilter({ q: "" }); filterRef.current && filterRef.current.focus(); }}><${Icon} name="close" size=${14} /></button>` : null}
|
|
662
|
+
onClick=${() => { recordEdit(filterRef.current); setNavFilter({ q: "" }); filterRef.current && filterRef.current.focus(); }}><${Icon} name="close" size=${14} /></button>` : null}
|
|
517
663
|
</label>
|
|
518
|
-
<div class
|
|
664
|
+
<div class=${`nav-facets${stacked ? "" : " compact"}`}>
|
|
519
665
|
<div class="nav-facet-groups">
|
|
520
666
|
${viewOnly ? null : chips("status", STATUS_FACETS, tn("filterStatus"), statusText)}
|
|
521
667
|
${backends.length > 1 || f.cli.length ? chips("cli", backends, tn("filterCli"), backendLabel) : null}
|
|
522
|
-
${f.status.length || f.cli.length ? html`<button type="button" class="chip-btn nav-facet-reset" onClick=${() => setNavFilter({ status: [], cli: [] })}>${tn("filterFacetsReset")}</button>` : null}
|
|
523
668
|
</div>
|
|
524
|
-
|
|
669
|
+
${/* #2401: the count, its "shown" and Clear stack at the row's right end — beside an open list there is room under
|
|
670
|
+
the count, so setting a facet adds no row (a word beside the count, or Clear beside it, wrapped CLI under the
|
|
671
|
+
list). With no such list (.compact) they share one cell of one line: a facet set, Clear takes the count's place
|
|
672
|
+
(the count kept for a screen reader and in Clear's title). Either way the column keeps Clear's width (idle, there
|
|
673
|
+
unseen at no height: .is-idle, a class — never a style attribute, #1300), and stacked the word's too
|
|
674
|
+
(data-reserve, drawn unseen at no height), so the headers' box beside it never narrows. */ ""}
|
|
675
|
+
<div class="nav-facet-end" data-reserve=${tn("filterShown")}>
|
|
676
|
+
<p class=${`note nav-shown${facetSet ? " is-set" : ""}`} aria-live="polite">${shown.shown} / ${shown.total}${shown.active ? html`<span class="nav-shown-word"> ${tn("filterShown")}</span>` : ""}</p>
|
|
677
|
+
<button type="button" class=${`chip-btn nav-facet-reset${facetSet ? "" : " is-idle"}`} title=${facetSet ? `${shown.shown} / ${shown.total} ${tn("filterShown")}` : undefined}
|
|
678
|
+
onClick=${() => setNavFilter({ status: [], cli: [] })}>${tn("filterFacetsReset")}</button>
|
|
679
|
+
</div>
|
|
525
680
|
</div>
|
|
526
681
|
</div>`}
|
|
527
682
|
</div>`;
|
|
@@ -80,6 +80,7 @@ function sync() {
|
|
|
80
80
|
let valid = false;
|
|
81
81
|
try { valid = link.disabled !== true && !!link.sheet && link.sheet.ownerNode === link && link.sheet.disabled !== true && link.sheet.href === new URL(href, location.href).href; } catch { /* unknown stylesheet */ }
|
|
82
82
|
linkState = valid ? "on" : "unknown"; life(linkState, valid ? "loaded" : "load_unverified");
|
|
83
|
+
if (valid) shownHere(sha); // #2198: an agent's trial is on this screen now
|
|
83
84
|
});
|
|
84
85
|
link.addEventListener("error", () => { if (!current()) return; linkState = "unknown"; life("unknown", "load_failed"); });
|
|
85
86
|
life("loading", "loading");
|
|
@@ -189,12 +190,40 @@ async function reread() {
|
|
|
189
190
|
if (body && mine === gen) reconcile(body); // nothing newer happened since this was asked
|
|
190
191
|
} catch { /* the next change tries again */ }
|
|
191
192
|
}
|
|
193
|
+
/**
|
|
194
|
+
* #2198: an agent's trial (General saved it) waits on the host until a person's page shows it. This page shows it once
|
|
195
|
+
* its stylesheet has loaded on screen, and says so: the host starts the keep window then (the dead-man switch, from when
|
|
196
|
+
* a person can see the Keep button). If the host cannot be told, this page reverts it itself at KEEP_SECONDS, as it does
|
|
197
|
+
* for a version it saved.
|
|
198
|
+
*/
|
|
199
|
+
let waitingShown = null; // { id, sha } of an agent's trial this page is showing and has not reported yet
|
|
200
|
+
function shownHere(sha) {
|
|
201
|
+
const w = waitingShown;
|
|
202
|
+
if (!w || w.sha !== sha) return;
|
|
203
|
+
waitingShown = null;
|
|
204
|
+
const trial = ownerCssTrial.get();
|
|
205
|
+
if (!trial || trial.id !== w.id) return;
|
|
206
|
+
post("/ui/owner-css/shown", { trial: w.id }).then((r) => {
|
|
207
|
+
if (ownerCssTrial.get() !== trial) return;
|
|
208
|
+
const ms = r.ok && r.body && Number.isFinite(r.body.remaining_ms) ? r.body.remaining_ms : null;
|
|
209
|
+
if (ms === null) { startOwnerCssTrial(w.id, w.sha, trial.previous, KEEP_SECONDS, true); return; }
|
|
210
|
+
startOwnerCssTrial(w.id, w.sha, trial.previous, ms / 1000 + 1, false);
|
|
211
|
+
}, () => { if (ownerCssTrial.get() === trial) startOwnerCssTrial(w.id, w.sha, trial.previous, KEEP_SECONDS, true); });
|
|
212
|
+
}
|
|
192
213
|
/** Match the host: the version in force, and whether one is on trial (another device's included) — or none any more. */
|
|
193
214
|
function reconcile(state) {
|
|
194
215
|
const pending = state.pending && state.pending.sha === state.active && typeof state.pending.id === "string" ? state.pending : null;
|
|
195
216
|
const trial = ownerCssTrial.get();
|
|
196
|
-
if (!pending) { if (trial) dropTrial(); showActive(state.active, state.effectState); return; }
|
|
217
|
+
if (!pending) { waitingShown = null; if (trial) dropTrial(); showActive(state.active, state.effectState); return; }
|
|
197
218
|
if (trial && trial.id === pending.id) return; // already asking about this one
|
|
219
|
+
if (pending.waiting === true) {
|
|
220
|
+
// Shown here: its stylesheet goes in, and once it has loaded this page reports it (shownHere). Until then the bar
|
|
221
|
+
// waits on the host's word, as for another device's trial.
|
|
222
|
+
waitingShown = { id: pending.id, sha: pending.sha };
|
|
223
|
+
startOwnerCssTrial(pending.id, pending.sha, pending.previous, HOST_TRIAL_SECONDS, false);
|
|
224
|
+
if (linkState === "on" && ownedLink && ownedLink.getAttribute("href") === `/ui/owner-css.css?v=${encodeURIComponent(pending.sha)}`) shownHere(pending.sha);
|
|
225
|
+
return;
|
|
226
|
+
}
|
|
198
227
|
// The host's own remaining time and a second (never this device's clock): it has settled the trial by then.
|
|
199
228
|
const remaining = typeof pending.remaining_ms === "number" && Number.isFinite(pending.remaining_ms) ? pending.remaining_ms : 0;
|
|
200
229
|
startOwnerCssTrial(pending.id, pending.sha, pending.previous, remaining / 1000 + 1, false);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// #1408 step 2: the View panel (/view, /view/<name>) — every agent's live terminal, its
|
|
1
|
+
// #1408 step 2: the View panel (/view, /view/<name>) — every agent's live terminal, its identity card, the AI usage
|
|
2
2
|
// panel, in the app shell. Public (/assets/): under `web.view_access: open` an anonymous reader opens it in the
|
|
3
3
|
// View-only shell, so nothing here needs a session to show, and the one write (Edit profile) asks for one.
|
|
4
4
|
//
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
// Everything recurring is a passive read (#1374) taken through the navigation's lease: the pane every 800 ms and the
|
|
7
7
|
// roster every 5 s while the panel is mounted and the tab is visible, usage every 60 s while its dialog is open. They
|
|
8
8
|
// stop when the panel goes.
|
|
9
|
-
import { html, useEffect,
|
|
9
|
+
import { html, useEffect, useRef, useState } from "./app-html.js";
|
|
10
10
|
import { t } from "./app-i18n.js";
|
|
11
11
|
import { appStore, createStore, useStore } from "./app-store.js";
|
|
12
12
|
import { useLease } from "./app-ctx.js";
|
|
@@ -16,7 +16,7 @@ import { HeaderTools, VIEW_SCALE, textSizeStore } from "./header-tools.js";
|
|
|
16
16
|
import { readStream } from "./read-stream.js";
|
|
17
17
|
// The usage dialog's metric row lives with the dialog now (usage-dialog.js); re-exported for what imported it here.
|
|
18
18
|
export { UsageMetric } from "./usage-dialog.js";
|
|
19
|
-
import {
|
|
19
|
+
import { STATUS_KEYS } from "./instance-nav.js";
|
|
20
20
|
import { viewStore } from "./view-roster-store.js";
|
|
21
21
|
|
|
22
22
|
/** #1580: the app's stream says the fleet is not answering (a restart): the View's polls skip their turn until it is. */
|
|
@@ -189,7 +189,7 @@ export function ViewPanel({ route, navKey }) {
|
|
|
189
189
|
else if (!name) body = html`<div data-trust="panel-content" class="panel-body"><${Skeleton} lines=${6} /></div>`;
|
|
190
190
|
else if (!it) body = html`<div data-trust="panel-content" class="panel-body center"><${Empty} icon="alert" title=${tn("notFound", { name })} hint=${tn("notFoundHint")} /></div>`;
|
|
191
191
|
else body = html`<${Terminal} key=${name} name=${name} lease=${lease} density=${density} />
|
|
192
|
-
<${
|
|
192
|
+
<${ViewIdentity} it=${it} viewOnly=${viewOnly} onEdit=${() => setDialog("edit")} />`;
|
|
193
193
|
|
|
194
194
|
return html`<div data-effect-panel="view" class="panel p-view">
|
|
195
195
|
<${PanelHeader} title=${it ? (it.display_name || it.instance_name) : tn("title")}
|
|
@@ -287,33 +287,18 @@ function glyphRatio(pre) {
|
|
|
287
287
|
return cellRatio || 0.6;
|
|
288
288
|
}
|
|
289
289
|
|
|
290
|
-
/** The
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
const avatar = useRef(null);
|
|
294
|
-
// The avatar is fetched once per instance (cache-busted then), not on every 5-second roster refresh.
|
|
295
|
-
useLayoutEffect(() => {
|
|
296
|
-
if (!avatar.current || !it.has_avatar) return;
|
|
297
|
-
avatar.current.src = `/api/avatar/${encodeURIComponent(it.instance_name)}?t=${Date.now()}`;
|
|
298
|
-
}, [it.instance_name, it.has_avatar]);
|
|
299
|
-
const initial = (it.display_name || it.instance_name || "?").trim().charAt(0) || "?";
|
|
300
|
-
const ctx = it.context_pct != null ? ` · ctx ${Math.round(it.context_pct)}%` : "";
|
|
301
|
-
const toggle = () => { const next = !open; setOpen(next); store("agend_view_card_expanded", next ? "1" : "0"); };
|
|
302
|
-
// #1629 r4 §1: the identity card, above the profile row while it is expanded.
|
|
290
|
+
/** The identity card under the terminal, with Edit profile (or Sign in to edit) below it. Always on: the old one-line
|
|
291
|
+
* strip is gone, and IdentityCard itself is null when the card is empty, so the button lives here rather than inside it. */
|
|
292
|
+
function ViewIdentity({ it, viewOnly, onEdit }) {
|
|
303
293
|
const card = useCard(it.instance_name, it.card_v);
|
|
304
|
-
return html
|
|
305
|
-
|
|
306
|
-
<div class="v-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
294
|
+
return html`<div data-trust="panel-content" class="v-id">
|
|
295
|
+
${card ? html`<${IdentityCard} name=${it.instance_name} card=${card} />` : null}
|
|
296
|
+
<div class="v-id-actions">
|
|
297
|
+
${viewOnly
|
|
298
|
+
? html`<a class="btn btn-sm" href=${signInHref()}><${Icon} name="edit" size=${14} />${tn("signInToEdit")}</a>`
|
|
299
|
+
: html`<button type="button" class="btn btn-sm" onClick=${onEdit}><${Icon} name="edit" size=${14} />${tn("editProfile")}</button>`}
|
|
310
300
|
</div>
|
|
311
|
-
|
|
312
|
-
<${Icon} name="chevron" size=${16} cls=${open ? "caret" : "caret flipped"} /></button>
|
|
313
|
-
${viewOnly
|
|
314
|
-
? html`<a class="btn btn-sm" href=${signInHref()}><${Icon} name="edit" size=${14} />${tn("signInToEdit")}</a>`
|
|
315
|
-
: html`<button type="button" class="btn btn-sm" onClick=${onEdit}><${Icon} name="edit" size=${14} />${tn("editProfile")}</button>`}
|
|
316
|
-
</section>`;
|
|
301
|
+
</div>`;
|
|
317
302
|
}
|
|
318
303
|
|
|
319
304
|
/** Edit profile: display name, role, description, avatar. A write: it needs a session (CSRF via agend-auth.js). */
|