@hybridlabor-api/aos 4.6.5 → 4.7.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/.agents/agents.md +1 -0
- package/.agents/nodes.json +5 -1
- package/.agents/state.schema.json +2 -1
- package/.agents/vendor-manifest.json +50 -0
- package/.claude/agents/architect.md +4 -0
- package/.claude/agents/security-reviewer.md +1 -1
- package/.claude/hooks/memb-inject.mjs +257 -135
- package/.claude/hooks/trail-relay.mjs +18 -0
- package/.claude/settings.json +70 -0
- package/.claude/workflows/startcycle-dispatch.mjs +29 -1
- package/.opencode/agents/architect.md +4 -0
- package/.opencode/plugins/bdb-aos.js +75 -0
- package/THIRD_PARTY_NOTICES.md +48 -0
- package/bin/aos-config.mjs +3 -1
- package/bin/aos-uninstall.mjs +55 -1
- package/docs/handover/agenttrail-for-ao.md +58 -0
- package/docs/handover/deja-for-ao.md +51 -0
- package/installer.js +257 -56
- package/mcp_config.json +4 -0
- package/mcps/mcsc/README.md +24 -0
- package/mcps/mcsc/package-lock.json +1234 -0
- package/mcps/mcsc/package.json +14 -0
- package/mcps/mcsc/packages/core/bin/mcsc.js +162 -0
- package/mcps/mcsc/packages/core/rulebook.default.yaml +49 -0
- package/mcps/mcsc/packages/core/schema/rulebook.schema.json +56 -0
- package/mcps/mcsc/packages/core/src/adapters/agy.js +107 -0
- package/mcps/mcsc/packages/core/src/adapters/codex.js +56 -0
- package/mcps/mcsc/packages/core/src/adapters/native.js +14 -0
- package/mcps/mcsc/packages/core/src/adapters/opencode.js +183 -0
- package/mcps/mcsc/packages/core/src/adapters/types.js +21 -0
- package/mcps/mcsc/packages/core/src/capabilities/buildOfferTable.js +34 -0
- package/mcps/mcsc/packages/core/src/capabilities/registry.js +99 -0
- package/mcps/mcsc/packages/core/src/capabilities/types.js +24 -0
- package/mcps/mcsc/packages/core/src/detect/cache.js +47 -0
- package/mcps/mcsc/packages/core/src/detect/inventory.js +73 -0
- package/mcps/mcsc/packages/core/src/detect/probes.js +158 -0
- package/mcps/mcsc/packages/core/src/detect/types.js +26 -0
- package/mcps/mcsc/packages/core/src/rulebook/load.js +53 -0
- package/mcps/mcsc/packages/core/src/rulebook/resolve.js +227 -0
- package/mcps/mcsc/packages/core/src/rulebook/rulebook.types.js +89 -0
- package/mcps/mcsc/packages/core/src/trail.js +17 -0
- package/mcps/mcsc/packages/mcp/server.js +302 -0
- package/mcps/mcsc/server.js +2 -0
- package/package.json +3 -1
- package/skills/basic/bdbmediastorm/SKILL.md +1 -1
- package/skills/basic/godmode-ui-ux/SKILL.md +2 -2
- package/skills/basic/godmode-ui-ux/references/anti-slop-rules.md +44 -40
- package/skills/basic/godmode-ui-ux/references/taste-archetypes.md +49 -0
- package/skills/basic/startcycle/SKILL.md +7 -0
- package/skills/basic/startcycle-graph/SKILL.md +12 -0
- package/skills/basic/startcycle-graph-user/SKILL.md +3 -0
- package/skills/basic/teamwork-preview/SKILL.md +2 -0
- package/skills/bdbrainstorm/SKILL.md +1 -0
- package/skills/global_config/agenttrail/LICENSE +21 -0
- package/skills/global_config/agenttrail/SKILL.md +48 -0
- package/skills/global_config/agenttrail/bin/agenttrail.mjs +1002 -0
- package/skills/global_config/agenttrail/public/index.html +661 -0
- package/skills/global_config/aos-project-init/SKILL.md +25 -5
- package/skills/global_config/aos-project-init/scripts/aos-project-doctor.mjs +11 -0
- package/skills/global_config/aos-setup/SKILL.md +34 -1
- package/skills/global_config/aos-setup/scripts/aos-doctor.mjs +115 -1
- package/skills/global_config/archify/LICENSE +22 -0
- package/skills/global_config/archify/SKILL.md +140 -0
- package/skills/global_config/archify/THIRD_PARTY_NOTICES.md +69 -0
- package/skills/global_config/archify/assets/JetBrainsMono-OFL.txt +93 -0
- package/skills/global_config/archify/assets/template.html +14937 -0
- package/skills/global_config/archify/bin/archify.mjs +2139 -0
- package/skills/global_config/archify/bin/open-artifact.mjs +86 -0
- package/skills/global_config/archify/bin/preview.mjs +653 -0
- package/skills/global_config/archify/bin/visual-check.mjs +829 -0
- package/skills/global_config/archify/brand-marks/README.md +31 -0
- package/skills/global_config/archify/brand-marks/catalog.json +131 -0
- package/skills/global_config/archify/delta/architecture-delta.mjs +1232 -0
- package/skills/global_config/archify/examples/agent-run.lifecycle.json +60 -0
- package/skills/global_config/archify/examples/agent-tool-call.workflow.json +94 -0
- package/skills/global_config/archify/examples/async-job-roundtrip.sequence.json +61 -0
- package/skills/global_config/archify/examples/brand-aware-delivery.architecture.json +47 -0
- package/skills/global_config/archify/examples/cache-miss-request.sequence.json +82 -0
- package/skills/global_config/archify/examples/checkout-platform.base.architecture.json +31 -0
- package/skills/global_config/archify/examples/checkout-platform.head.architecture.json +31 -0
- package/skills/global_config/archify/examples/deployment-release.lifecycle.json +49 -0
- package/skills/global_config/archify/examples/event-stream.dataflow.json +57 -0
- package/skills/global_config/archify/examples/incident-response.workflow.json +64 -0
- package/skills/global_config/archify/examples/product-analytics.dataflow.json +76 -0
- package/skills/global_config/archify/examples/production-deployment.architecture.json +71 -0
- package/skills/global_config/archify/examples/release-delivery.workflow.json +62 -0
- package/skills/global_config/archify/examples/web-app.architecture.json +46 -0
- package/skills/global_config/archify/migrations/workflow-v2.mjs +279 -0
- package/skills/global_config/archify/package.json +39 -0
- package/skills/global_config/archify/recipes/scenarios.mjs +391 -0
- package/skills/global_config/archify/references/authoring-contract.md +247 -0
- package/skills/global_config/archify/references/brand-marks.md +65 -0
- package/skills/global_config/archify/references/delivery-contract.md +138 -0
- package/skills/global_config/archify/references/viewer-runtime.md +45 -0
- package/skills/global_config/archify/renderers/architecture/grid.mjs +62 -0
- package/skills/global_config/archify/renderers/architecture/render-architecture.mjs +1078 -0
- package/skills/global_config/archify/renderers/dataflow/README.md +104 -0
- package/skills/global_config/archify/renderers/dataflow/render-dataflow.mjs +483 -0
- package/skills/global_config/archify/renderers/lifecycle/README.md +115 -0
- package/skills/global_config/archify/renderers/lifecycle/render-lifecycle.mjs +561 -0
- package/skills/global_config/archify/renderers/sequence/README.md +114 -0
- package/skills/global_config/archify/renderers/sequence/render-sequence.mjs +464 -0
- package/skills/global_config/archify/renderers/shared/brand-marks.mjs +563 -0
- package/skills/global_config/archify/renderers/shared/cli.mjs +218 -0
- package/skills/global_config/archify/renderers/shared/desktop-readability.mjs +26 -0
- package/skills/global_config/archify/renderers/shared/diagnostics.mjs +160 -0
- package/skills/global_config/archify/renderers/shared/engineering-profiles.mjs +157 -0
- package/skills/global_config/archify/renderers/shared/generated-brand-marks.mjs +2003 -0
- package/skills/global_config/archify/renderers/shared/generated-validators.mjs +13 -0
- package/skills/global_config/archify/renderers/shared/geometry.mjs +1423 -0
- package/skills/global_config/archify/renderers/shared/i18n.mjs +595 -0
- package/skills/global_config/archify/renderers/shared/layout-report.mjs +40 -0
- package/skills/global_config/archify/renderers/shared/legend.mjs +217 -0
- package/skills/global_config/archify/renderers/shared/output-path.mjs +340 -0
- package/skills/global_config/archify/renderers/shared/repository-evidence.mjs +239 -0
- package/skills/global_config/archify/renderers/shared/repository-location.mjs +58 -0
- package/skills/global_config/archify/renderers/shared/text-fit.mjs +49 -0
- package/skills/global_config/archify/renderers/shared/utils.mjs +232 -0
- package/skills/global_config/archify/renderers/shared/validator.mjs +86 -0
- package/skills/global_config/archify/renderers/workflow/README.md +223 -0
- package/skills/global_config/archify/renderers/workflow/render-workflow.mjs +35 -0
- package/skills/global_config/archify/renderers/workflow/workflow-compiler.mjs +4400 -0
- package/skills/global_config/archify/renderers/workflow/workflow-migration-geometry.mjs +144 -0
- package/skills/global_config/archify/schemas/README.md +211 -0
- package/skills/global_config/archify/schemas/architecture.schema.json +178 -0
- package/skills/global_config/archify/schemas/common.schema.json +115 -0
- package/skills/global_config/archify/schemas/dataflow.schema.json +243 -0
- package/skills/global_config/archify/schemas/lifecycle.schema.json +266 -0
- package/skills/global_config/archify/schemas/sequence.schema.json +223 -0
- package/skills/global_config/archify/schemas/workflow.schema.json +428 -0
- package/skills/global_config/archify/scripts/check-render-output.mjs +916 -0
- package/skills/global_config/archify/scripts/generate-brand-marks.mjs +141 -0
- package/skills/global_config/archify/scripts/generate-validators.mjs +66 -0
- package/skills/global_config/archify/scripts/render-examples.mjs +26 -0
- package/skills/global_config/archify/skill-release.json +10 -0
- package/skills/global_config/bdb-security-audit/SKILL.md +109 -0
- package/skills/global_config/bdb-security-audit/references/audit-checklists.md +172 -0
- package/skills/global_config/bdb-security-audit/references/skylos-guide.md +139 -0
- package/skills/global_config/bdb-security-audit/references/xalgorix-bridge.md +110 -0
- package/skills/global_config/bdbdesignpro/SKILL.md +4 -4
- package/skills/global_config/bdbdesignpro/references/bdb-motion-tokens.md +52 -31
- package/skills/global_config/bdbresilience/SKILL.md +10 -0
- package/skills/global_config/brag/SKILL.md +179 -0
- package/skills/global_config/brag/assets/music/README.md +20 -0
- package/skills/global_config/brag/assets/music/cues/happy-beats-business-moves-vol-1-by-ende-dot-app.music-cues.json +3846 -0
- package/skills/global_config/brag/assets/music/cues/happy-beats-business-moves-vol-1-by-ende-dot-app.music-cues.md +35 -0
- package/skills/global_config/brag/assets/music/cues/happy-beats-business-moves-vol-10-by-ende-dot-app.music-cues.json +1826 -0
- package/skills/global_config/brag/assets/music/cues/happy-beats-business-moves-vol-10-by-ende-dot-app.music-cues.md +35 -0
- package/skills/global_config/brag/assets/music/cues/happy-beats-business-moves-vol-11-by-ende-dot-app.music-cues.json +2266 -0
- package/skills/global_config/brag/assets/music/cues/happy-beats-business-moves-vol-11-by-ende-dot-app.music-cues.md +35 -0
- package/skills/global_config/brag/assets/music/cues/happy-beats-business-moves-vol-12-by-ende-dot-app.music-cues.json +2806 -0
- package/skills/global_config/brag/assets/music/cues/happy-beats-business-moves-vol-12-by-ende-dot-app.music-cues.md +35 -0
- package/skills/global_config/brag/assets/music/cues/happy-beats-business-moves-vol-9-by-ende-dot-app.music-cues.json +2796 -0
- package/skills/global_config/brag/assets/music/cues/happy-beats-business-moves-vol-9-by-ende-dot-app.music-cues.md +35 -0
- package/skills/global_config/brag/assets/music/happy-beats-business-moves-vol-1-by-ende-dot-app.mp3 +0 -0
- package/skills/global_config/brag/assets/music/happy-beats-business-moves-vol-10-by-ende-dot-app.mp3 +0 -0
- package/skills/global_config/brag/assets/music/happy-beats-business-moves-vol-11-by-ende-dot-app.mp3 +0 -0
- package/skills/global_config/brag/assets/music/happy-beats-business-moves-vol-12-by-ende-dot-app.mp3 +0 -0
- package/skills/global_config/brag/assets/music/happy-beats-business-moves-vol-9-by-ende-dot-app.mp3 +0 -0
- package/skills/global_config/brag/assets/sfx/casino/card-fan-1.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/casino/card-fan-2.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/casino/card-place-1.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/casino/card-place-2.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/casino/card-place-3.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/casino/card-place-4.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/casino/card-shove-1.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/casino/card-shove-2.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/casino/card-shove-3.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/casino/card-shove-4.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/casino/card-shuffle.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/casino/card-slide-1.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/casino/card-slide-2.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/casino/card-slide-3.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/casino/card-slide-4.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/casino/card-slide-5.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/casino/card-slide-6.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/casino/card-slide-7.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/casino/card-slide-8.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/casino/cards-pack-open-1.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/casino/cards-pack-open-2.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/casino/chip-lay-1.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/casino/chip-lay-2.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/casino/chip-lay-3.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/casino/chips-collide-1.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/casino/chips-collide-2.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/casino/chips-collide-3.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/casino/chips-collide-4.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/casino/chips-handle-1.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/casino/chips-handle-2.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/casino/chips-handle-3.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/casino/chips-handle-4.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/casino/chips-handle-6.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/casino/chips-stack-1.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/casino/chips-stack-2.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/casino/chips-stack-3.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/casino/chips-stack-4.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/casino/chips-stack-5.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/casino/chips-stack-6.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/casino/dice-grab-1.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/casino/dice-grab-2.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/casino/dice-shake-1.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/casino/dice-shake-2.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/casino/dice-shake-3.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/casino/dice-throw-1.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/casino/dice-throw-2.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/casino/dice-throw-3.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/casino/die-throw-1.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/casino/die-throw-2.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/casino/die-throw-3.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/casino/die-throw-4.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/footstep_carpet_000.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/footstep_carpet_003.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/footstep_carpet_004.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/footstep_concrete_000.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/footstep_concrete_001.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/footstep_concrete_002.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/footstep_concrete_003.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/footstep_concrete_004.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/footstep_grass_000.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/footstep_grass_001.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/footstep_grass_002.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/footstep_grass_003.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/footstep_grass_004.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/footstep_snow_000.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/footstep_snow_001.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/footstep_snow_002.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/footstep_snow_003.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/footstep_snow_004.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/footstep_wood_000.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/footstep_wood_001.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/footstep_wood_002.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/footstep_wood_003.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/footstep_wood_004.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/impactBell_heavy_000.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/impactBell_heavy_003.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/impactBell_heavy_004.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/impactGeneric_light_000.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/impactGeneric_light_001.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/impactGeneric_light_002.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/impactGeneric_light_003.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/impactGeneric_light_004.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/impactGlass_heavy_002.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/impactGlass_light_001.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/impactGlass_light_002.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/impactGlass_light_003.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/impactGlass_medium_000.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/impactGlass_medium_002.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/impactGlass_medium_004.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/impactMetal_heavy_000.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/impactMetal_heavy_002.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/impactMetal_heavy_004.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/impactMetal_light_002.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/impactMetal_light_003.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/impactMetal_medium_000.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/impactMetal_medium_001.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/impactMetal_medium_002.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/impactMetal_medium_003.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/impactMetal_medium_004.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/impactMining_001.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/impactPlank_medium_000.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/impactPlank_medium_001.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/impactPlank_medium_002.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/impactPlank_medium_003.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/impactPlank_medium_004.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/impactPlate_heavy_000.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/impactPlate_heavy_001.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/impactPlate_heavy_002.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/impactPlate_heavy_003.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/impactPlate_heavy_004.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/impactPlate_light_000.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/impactPlate_light_001.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/impactPlate_light_002.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/impactPlate_light_003.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/impactPlate_light_004.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/impactPlate_medium_000.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/impactPlate_medium_001.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/impactPlate_medium_002.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/impactPlate_medium_003.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/impactPlate_medium_004.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/impactPunch_heavy_000.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/impactPunch_heavy_001.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/impactPunch_heavy_002.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/impactPunch_heavy_003.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/impactPunch_heavy_004.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/impactPunch_medium_000.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/impactPunch_medium_001.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/impactPunch_medium_002.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/impactPunch_medium_003.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/impactPunch_medium_004.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/impactSoft_heavy_000.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/impactSoft_heavy_001.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/impactSoft_heavy_002.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/impactSoft_heavy_003.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/impactSoft_heavy_004.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/impactSoft_medium_000.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/impactSoft_medium_001.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/impactSoft_medium_002.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/impactSoft_medium_003.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/impactSoft_medium_004.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/impactTin_medium_000.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/impactTin_medium_001.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/impactTin_medium_002.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/impactTin_medium_003.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/impactTin_medium_004.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/impactWood_heavy_000.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/impactWood_heavy_001.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/impactWood_heavy_002.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/impactWood_heavy_003.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/impactWood_heavy_004.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/impactWood_light_000.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/impactWood_light_001.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/impactWood_light_002.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/impactWood_light_003.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/impactWood_light_004.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/impactWood_medium_000.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/impactWood_medium_001.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/impactWood_medium_002.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/impactWood_medium_003.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/impact/impactWood_medium_004.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/interface/bong_001.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/interface/click_001.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/interface/click_002.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/interface/click_003.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/interface/click_004.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/interface/click_005.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/interface/drop_001.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/interface/drop_002.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/interface/drop_003.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/interface/error_005.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/interface/error_006.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/interface/glitch_002.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/interface/glitch_004.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/interface/select_008.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/interface/switch_001.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/interface/switch_002.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/interface/switch_004.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/interface/switch_005.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/interface/switch_006.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/interface/switch_007.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/keyboard/keypress-001.wav +0 -0
- package/skills/global_config/brag/assets/sfx/keyboard/keypress-002.wav +0 -0
- package/skills/global_config/brag/assets/sfx/keyboard/keypress-003.wav +0 -0
- package/skills/global_config/brag/assets/sfx/keyboard/keypress-004.wav +0 -0
- package/skills/global_config/brag/assets/sfx/keyboard/keypress-005.wav +0 -0
- package/skills/global_config/brag/assets/sfx/keyboard/keypress-006.wav +0 -0
- package/skills/global_config/brag/assets/sfx/keyboard/keypress-007.wav +0 -0
- package/skills/global_config/brag/assets/sfx/keyboard/keypress-008.wav +0 -0
- package/skills/global_config/brag/assets/sfx/keyboard/keypress-009.wav +0 -0
- package/skills/global_config/brag/assets/sfx/keyboard/keypress-010.wav +0 -0
- package/skills/global_config/brag/assets/sfx/keyboard/keypress-011.wav +0 -0
- package/skills/global_config/brag/assets/sfx/keyboard/keypress-012.wav +0 -0
- package/skills/global_config/brag/assets/sfx/keyboard/keypress-013.wav +0 -0
- package/skills/global_config/brag/assets/sfx/keyboard/keypress-014.wav +0 -0
- package/skills/global_config/brag/assets/sfx/keyboard/keypress-015.wav +0 -0
- package/skills/global_config/brag/assets/sfx/keyboard/keypress-016.wav +0 -0
- package/skills/global_config/brag/assets/sfx/keyboard/keypress-017.wav +0 -0
- package/skills/global_config/brag/assets/sfx/keyboard/keypress-018.wav +0 -0
- package/skills/global_config/brag/assets/sfx/keyboard/keypress-019.wav +0 -0
- package/skills/global_config/brag/assets/sfx/keyboard/keypress-020.wav +0 -0
- package/skills/global_config/brag/assets/sfx/keyboard/keypress-021.wav +0 -0
- package/skills/global_config/brag/assets/sfx/keyboard/keypress-022.wav +0 -0
- package/skills/global_config/brag/assets/sfx/keyboard/keypress-023.wav +0 -0
- package/skills/global_config/brag/assets/sfx/keyboard/keypress-024.wav +0 -0
- package/skills/global_config/brag/assets/sfx/keyboard/keypress-025.wav +0 -0
- package/skills/global_config/brag/assets/sfx/keyboard/keypress-026.wav +0 -0
- package/skills/global_config/brag/assets/sfx/keyboard/keypress-027.wav +0 -0
- package/skills/global_config/brag/assets/sfx/keyboard/keypress-028.wav +0 -0
- package/skills/global_config/brag/assets/sfx/keyboard/keypress-029.wav +0 -0
- package/skills/global_config/brag/assets/sfx/keyboard/keypress-030.wav +0 -0
- package/skills/global_config/brag/assets/sfx/keyboard/keypress-031.wav +0 -0
- package/skills/global_config/brag/assets/sfx/keyboard/keypress-032.wav +0 -0
- package/skills/global_config/brag/assets/sfx/sfx-analysis.json +15584 -0
- package/skills/global_config/brag/assets/sfx/sfx-analysis.md +75 -0
- package/skills/global_config/brag/assets/sfx/ui/click1.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/ui/click2.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/ui/click3.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/ui/click4.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/ui/click5.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/ui/mouseclick1.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/ui/rollover1.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/ui/rollover2.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/ui/rollover4.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/ui/rollover5.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/ui/switch1.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/ui/switch10.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/ui/switch11.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/ui/switch12.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/ui/switch13.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/ui/switch14.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/ui/switch15.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/ui/switch16.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/ui/switch17.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/ui/switch18.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/ui/switch19.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/ui/switch2.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/ui/switch20.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/ui/switch21.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/ui/switch22.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/ui/switch23.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/ui/switch24.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/ui/switch25.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/ui/switch26.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/ui/switch27.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/ui/switch28.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/ui/switch29.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/ui/switch3.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/ui/switch30.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/ui/switch31.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/ui/switch32.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/ui/switch33.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/ui/switch34.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/ui/switch35.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/ui/switch36.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/ui/switch37.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/ui/switch38.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/ui/switch4.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/ui/switch5.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/ui/switch6.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/ui/switch7.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/ui/switch8.ogg +0 -0
- package/skills/global_config/brag/assets/sfx/ui/switch9.ogg +0 -0
- package/skills/global_config/brag/references/audio.md +307 -0
- package/skills/global_config/brag/references/step-1-inspect.md +133 -0
- package/skills/global_config/brag/references/step-2-plan.md +234 -0
- package/skills/global_config/brag/references/step-3-compose.md +210 -0
- package/skills/global_config/brag/references/step-4-deliver.md +168 -0
- package/skills/global_config/brag/references/tones.md +231 -0
- package/skills/global_config/brag/scripts/analyze_music_cues.py +309 -0
- package/skills/global_config/brag/scripts/pyproject.toml +14 -0
- package/skills/global_config/brag/scripts/uv.lock +819 -0
- package/skills/global_config/deja-memory/SKILL.md +42 -0
- package/skills/global_config/editable-design/LICENSE +202 -0
- package/skills/global_config/editable-design/README.md +34 -0
- package/skills/global_config/editable-design/SKILL.md +630 -0
- package/skills/global_config/editable-design/THIRD_PARTY_NOTICES.md +10 -0
- package/skills/global_config/editable-design/agents/openai.yaml +4 -0
- package/skills/global_config/editable-design/assets/editor/layer-editor.css +262 -0
- package/skills/global_config/editable-design/assets/editor/layer-editor.js +1929 -0
- package/skills/global_config/editable-design/assets/poster-starter/README.md +92 -0
- package/skills/global_config/editable-design/assets/poster-starter/assets/.gitkeep +0 -0
- package/skills/global_config/editable-design/assets/poster-starter/index.html +91 -0
- package/skills/global_config/editable-design/assets/poster-starter/poster.json +6 -0
- package/skills/global_config/editable-design/assets/poster-starter/reference/.gitkeep +1 -0
- package/skills/global_config/editable-design/assets/replay/icons/assets.png +0 -0
- package/skills/global_config/editable-design/assets/replay/icons/html.png +0 -0
- package/skills/global_config/editable-design/assets/replay/icons/input.png +0 -0
- package/skills/global_config/editable-design/assets/replay/icons/layers.png +0 -0
- package/skills/global_config/editable-design/assets/replay/icons/plan.png +0 -0
- package/skills/global_config/editable-design/assets/replay/icons/reference.png +0 -0
- package/skills/global_config/editable-design/assets/replay/icons/review.png +0 -0
- package/skills/global_config/editable-design/assets/replay/index.html +59 -0
- package/skills/global_config/editable-design/assets/replay/replay-motion.js +410 -0
- package/skills/global_config/editable-design/assets/replay/replay.css +380 -0
- package/skills/global_config/editable-design/assets/replay/replay.js +374 -0
- package/skills/global_config/editable-design/font-kit/package-lock.json +115 -0
- package/skills/global_config/editable-design/font-kit/package.json +19 -0
- package/skills/global_config/editable-design/pack.sh +20 -0
- package/skills/global_config/editable-design/references/asset-architecture.md +72 -0
- package/skills/global_config/editable-design/references/editor-pitfalls.md +70 -0
- package/skills/global_config/editable-design/references/editor-runtime.md +131 -0
- package/skills/global_config/editable-design/references/font-system.md +89 -0
- package/skills/global_config/editable-design/references/imagery.md +145 -0
- package/skills/global_config/editable-design/references/layout-typography.md +116 -0
- package/skills/global_config/editable-design/references/replay-contract.md +169 -0
- package/skills/global_config/editable-design/scripts/_browser.mjs +247 -0
- package/skills/global_config/editable-design/scripts/bake.mjs +87 -0
- package/skills/global_config/editable-design/scripts/build-replay.mjs +316 -0
- package/skills/global_config/editable-design/scripts/check-contract.mjs +293 -0
- package/skills/global_config/editable-design/scripts/check-fonts.sh +252 -0
- package/skills/global_config/editable-design/scripts/check-poster.sh +70 -0
- package/skills/global_config/editable-design/scripts/doctor.sh +78 -0
- package/skills/global_config/editable-design/scripts/explode.mjs +115 -0
- package/skills/global_config/editable-design/scripts/font-kit.mjs +187 -0
- package/skills/global_config/editable-design/scripts/import-asset.sh +23 -0
- package/skills/global_config/editable-design/scripts/init-poster.sh +64 -0
- package/skills/global_config/editable-design/scripts/install-font-kit.sh +13 -0
- package/skills/global_config/editable-design/scripts/package-lock.json +334 -0
- package/skills/global_config/editable-design/scripts/package.json +14 -0
- package/skills/global_config/editable-design/scripts/remove-chroma-key.py +201 -0
- package/skills/global_config/editable-design/scripts/render-poster.sh +202 -0
- package/skills/global_config/editable-design/scripts/trace.sh +159 -0
- package/skills/global_config/editable-design/scripts/verify-replay.mjs +97 -0
- package/skills/global_config/editable-design/scripts/verify.mjs +782 -0
- package/skills/global_config/editable-design/scripts/wire-editor.mjs +104 -0
- package/skills/global_config/editable-design/tests/fixtures/small-explode/index.html +86 -0
- package/skills/global_config/plan-canvas/SKILL.md +8 -0
|
@@ -0,0 +1,661 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
6
|
+
<meta name="theme-color" content="#000000">
|
|
7
|
+
<meta name="description" content="A live map of your coding agent's plan, progress, and drift.">
|
|
8
|
+
<title>agenttrail</title>
|
|
9
|
+
<link id="fav" rel="icon" href="data:image/svg+xml,%3Csvg%20xmlns=%22http://www.w3.org/2000/svg%22%20viewBox=%220%200%201000%201000%22%3E%3Crect%20width=%221000%22%20height=%221000%22%20rx=%22210%22%20fill=%22%2317171a%22/%3E%3Cg%20fill=%22none%22%20stroke=%22%23fff%22%20stroke-width=%2292%22%20stroke-linecap=%22round%22%20stroke-linejoin=%22round%22%3E%3Cpath%20d=%22M180%20810C278%20617%20363%20405%20445%20180C467%20120%20535%20120%20560%20180C650%20414%20733%20625%20820%20810%22/%3E%3Cpath%20d=%22M180%20810C300%20704%20430%20646%20610%20590%22/%3E%3C/g%3E%3Ccircle%20cx=%22180%22%20cy=%22810%22%20r=%2274%22%20fill=%22%23fff%22/%3E%3Ccircle%20cx=%22610%22%20cy=%22590%22%20r=%2274%22%20fill=%22%23fff%22/%3E%3Ccircle%20cx=%22820%22%20cy=%22810%22%20r=%2274%22%20fill=%22%23fff%22/%3E%3Ccircle%20cx=%22820%22%20cy=%22810%22%20r=%2238%22%20fill=%22%23FFB21A%22/%3E%3C/svg%3E">
|
|
10
|
+
<script>try{document.documentElement.dataset.theme=localStorage.getItem('at-theme')==='light'?'light':'dark'}catch{document.documentElement.dataset.theme='dark'}</script>
|
|
11
|
+
<style>
|
|
12
|
+
:root{color-scheme:dark;--bg:#000;--surface:#181818;--raised:#1f1f1f;--hover:#272727;--line:#313131;--text:#fff;--soft:#d1d1d1;--dim:#9b9b9b;--faint:#686868;--accent:#e9a23b;--accent-bg:#2e220f;--danger:#db6b67;--success:#7aa36a;--complete-text:#70a784;--complete-bg:rgba(63,127,87,.18);--complete-weight:500;--done-agent-opacity:.45;--working-border:#1d2b22;--working-bg:#141f18;--watch-ring:#172015;--region:rgba(24,24,24,.5);--minimap-bg:rgba(24,24,24,.92);--overlay:#181818;--card:#181818;--card-hover:#1f1f1f;--human:#5fb8c9;--knowledge:#a189e0;--knowledge-bg:rgba(122,95,201,.14);--grid:#313131;--shadow-soft:rgba(0,0,0,.24);--shadow-strong:#000;--agent-openai:#e8e8ea;--font:"Geist","Manrope",sans-serif;--mono:"Geist Mono",monospace;--ease:cubic-bezier(.32,.72,0,1);--ease-out:cubic-bezier(.23,1,.32,1)}
|
|
13
|
+
:root[data-theme="light"]{color-scheme:light;--bg:#fff;--surface:#f9f8f7;--raised:#f3f2f0;--hover:#eeecea;--line:rgb(0 0 0/.14);--text:#000;--soft:rgb(0 0 0/.78);--dim:#666;--faint:rgb(0 0 0/.48);--accent:color-mix(in srgb,#e9a23b 64%,#000);--accent-bg:rgb(233 162 59/.16);--success:#3f7f57;--complete-text:#3f7f57;--complete-bg:rgb(63 127 87/.18);--complete-weight:600;--done-agent-opacity:.72;--working-border:rgb(63 127 87/.24);--working-bg:rgb(63 127 87/.10);--watch-ring:rgb(63 127 87/.14);--region:rgb(0 0 0/.015);--minimap-bg:rgb(255 255 255/.94);--overlay:#fbfaf8;--card:#f6f5f2;--card-hover:#efeeea;--human:#2e7f91;--knowledge:#7a5fc9;--knowledge-bg:rgb(122 95 201/.10);--grid:rgb(0 0 0/.16);--shadow-soft:rgb(0 0 0/.10);--shadow-strong:rgb(0 0 0/.14);--agent-openai:#313131}
|
|
14
|
+
*{box-sizing:border-box;margin:0}html{background:var(--bg);scroll-behavior:smooth}body{min-width:320px;min-height:100dvh;background:var(--bg);color:var(--text);font:400 14px/20px var(--font);letter-spacing:-.01em}body:before{content:"";position:fixed;inset:0;pointer-events:none;opacity:.025;z-index:4;background-image:url("data:image/svg+xml,%3Csvg viewBox='0 0 180 180' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.9' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)' opacity='.8'/%3E%3C/svg%3E")}button{font:inherit}button,[role=button]{-webkit-tap-highlight-color:transparent}button:focus-visible,[role=button]:focus-visible{outline:2px solid var(--accent);outline-offset:2px}.skip{position:fixed;top:8px;left:8px;z-index:5;transform:translateY(-160%);padding:8px 12px;color:var(--text);background:var(--raised);border-radius:8px;transition:transform 700ms var(--ease)}.skip:focus{transform:none}
|
|
15
|
+
.app{display:grid;grid-template-rows:auto minmax(0,1fr);height:100dvh;min-height:100dvh}.topbar{position:relative;z-index:3;display:grid;grid-template-columns:minmax(0,1fr) auto;align-items:center;gap:24px;min-height:64px;padding:12px 24px;background:var(--bg);border-bottom:1px solid var(--hover)}.brand,.status,.project,.phase-copy,.task-copy{min-width:0}.brand{display:flex;align-items:center;gap:12px}.brand:before{content:"";display:none;flex:none;width:32px;height:32px;background:url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAxMDAwIDEwMDAiIHJvbGU9ImltZyIgYXJpYS1sYWJlbD0iQWdlbnQgVHJhaWwgbWFyayBmb3IgZGFyayBiYWNrZ3JvdW5kcyI+CiAgPGRlZnM+CiAgICA8bWFzayBpZD0ibm9kZS1ob2xlcyI+CiAgICAgIDxyZWN0IHdpZHRoPSIxMDAwIiBoZWlnaHQ9IjEwMDAiIGZpbGw9IndoaXRlIi8+CiAgICAgIDxjaXJjbGUgY3g9IjE4MCIgY3k9IjgxMCIgcj0iMzQiIGZpbGw9ImJsYWNrIi8+CiAgICAgIDxjaXJjbGUgY3g9IjYxMCIgY3k9IjU5MCIgcj0iMzQiIGZpbGw9ImJsYWNrIi8+CiAgICA8L21hc2s+CiAgPC9kZWZzPgogIDxnIGZpbGw9IiNmZmYiIG1hc2s9InVybCgjbm9kZS1ob2xlcykiPgogICAgPHBhdGggZD0iTTE4MCA4MTBDMjc4IDYxNyAzNjMgNDA1IDQ0NSAxODBDNDY3IDEyMCA1MzUgMTIwIDU2MCAxODBDNjUwIDQxNCA3MzMgNjI1IDgyMCA4MTAiIGZpbGw9Im5vbmUiIHN0cm9rZT0iI2ZmZiIgc3Ryb2tlLXdpZHRoPSI5MiIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIi8+CiAgICA8cGF0aCBkPSJNMTgwIDgxMEMzMDAgNzA0IDQzMCA2NDYgNjEwIDU5MCIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjZmZmIiBzdHJva2Utd2lkdGg9IjkyIiBzdHJva2UtbGluZWNhcD0icm91bmQiLz4KICAgIDxjaXJjbGUgY3g9IjE4MCIgY3k9IjgxMCIgcj0iNzQiLz4KICAgIDxjaXJjbGUgY3g9IjYxMCIgY3k9IjU5MCIgcj0iNzQiLz4KICAgIDxjaXJjbGUgY3g9IjgyMCIgY3k9IjgxMCIgcj0iNzQiLz4KICA8L2c+CiAgPGNpcmNsZSBjeD0iODIwIiBjeT0iODEwIiByPSIzOCIgZmlsbD0iI0ZGQjIxQSIvPgo8L3N2Zz4K") center/contain no-repeat}:root:not([data-theme="light"]) .brand:before{display:block}:root:not([data-theme="light"]) .mark{display:none}.mark{display:block;flex:none;width:32px;height:32px;border-radius:8px;object-fit:cover}.project-name,.file-name{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.project-name{font-weight:600}.file-name{color:var(--dim);font-size:12px;line-height:16px}.switcher{display:flex;gap:2px;padding:4px;background:var(--surface);border:1px solid var(--hover);border-radius:12px}.tab{display:flex;align-items:center;gap:8px;min-height:32px;padding:4px 12px;border:0;border-radius:8px;background:transparent;color:var(--dim);cursor:pointer;font-weight:500;transition:color 700ms var(--ease),background 700ms var(--ease),transform 700ms var(--ease)}.tab:hover,.tab.on{color:var(--text)}.tab.on{background:var(--hover)}.tab:active{transform:scale(.98)}a.tab{text-decoration:none}.tab svg,.icon svg,.chev svg{width:16px;height:16px;fill:none;stroke:currentColor;stroke-width:1.8;stroke-linecap:round;stroke-linejoin:round}.status,#state,.theme-toggle{display:flex;align-items:center}.status{justify-self:end;gap:12px}#state{gap:8px}.theme-toggle{gap:8px;min-height:32px;padding:4px 8px;border:1px solid var(--line);border-radius:8px;background:transparent;color:var(--dim);cursor:pointer;font-size:12px;line-height:16px;font-weight:500;transition:color 250ms var(--ease-out),background 250ms var(--ease-out),border-color 250ms var(--ease-out),transform 250ms var(--ease-out)}.theme-toggle:hover{color:var(--text);background:var(--hover)}.theme-toggle:active{transform:scale(.98)}.theme-track{position:relative;width:32px;height:16px;border-radius:8px;background:var(--hover);box-shadow:inset 0 0 0 1px var(--line);transition:background 250ms var(--ease-out)}.theme-thumb{position:absolute;top:2px;left:2px;width:12px;height:12px;border-radius:4px;background:var(--dim);transition:transform 250ms var(--ease-out),background 250ms var(--ease-out)}:root[data-theme="light"] .theme-track{background:var(--accent-bg)}:root[data-theme="light"] .theme-thumb{transform:translateX(16px);background:var(--accent)}.badge{display:flex;align-items:center;gap:8px;padding:4px 8px;border:1px solid var(--line);border-radius:8px;color:var(--dim);white-space:nowrap;font-size:12px;line-height:16px}.badge.changed{border-color:var(--accent-bg);color:var(--accent);background:var(--accent-bg)}.badge.working{border-color:var(--working-border);color:var(--success);background:var(--working-bg)}.badge.working .dot{background:var(--success);animation:working-pulse 1.6s ease-in-out infinite}@keyframes working-pulse{50%{opacity:.35}}.dot{width:6px;height:6px;border-radius:50%;background:var(--faint)}.changed .dot{background:var(--accent)}.watching{display:flex;align-items:center;gap:8px;color:var(--faint);font-size:12px}.watching:before{content:"";width:6px;height:6px;border-radius:50%;background:var(--success);box-shadow:0 0 0 4px var(--watch-ring)}
|
|
16
|
+
:root:not([data-theme="light"]) .brand:before{background-image:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAIAAAAlC+aJAAAAAXNSR0IArs4c6QAAAERlWElmTU0AKgAAAAgAAYdpAAQAAAABAAAAGgAAAAAAA6ABAAMAAAABAAEAAKACAAQAAAABAAAAQKADAAQAAAABAAAAQAAAAABGUUKwAAAb7klEQVRoBX1aB7Bc1Xnee/eW7ft6VXl6klAXKggBEsWICCRAoEITGjDYcYnHxh7GGduxx45JMhOXEI8nkzjgcTCmyHYAgwtgI7DBYIyNKDIwgBBCSHpPr+zbevtuvu8/dwVMMjlv9+y5p35/Pf8592malmi1Eu9PhmEZZiqTznX3DjWbrTBqRmGgaXoYBeimJRLNVrPVamkoakh6PB4TIbXYFkWRepLHJgbp8oxR7IEvJ2hygoQWRUHv8OIVG/c4TsNt1MIo9H0n8L1WAn9NPalriWb5xMGJd19xaye8Rvn9ULGkrNquM007lSnqug1csgzWfl9CrXrCD2G0x7arpfJ9/VlEN35lhJSFW4oSNDWjKFvsXXnO9ZpmOo1q4Dth4PuBB6rwaTXRHpJfCXBAazUDtzpWPvayW5/m3DJtG0QikUrlrFQRtcJdNL7XpLqySUokTgrMUQaPVA2f4yQQVVnahJ+KQjK/GaItmTSTpr1o7WXZ4mCtOhX6LtCHoUeRQ+5Nyh5d0V8lYkoaFMjxF6uTh9TsMUrbzlqpAvrFz230FDJBEoSUhYFCekwhWj5AqZqWObq21bMNH9CjKJk0ir1zegZPyXb0aboFBriNqu81mqEPfkP9gJuMRwLvRdkwngmSwGo6CLcqx1+oTR/hI75Q+nSmE91P6gir2ZlKzp//J7V7kp6YOs7KaplBYecT0YcdfXPnLN6YzvWEvhcGYLkXkPFeGPqADZUBbBAQlzmISYZjCpoTMdEytOkjz0KliC+T6RBOyIIx1jbw/wP/B6ra/YTe90/AeeJnQcDlh+efNjhvje97nlMB6GYYCFrBHaOPwHHhPQapX5Ku6FclTgwaklboTJSPHzAg0ISWxEQxrhjRSSyKr0qVOBZzSU8YAh3RyX6qjc1t2CyzljJA7axT1vcML6mUxpW2QMVFR4AeCAE2Qqb+3tN6tkgidqVHnJGpGelmHt7SgDyUoVAHTqoOuwjOGA4gsDnGxlYkTikFIRLt78cej2c36EP/nOWd/QuqpTGgh1NuiocRHiuAyGPYH0CPGfHMXNQfBS4oYFpaSzeSVs4YHBqdmWmAlc0QiujCies6qEoSnJBAiO1hCi9rYuiqiJ5A31R10icWDsqAn851gveN6pTn1OBkmiF4j0+E/tJBwAkhRK9AC9S4jpNA+zlxm0WQPrxRK981ZCxddcPYxIxpmCAz8Cu18tulyVfr1TGoGWwFY4QK4MMYWRCz/C9ZnGwRgrgWqOEYLtnqGlgYBK4HNx+4sGOij8I2U4mJQCVTvyBIHjmZkkzcKM+sJQANvM729CdT2VM8FyblQkqm3VHsWdI3fHq+Y5bTmPCcEqjgAIESD+RwTkBmxKah+qh+MXUwUPTQ9aSd7egaXOA1KnDz2JugPEQfK4wyU/Ad2s+PACV+SaxXNRQvMbAa6k/6mk1N1y0jp81btANeCzpD35S0DCNl2rl0bsC0UscP7zt26AniRChAeHES5FKO9f79Jk6d0XSje2hh19AioE/CzgzLdyrliSO1mfEW0cfw0VUhwlyCTDKWBWn8pGArNVMdqbGF4nBPz8K0XUxmC3OiyG82g4g7H5wx9kLXc8qIfDr7Tk1lesuTr0FYIraYALFnkUK7Qn5BF9Gnsp0L1l7cO7JKS9rcWeHjA19LmtmOfsO06tUpGLEAFPQCto2XM3ASJhGLSIDBl8ytKsHZ2bPW9XQvxE7iO6VkJjcMpWy1aFWSU0GjCKs6vlfNdY5i0ylPvoppgZuqg78PUCNrsh7fpp0pzF97sWFnaqXjjeo0Yi/PqfpONXBrvlc3Ea2k805lkityJtIg+No56WkToEiRidXs5E6mY/7oubaVr1fGoOGh7yQzWYScQI+5QAD2EYQfihLIJAhAQ3EEqlydeQuKFINVPye1iktyFOgaWrTBtHL18oRbL1crM06j5rkN14HrhGyx27oQgm6YTnVa2KwwKn6TA5KkhYQpvrOOD82mnSqOjpyNHaBWOe671Sh0YFEGHAZsi1qMP1iCpjW1SGvCJJS7CGBcxZ6llenXG9Xjyi9xPpEFRgC3njRyXXNyHQNgvJku1sonGvWqZWhbLzjzrPWrspn04SPHHt339IFX3kyl0lHgWXbGSuVcFRi3OS7QyRLKRP0JcJJFF91CADRn1mmt0G/UTvh+A3oOvqO31tG3GhSIdiADJNirzhw2rRti1ul0vi/wSsfefER1E45wRXAlU+zvn7c2levCdAxs3HqtOjN/ZPAbt9y8Yf1qOArprFWrtf/4/r3f+u4PkknLsklGZepd2XibwgpORoZzAawuwpUKLgMlazaHhlZ25Ifr1fHAr0MxYKvUw1YraaV6qDMcT0GReHKAD5JYA+ux051O7VjoVds9WZvvnj244EyollMv+XDzbs2pVwf6ind//5urT13quR5OJ2HAr2WaZ29YB97se+LpZFLHJFCqQr6/f2DJwPDy3v5FnV0j2Vwv8Ad+A05FaGiT0WplMp0DvYud+pTv1SLG20QvBERaoWspYj8hXdhPRcc8qmxwV9YNhBx2uisMyqFfQ1MU1BrVMRjG4IKzoH6QKh0AP2HDqf/7rV+75spLXcfBPDoEqeMoB/NIQKAI2C658uPPv/Da0NAp3V0jqVSB0Sd8BjYNrop9s+U4pfGx1yYnDsIrYpQulUODKzN2oVGbCgKQx80ErdzLEy3YABSBKtSC2kDXqEIsM8Nml2ghVKKuJabhv+xsH6LxpGHne05JtOgf4alkz6LXQ5C5bPHopVs+hHCTiI2k67jTpZnhoQEG+c1mKp368LW7jh27v797FFtnZeo4IgtQLlAwAijAFmOwf1ku2zc9fSif7zPNLBQIfHCdcgS9V+gRStGDwdmICkEWGE1LQYotSBSJtUioRRMPq2HowHNBDrJjQOcbjIqVNMH+Rv3mz9y4ccPpQRiCzGqtftWeT37177/ZP9C3bu2poAGsmjU0tG/fgamJcd+tQB/kSOBGOBiEcY75YaMmeJTrM5OpBEIPCMitgfeh7FfYskR5cNQUCUBZBJ/QT+cicTIiDXgjZNi3IRON7lXTfLCICZqBUz5HYoMkG1AIwmB4sHf7tgvDgGd/07Lu+fHdjz7yuGaa37r1ezsv25LJpMGG3t6Os06fd8cPX0mnTMGBOhgzdQxJGR7sOPTrQALJQPzI0ROaQ18sjxjCwIOhR1MXQTDkEAnIdNIsVFG4nIKCw3nPpwHJR3GO51eEltz4yP7t2zYPDw+C06C4Xqv/150/Sdp2KpV6/Y2Djz3+FEiidJvNrVvWG0kEjo5MhTkhWB6F+cFxHq4swK1EA54+8LD9NWBjWE7QM44iJG47hAoydDxDyQQuWIkVuEibmJgBwgbcEUB2/HAuRY/cHdCOwyCfTe+5ejs8Mhhp2fZv9j25/4UDFkDTCbbu/vGDWA4yC4Jg5YqFK5aPuE4dqoGpZDZBhjCJEwpJPGdCr3A9IblwTW4oAInmpPgLmXFzBWTJBLcotEjqJK1CMYlGDNyWCQoSGIt8QqDZdN6ZK1csAT5MCCHc9oO7KWJJqXRm/5/fev2Nw4YBnwHyrEsvPRfmz1UgW5mWM78XaYMMQBeZU7zK52B1ASC7vlIcjBKfA74xQI8QjtpmNm0X0nbeStqMuIUlokiKbsUAlIU8oQet0Jkbr78KtgEZmpb55+dffPyJp8l+EWkuXaxXvIcefAweDLyC1m3efNbgQDecGBlJlQCxmJCqSAkLVawhKjgcWY5hDjjPzuqDyVGAm09m013FXH8+3ZOxO1JWDpslKIGNBpFX90pTlaMTpUOOX03qRoI7q9pf6PN4ZmslwMs1q5efd+4GOFVUwhH+4I69jVo9lctCAKiACwcfHnjg0Y99/Kp0yga3Bgf7PnT+6T+84/502obwJakDGjHh264EYtonK/nDJimfLLSSi+Zu6inMzae7Dc0ArUEI5XOQgzFm0i5m+2d1L5rdtyxl52rOFBwcdomYAqCj18BR1P3i335644b1YRhCSQ4fPnLzF77uhxAL9ZMOMd2ptaJjx46tWbt88ZIF4DM242w2fd9/PyxAgYaWA2i08dgCCZQLCL+VwyENsfaLZNkBLG1prleuO5N1d7rhlhxvhh+3VGtMVOpjpfKRqZl3Aq8+0rtiw7Kr5vavFIFCjpwaHzjN2XNn7dxxMQogJ2kY9/z4gRPHxrEBC30tXL7FShI19977IAaC/jAIT1u3cuXKRZ6HjUzYyZxKIhgxP09tTEKSVEpROuBRPhSU7nhlz6/h4wf4IE6q+0EDBXxAWMOdrruTM7Xjk6W3fbe2bPbGlfM2QcHUeGhk5Dau3HXp4OAA+arrpdLMnXf9VLdM8AZJApKk8n2WZf72iWfefPNtmnKrlUrZO3ZeSFOOQSuosDGhBz2o7jQ5rsUzDZIoEuQQqxPJ030cXMIGdCaAG4485EGIGkfIYO56VcctN7yZamOyVDk2UBw5dXQTrBbTpYzM3FmLP7znasazsnk99uun3j00gchJvDU1DFqAMj7YIcfHJ+6/75cwZTTAlLduPa+/vweiI6PbTBWsJ2kQxDFJsXzinlRfEgILHpDFhFwQCVrpuPEoZc6rOEEvhst27PnFTG8mVcha+a7U4LZLL7j2+m3wnjBfwPzG129vVqxZ3XPTVtrF5t8MLMNOagYOqdD7/r7u6enStss2IzgF6M6uzldffWP/8wdMwwCaOAkyQSkVxIMCTZkf6QbGo4ANO4nL3maLt9hkFT0GDBTMpeSbCezSMETEvowp6dSQQU3p6Zodqe5klKg2pi/dea5yS/Cef3h6/3N/eCGfLiZb+uziyGB++O3SwbJbdpz62ees+8pXPr1kyYKZmbL4AK4GXFdcecnee35GfHEiSoVP4MfoUUY72tBk62aHXSikipZOb2mA/fC+xIxvC9sCYh76SkCHvbUYBeEmFTQosVIaIAamUq2Xl64cWX/mKt9TLz60e+78meM4tmljIT9wkwljwJ6V8JLz1wzfcce33z06dsst31mwYOSa3ZcRDew0CM5Yv3r5ysUv7v8L6GeVMJiNUoAuKHmgPziXMVNzc0N9qS7AcyPPgdoHLgiQsI4uHSSAE8xBAxjBHFIC3xPgKaWmFLSpw4vAd9V2XbMFEbLnurDLN9449OjDTximDn43onqhUJi3ZHT12uXLVy1YffpSRKZX7PrEwYOHwaxypfK5z/01rhQAOJ1J79y55bk/7jctBvZMkgu/iZ6PPPY35xeGTynOhQiqQa2Oa7LI97HltSKoEHhME+StAy/YUYzRqzgVJwNUJJq8peNsOB/gOBUEQ7N7Nl90tsQO0ETjJ3t/fuzoeCGXX7xk4cXbLth04VkLF42kMylOnND3Pfb7d945ns/narXqH599AWzCapgN4t+2bfO/fPt7lQqPSkKBaAuVXi3YwvF8Xc+iOem+klet+DU38l0IGDsWgGAnxtGGnks4TxowNXnP6TEfZYIzEbgPtaJ4sAR/avXaJZfv6untBvvhVcbHTtx9531Qp0986roLLjy72FGAZiJUw6kSKHA2WLVq2brTVjzzzPMIqq+44hLPw31RALWBTxiZN2fTBRvvvut+NBFynMhSJENLbuxf3m91nGhMV4OGE3lgfMjr1VA8blMr5kdhlOQ7lQc/6ktTBufxJAYAM+AHtoAq9E+lrV88esf8hXOhTIiTH/nV44fffve6G64ECN/3lVfg0c3khtCoNwAlCCPIYXR0zqrVyz77mS9dtGXThRedj+gDsd2vH/3dju03Qg+xJBiEzoIefG1t6FsyK9UzodCHnoebHkGPwFUYHzE8FFshBSJuykDER72Rc42wX6bUYQwJrdFobN123sJFo9BjjAn84Oxz1m/Zej7fzrkuSLZTiHC048fGnvvD/meefG7/n15CwH39R69ed8bq6amZj1x3050/2us6PmhAN1zenbVh3fIVi1568VVsdm309BVLO+fMTnUL+jo0h7yH5oAAbKBUmwh6phwwtQLMBuUCH1EaoEN/wG6qDt0sBEH24ACWMExt957LhUrR00QLC0MrwD+cXxAdPP6bp+7b+/OnfvvsieMTcNW2ZWOxL372FjtlwQnj8NJd6N73698deefoEI/LYTab2bXrkj//6UXTBB6qDqy2aGUX54ZLbqUWNLyQmgO9Dwgd52sSEMlhEL6d4atYAuJBGatyomUJSeZkAVzxXGfNuhVnnLUWjKfMFB2tlmWb0K+HHnh41yU37N7x8Z/e9VB1ulbMFYv5joydyaZynfmOlJHOpfNd+a60lTlxfPIXDz2Kgz94Bzzbt2/t7eumadIrUovm5/pxTqr44L1Cz7MfcAI9OmCbApn4GoO9pzJq8KsIFny/BkBJ3STvoUaUCJkf08BruKjWqO3es922bWgL5UbZ8Aj28kuv/MNXv/3YI7/DvpvNZM20YSQNERtliy+uaEgtPWMCxmSZ1k/3PnjDR3fLdUs0On/kry4478F7fr50cG7BSGPelGaUvAqcvQ/GU21gtcL4WH9AAzmbXDB7c0duuL9nyVDfiq4OXIPqDacEmYj+YEVMJQg0DR4jm0t//gt/87GP7YHfxFGJ6HUNlvr92370yY98/tWXX8+mcyk7JcbOo5LYEkVHWaovFZIshIN49+jRjeecMW/eHFzmgG0rly3Ll5rB0arN+CCqB04jdGC1VB4abqw8ZL/6kBuJZCE7y/Orvl+H38ule2YPrgIxUdOt1k4QfjuJ57HvvOvfrt2z8+FfPT5+/MScOcNgLCb70hf+8R+/diusJpPO4DRD3EwKMXOsR+0TzQYXZXns6M16vZ4rZDdfdH6rcTxx9JGevq4zd+x45S+vvPTiAS+BQMrDbgXo+IjSU/UxluaLZalCnBzRKGLpKs4AdWdieubQ+InXwdrl8y9Zs2yXZWUY5xFLC1fMO3ZuPX/Thmt3f2rH5Tduueja22+7C7z/8pf+6bu33p5OZ0wTDoQ9OTXpIqcj/qeF+sB6od80NkUD+uIN6dLlSxPemP7U1uRzV7Z+s9GsPXvFTdeFVqvR3mtFeeS8S6uNoWNqLoQvwmmE0BJFN3AkcL0KTjYzlXcmpt4qZGatW35NLtMDybBjMzzt9FPfOngY7M/kMhi8d++DwPvygdegzdB1mgyZrRjcljLVBZZP6ZN5KICjeIfSDCGmfDa7as3axORTWuOlRD6TaE213t47ODIn1ZEBAbJhYe1YeTC8DTqWrTC2xf9B4euM0JcjgePxHAMypqZKh7RWctWiy7OZLjAS4w+/fWRwaGB0/txqte767pq1K6BhlXIN19iYS7kF4QsYBAIUaOCOdRfogRu61Z0uLOwYXNw53GvmpsfGEh1LE2Yx4TYSYULrWV+eLk3OlOAueWMhOT2mmo0CBh0MUamjeIQR5zKDjPhZpWoYVkhPHDk828r1dI6cmH4Dh4EjR45edfXlO3ZebFvW9su3/N2Xb7r//l/e/r0fIZqnm5FJea+nJlK/sgbmBXmo788WF3YM9Wc6TC0ZNIOqU5uYmth61fXJ/g0JPa8tvCkxd/e//vN3nnzsSWwIGAKscY6C4o3IQQEVzIlkLjsIsRKAIACJKDHmwVje8AS5dBf+FWSqfGhycur3v392/fo11+zefuqqZffee//nb/4qdgNeYXAMElkSU8IyEtkBHCnDXNg1a1YWrxGimt9wAhfePUxEr7z62huvvzm6+kJr3hVj7tCt37j1P79zG447vNMEAkqSmiPHKzKe1oWcc6v5E1p/zyrsFwDBUAcBD15/a7AuC2/BTSNlmzlcExUL/QcOPozTPeIcBMzDw0PYd8fHJizTxgtmBkzcpvFDjyu/UsYqgqNg5xZ2Dpu4YUYEL3eRyi1Cy53AK9cqqVyqs6ezXC5PT5YydhqvVTiN0hbgF1EANElBAmnM+UU3SGAABJBZSFQAhhMsMheaecwx8EJzonSQ8VZCL89U8M9VeNNC3ktnGYCxHKDKqgD+FezsvCK1tO47Ll4syq4k94lxOInNOAjCygxePoQIOhBFqnkEZszvNvST6LkMlgDfsD8y+gdqVFHR+EtW4mCGiVDQQm5tuWw3PBLuWtAfifVqJcwpK8qvVPGZraI51lC+1ydu/htTexNAtIAyFqNTBxIcl/HSWvFVGE9wbY6jGx9UJrhVK2oID/+qQm1DxNDUGfNDGcAthHmEQWzgiBvo2HfznfmhSn0cCoMGDJZWAiUx8pE6KYEPPJTqPZkOoMdHoCsPqzThpIG2gXI+4mRqF+iWuRaolDopSw2P9OyoIbLkLQ2MBBVitnJJxqAfB+WkjjeteqTDw+KaKJ/uwjWbBW0yoKaIl3h/jz3E460Mr3fkzMCABwl8g/JAnI3A4VaAfQAr0ihjfOAYihgl0FBJQHFiE57jJpIm0OlV0AOTkx7W4sBBreYwVvDfCKUkPg8ywK1EizSEoY7NwbYypwyfYeIfFJKmoZlgMGwARgBFcIPqdH18snqs7s6IZsIV4DrFcvAGqb21YQEkIla/xMVHhUq1MpduCjE68omVTBwnBZblEUdWmBBuW+NqyF1pNFWirRuqjThCHMN5eeohQA/qyOUCDwwOrWSqK9s/1DFSSHd5oeMG9bSRgjPBDiiHD7AfuN97EwEWAjoZqWDhSQrIFPNJHB5k7XaTqmMek6RreOmk4bW9ZfWgkq6Q1yfgKXIWEJkhx6V07GRFrYUw0geTYQ45sA9CZxPCSVlZy7BOVI9MVd5B9EGmcT3F9RiSMDIG2gbHOmJmrjL1o2pOtkkjCWMNVg9bdexBLdPgPTgS3QkByq88yTQYJjYHIYg2Y1cWl4KX+IwQJUTjYS9o4qzuINQppDphMA2v4oYNcl4AIKMZUFNRlCRQBAxxtznMPvjj0vKRDu0q9pJGINUTQeQkIUr8vyv+7xQjgJ5Z/GWFJJlRdACIlU7L5sKQmLoh+tAO1/Afnzjl4iLRxJW9gwAxwOs6Lk9/iSTlGFlcgyf5U4vFOTuqhWV4DFoNZxcwXsONgsPbHtwYmGZOab80kQR05Y8aKFNxVSAWhsblNnoqNJISlBy6IQe4YLwZqPsVH28bOBGGx5AIWD3iFwVZSNqkyJp2JZtkrOrDnFsVVDyI6liXBEAxsBkYRprzfyDhUdaKZ3hv3nhVAYRMlIQZySMyUEPVgo2krXzNK1F0bRxtUXwQIldWq3NFeWIHVVSgpJmV2PaiFt7CyKWTasMbNdBgGpn38IP/1CiCFnYAlkx7cnJ5JmbWMFOuhnQwUSRwUPC5uB+qexW19bRBoEOM6mQBz6pWVpRlZPk2JEECTDrua3nvj3rgowRUAg24c8DWgHiOBkI1F3DtFdCNwPioqmRGKSoKVCvJVXKg3vMQY5kZbHZwefFKMgUmURjVD8eyuU0WWzm/VIvHoZOEe8fxpQ5XrqYSApC1ExwfTphRExdpfjqb6e7qwxjRCmUP7X78lUmlAmu+NwdsiyDiCgWH93m6jteEED4b26Okl3ABJfEf7UYqCUaZ+J/odCafLyQ0vHepBhF2Hv5jSTwFfrTE/wACHjDz6fFrRAAAAABJRU5ErkJggg==")}
|
|
17
|
+
.workspace{position:relative;display:grid;grid-template-columns:272px minmax(0,1fr);min-height:0;height:auto;overflow:hidden}
|
|
18
|
+
.sidebar{min-width:0;min-height:0;overflow:auto;display:flex;flex-direction:column;padding:12px 0 0;background:var(--surface);border-right:1px solid var(--hover)}.tree-head{padding:4px 16px 10px;color:var(--faint);font-size:11px;line-height:16px;font-weight:600;letter-spacing:.08em;text-transform:uppercase}.tree-row{display:flex;align-items:center;gap:6px;width:100%;min-height:24px;padding-right:12px;border:0;background:transparent;color:var(--soft);cursor:pointer;text-align:left;font-size:12.5px;line-height:16px;white-space:nowrap;transition:background 250ms var(--ease-out),color 250ms var(--ease-out)}.tree-row.file{color:var(--dim);cursor:default}.tree-row.dir:hover{background:var(--hover);color:var(--text)}.tree-chev{display:grid;flex:none;place-items:center;width:14px;height:14px;color:var(--faint)}.tree-chev svg{width:12px;height:12px;fill:none;stroke:currentColor;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;transition:transform 250ms var(--ease-out)}.tree-row.dir.open .tree-chev svg{transform:rotate(90deg)}.tree-name{overflow:hidden;text-overflow:ellipsis}.tree-row.hot .tree-name{color:var(--accent)}.tree-ago{margin-left:auto;color:var(--accent);font:400 10px/16px var(--mono);font-variant-numeric:tabular-nums}:root[data-theme="light"] .tree-row.hot{background:var(--accent-bg);box-shadow:inset 2px 0 #e9a23b}:root[data-theme="light"] .tree-row.hot .tree-name,:root[data-theme="light"] .tree-row.hot .tree-ago{font-weight:600}.tree-empty{padding:16px;color:var(--faint);font-size:12px;line-height:16px}
|
|
19
|
+
.tree-add{display:flex;align-items:center;gap:8px;width:100%;min-height:30px;margin-top:8px;padding:0 16px;border:0;background:transparent;color:var(--faint);cursor:pointer;text-align:left;font-size:12.5px;transition:color 250ms var(--ease-out)}
|
|
20
|
+
.tree-add:hover{color:var(--text)}
|
|
21
|
+
.tree-add-form{padding:6px 12px 10px;display:flex;flex-direction:column;gap:6px}
|
|
22
|
+
.tree-add-form input{width:100%;padding:7px 10px;border:1px solid var(--line);border-radius:8px;background:var(--raised);color:var(--text);font:400 12px var(--mono)}
|
|
23
|
+
.tree-add-form input:focus{outline:none;border-color:var(--faint)}
|
|
24
|
+
.tree-add-form .row{display:flex;gap:6px}
|
|
25
|
+
.tree-add-msg{color:var(--faint);font-size:11.5px;padding:0 2px}
|
|
26
|
+
.tree-sugg{display:flex;flex-direction:column;gap:2px;max-height:216px;overflow-y:auto}
|
|
27
|
+
.sugg-row{display:flex;flex-direction:column;gap:1px;width:100%;padding:6px 10px;border:0;border-radius:8px;background:transparent;color:var(--text);cursor:pointer;text-align:left;transition:background 150ms var(--ease-out)}
|
|
28
|
+
.sugg-row:hover{background:var(--hover)}
|
|
29
|
+
.sugg-name{font-size:12.5px;font-weight:550}
|
|
30
|
+
.sugg-dir{font:400 10.5px var(--mono);color:var(--faint);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
|
31
|
+
#toast{position:fixed;left:50%;bottom:28px;transform:translate(-50%,12px);max-width:520px;padding:11px 18px;border-radius:12px;background:var(--raised);border:1px solid var(--line);color:var(--text);font-size:13px;line-height:18px;opacity:0;pointer-events:none;transition:opacity 250ms var(--ease-out),transform 250ms var(--ease-out);z-index:60}
|
|
32
|
+
#toast.show{opacity:1;transform:translate(-50%,0)}.firstrun{position:absolute;left:50%;bottom:40px;transform:translateX(-50%);z-index:2;display:flex;align-items:center;gap:16px;max-width:640px;padding:14px 20px;border:1px solid var(--line);border-radius:14px;background:var(--surface);box-shadow:0 12px 40px var(--shadow-strong)}.firstrun p{color:var(--dim);font-size:13px;line-height:18px;text-wrap:pretty;margin:0}.firstrun strong{color:var(--text);font-weight:600}.firstrun .btn{flex:none}.firstrun.inline{position:static;transform:none;max-width:none;margin-top:8px}
|
|
33
|
+
.zero{display:flex;flex-direction:column;justify-content:center;gap:18px;max-width:600px;min-height:100%;margin:0 auto;padding:48px 32px}
|
|
34
|
+
.zero h1{font-size:24px;line-height:32px;font-weight:600;letter-spacing:-.03em}
|
|
35
|
+
.zero .zero-sub{color:var(--dim);text-wrap:pretty}
|
|
36
|
+
.zero-feed{display:flex;flex-direction:column;gap:2px;padding:10px 4px;border-radius:14px;background:var(--surface)}
|
|
37
|
+
.zero-row{display:flex;align-items:center;gap:10px;min-height:30px;padding:0 12px;font:400 12.5px/16px var(--mono);color:var(--soft)}
|
|
38
|
+
.zero-row .dot{flex:none;background:var(--accent)}
|
|
39
|
+
.zero-row:first-child .dot{animation:working-pulse 1.6s ease-in-out infinite}
|
|
40
|
+
.zero-row .zf{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
|
41
|
+
.zero-row .za{margin-left:auto;color:var(--faint);flex:none;font-variant-numeric:tabular-nums}
|
|
42
|
+
.zero-empty{padding:14px 12px;color:var(--faint);font-size:12.5px}
|
|
43
|
+
.fleet{position:absolute;inset:0;z-index:3;display:none;overflow:auto;background:var(--bg);padding:56px 48px}
|
|
44
|
+
.fleet.on{display:block;animation:reveal 500ms var(--ease-out) both}
|
|
45
|
+
.fleet-head{max-width:1080px;margin:0 auto 22px;display:flex;align-items:baseline;gap:14px}
|
|
46
|
+
.fleet-head h1{font-size:22px;font-weight:600;letter-spacing:-.03em}
|
|
47
|
+
.fleet-head .fh-sub{color:var(--faint);font-size:12.5px}
|
|
48
|
+
.fleet-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(300px,1fr));gap:14px;max-width:1080px;margin:0 auto}
|
|
49
|
+
.fleet-card{position:relative;padding:16px 18px;border:1px solid var(--line);border-radius:16px;background:var(--surface);cursor:pointer;transition:border-color 300ms var(--ease-out),background 300ms var(--ease-out)}
|
|
50
|
+
.fleet-card:hover{background:var(--raised);border-color:var(--faint)}
|
|
51
|
+
.fleet-card.glow{border-color:rgba(233,162,59,.55);box-shadow:0 0 24px rgba(233,162,59,.08)}
|
|
52
|
+
.fleet-name{display:flex;align-items:center;gap:10px;font-size:15px;font-weight:600}
|
|
53
|
+
.fleet-name .live-dot{width:6px;height:6px;border-radius:50%;background:var(--faint)}
|
|
54
|
+
.fleet-card.glow .live-dot{background:var(--success);animation:working-pulse 1.6s ease-in-out infinite}
|
|
55
|
+
.fleet-minimap{display:flex;flex-wrap:wrap;gap:5px;margin-top:12px}
|
|
56
|
+
.fleet-sq{width:12px;height:12px;border-radius:3.5px;border:1.6px solid var(--faint)}
|
|
57
|
+
.fleet-sq.done{border-color:transparent;background:#3f7f57}
|
|
58
|
+
.fleet-sq.active{border-color:var(--accent)}
|
|
59
|
+
.fleet-sq.blocked{border-color:transparent;background:var(--danger)}
|
|
60
|
+
.fleet-sq.hot{box-shadow:0 0 8px rgba(233,162,59,.7);border-color:var(--accent)}
|
|
61
|
+
.fleet-agents{display:flex;align-items:center;gap:8px;margin-top:12px;min-height:20px}
|
|
62
|
+
.fleet-agents svg{width:15px;height:15px}
|
|
63
|
+
.fleet-tool{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--dim);font:400 11.5px/16px var(--mono)}
|
|
64
|
+
.fleet-idle{color:var(--faint);font-size:12px}
|
|
65
|
+
.fleet-back{position:absolute;right:24px;top:24px;z-index:4}
|
|
66
|
+
body.hide-activity .obs-ring,body.hide-activity .obs-label,body.hide-activity .obs-file,body.hide-activity .task-capsule.obsrow{display:none!important}
|
|
67
|
+
body.hide-runs .runs,body.hide-runs .task-capsule.sess{display:none!important}
|
|
68
|
+
.icon.ov-off{opacity:.35}
|
|
69
|
+
.file-ic{stroke:var(--faint);fill:none;stroke-width:1.9;stroke-linecap:round;stroke-linejoin:round}
|
|
70
|
+
.file-ic.hot{stroke:var(--accent)}
|
|
71
|
+
.gnode .file-label{fill:var(--dim);font:400 10.5px var(--mono)}
|
|
72
|
+
.gnode .file-label.hot{fill:var(--accent)}
|
|
73
|
+
.minimap{position:absolute;right:24px;bottom:24px;z-index:2;border:1px solid var(--line);border-radius:10px;background:var(--minimap-bg);box-shadow:0 12px 40px var(--shadow-strong);cursor:pointer}
|
|
74
|
+
.minimap svg{display:block}
|
|
75
|
+
.mm-node{fill:var(--faint)}.mm-node.active{fill:var(--accent)}.mm-node.blocked{fill:var(--danger)}.mm-node.done{fill:#3f7f57}
|
|
76
|
+
.mm-view{fill:none;stroke:var(--soft);stroke-width:1.2;opacity:.8}
|
|
77
|
+
.trail{fill:none;stroke-width:2.5;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:1 7;opacity:.55}
|
|
78
|
+
.handoff-label{font:600 10px var(--mono);letter-spacing:.04em}
|
|
79
|
+
.runs{position:absolute;right:24px;top:24px;z-index:2;display:flex;flex-direction:column;gap:10px;width:340px;max-width:44vw}
|
|
80
|
+
.cycle-row{display:flex;gap:8px;align-items:baseline;padding:6px 10px;border:1px solid var(--line);border-radius:10px;background:var(--overlay);font-size:11.5px;line-height:15px;color:var(--dim)}
|
|
81
|
+
.cycle-when{flex:none;color:var(--faint);font:500 10px var(--mono)}
|
|
82
|
+
.cycle-what{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
|
83
|
+
.run-sub{display:flex;align-items:center;gap:7px;margin-top:6px;padding:4px 8px;border-radius:7px;background:var(--hover);font-size:11.5px;color:var(--dim)}
|
|
84
|
+
.run-sub.ended{background:transparent;opacity:.55}
|
|
85
|
+
.run-sub-dot{width:5px;height:5px;border-radius:50%;background:var(--accent);animation:working-pulse 1.6s ease-in-out infinite}
|
|
86
|
+
.run-sub-tick{color:var(--success);font-size:10px}
|
|
87
|
+
.run-sub-name{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;flex:1}
|
|
88
|
+
.run-sub-ago{color:var(--faint);font:400 10px var(--mono)}
|
|
89
|
+
.run-cycle{margin:4px 2px 0;color:var(--faint);font:600 10.5px var(--mono);letter-spacing:.08em;text-transform:uppercase}
|
|
90
|
+
.run-card{padding:12px 14px;border:1px solid var(--line);border-radius:14px;background:var(--overlay);box-shadow:0 12px 40px var(--shadow-strong);cursor:pointer;animation:reveal 500ms var(--ease) both}
|
|
91
|
+
.run-head{display:flex;align-items:center;gap:8px}
|
|
92
|
+
.run-agent{display:inline-flex;flex:none;width:16px;height:16px}
|
|
93
|
+
.run-name{font-weight:600;font-size:13px}
|
|
94
|
+
.run-elapsed{margin-left:auto;color:var(--faint);font-size:11.5px;font-variant-numeric:tabular-nums}
|
|
95
|
+
.run-live-dot{width:6px;height:6px;border-radius:50%;background:var(--success);animation:working-pulse 1.6s ease-in-out infinite}
|
|
96
|
+
.run-card.ended .run-live-dot{background:var(--faint);animation:none}
|
|
97
|
+
.run-task{margin-top:6px;color:var(--text);font-size:12.5px;line-height:17px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
|
98
|
+
.run-tool{display:flex;gap:8px;align-items:baseline;margin-top:5px;color:var(--dim);font:400 11.5px/16px var(--mono);overflow:hidden;white-space:nowrap}
|
|
99
|
+
.run-tool .tn{color:var(--accent);flex:none}
|
|
100
|
+
.run-tool .td{overflow:hidden;text-overflow:ellipsis}
|
|
101
|
+
.run-tool .ta{margin-left:auto;color:var(--faint);flex:none;font-variant-numeric:tabular-nums}
|
|
102
|
+
.run-detail{margin-top:8px;border-top:1px solid var(--hover);padding-top:8px;display:none}
|
|
103
|
+
.run-card.open .run-detail{display:block}
|
|
104
|
+
.run-todo{display:flex;gap:8px;align-items:center;min-height:20px;color:var(--dim);font-size:12px}
|
|
105
|
+
.run-todo.in_progress{color:var(--text)}
|
|
106
|
+
.run-todo.completed{color:var(--faint);text-decoration:line-through}
|
|
107
|
+
.run-todo .box{width:10px;height:10px}
|
|
108
|
+
.run-hist{margin-top:6px;color:var(--faint);font:400 11px/16px var(--mono);display:flex;justify-content:space-between;gap:8px}
|
|
109
|
+
.run-hist .hd{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.tree-legend{position:sticky;bottom:0;margin-top:auto;padding:8px 16px;background:var(--surface);border-top:1px solid var(--hover);display:flex;align-items:center;gap:7px;color:var(--accent);opacity:.72;font-size:11px;line-height:15px}.tree-legend-dot{flex:none;width:6px;height:6px;border-radius:50%;background:var(--accent)}.primary{min-width:0;min-height:0;overflow:auto}.plan-view{padding:40px 48px 80px}.plan-inner{width:100%;max-width:960px;margin:auto}.intro{display:grid;grid-template-columns:minmax(0,1fr) auto;align-items:end;gap:24px;margin-bottom:32px}.eyebrow{margin-bottom:8px;color:var(--dim);font-size:12px;font-weight:500;letter-spacing:.04em}.title{font-size:30px;line-height:36px;font-weight:600;letter-spacing:-.04em;text-wrap:balance}.subtitle{max-width:65ch;margin-top:8px;color:var(--dim);text-wrap:pretty}.summary{min-width:176px;text-align:right}.summary-value{font-size:24px;line-height:32px;font-weight:600;font-variant-numeric:tabular-nums}.summary-label{color:var(--dim);font-size:12px}.track{height:4px;margin-top:8px;overflow:hidden;border-radius:2px;background:var(--hover)}.fill{height:100%;transform-origin:left;background:var(--soft);transition:transform 700ms var(--ease)}
|
|
110
|
+
.phases{display:grid;gap:12px}.phase{overflow:hidden;background:var(--surface);border:1px solid transparent;border-radius:16px;transition:border-color 700ms var(--ease),background 700ms var(--ease)}.phase:hover{background:var(--raised)}.phase.open{border-color:var(--line);background:var(--raised)}.phase.selected{border-color:var(--faint)}.phase-head{display:grid;grid-template-columns:auto minmax(0,1fr) auto auto;align-items:center;gap:16px;width:100%;min-height:72px;padding:12px 16px;border:0;background:transparent;color:var(--text);text-align:left;cursor:pointer}.phase-index{color:var(--faint);font:500 12px/16px var(--mono);font-variant-numeric:tabular-nums}.phase-title{display:block;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:16px;line-height:24px;font-weight:550}.phase-detail{display:block;margin-top:2px;color:var(--dim);font-size:12px;line-height:16px}.phase-meta{display:flex;align-items:center;gap:12px;color:var(--dim);font-variant-numeric:tabular-nums}.meter{width:64px;height:4px;overflow:hidden;border-radius:2px;background:var(--hover)}.meter span{display:block;height:100%;background:var(--soft)}.flag{padding:2px 8px;border-radius:4px;color:var(--accent);background:var(--accent-bg);font-size:12px;line-height:16px;font-weight:500}.chev{display:grid;place-items:center;color:var(--faint);transition:transform 700ms var(--ease),color 700ms var(--ease)}.open .chev{transform:rotate(180deg);color:var(--soft)}.phase-body{display:none;padding:0 12px 16px 48px}.open .phase-body{display:block;animation:reveal 800ms var(--ease) both}@keyframes reveal{from{opacity:0;transform:translateY(16px);filter:blur(4px)}to{opacity:1;transform:none;filter:blur(0)}}.tasks{display:grid;gap:4px;padding:8px;background:var(--surface);border-radius:12px}.task{display:grid;grid-template-columns:auto minmax(0,1fr) auto;align-items:center;gap:12px;width:100%;min-height:44px;padding:8px 12px;border:0;border-radius:8px;background:transparent;color:var(--soft);text-align:left;cursor:pointer;transition:color 700ms var(--ease),background 700ms var(--ease),transform 700ms var(--ease)}.task:hover,.task.selected{background:var(--hover);color:var(--text)}.task:active{transform:scale(.99)}.task.done{color:var(--dim)}.task.active{color:var(--text)}.box{position:relative;z-index:1;width:12px;height:12px;border:1px solid var(--faint);border-radius:50%;background:var(--surface)}.done .box{border-color:var(--soft);background:var(--soft)}.active .box{border-color:var(--accent);background:var(--surface)}.active .box:after{content:"";position:absolute;inset:3px;border-radius:50%;background:var(--accent);animation:breathe 1800ms var(--ease-out) infinite alternate}.revised .box{border-color:var(--accent);border-style:dashed}@keyframes breathe{from{opacity:.4;transform:scale(.65)}to{opacity:1;transform:scale(1)}}.task-title{display:block;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.task-meta{color:var(--faint);font-size:12px}.task-meta.current{color:var(--accent)}.activity{margin:4px 0 8px 40px;padding:12px 16px;border-left:1px solid var(--line);color:var(--dim);font:400 12px/16px var(--mono)}.activity-line{display:grid;grid-template-columns:auto minmax(0,1fr) auto;gap:12px;padding:4px 0}.pulse{color:var(--accent)}.activity-file{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.activity-time{color:var(--faint);font-variant-numeric:tabular-nums}.empty{display:grid;place-items:center;min-height:320px;color:var(--dim);text-align:center}
|
|
111
|
+
.graph-view{position:relative;overflow:hidden;background:var(--bg);touch-action:none}.graph-grid{position:absolute;inset:0;width:100%;height:100%;pointer-events:none;opacity:.35}.graph-stage{position:absolute;inset:0;cursor:grab}.graph-stage.dragging{cursor:grabbing}.graph-svg{display:block;width:100%;height:100%;overflow:visible}.world{transform-origin:0 0}.edge{fill:none;stroke:var(--line);stroke-width:1.5;vector-effect:non-scaling-stroke}.edge.link{stroke:var(--faint);stroke-dasharray:2 7;stroke-linecap:round;opacity:.8}.edge.active{stroke:var(--accent);stroke-dasharray:5 8;animation:travel 1800ms linear infinite}.edge.handoff{stroke-width:2.5;stroke-dasharray:9 7;animation:travel 1400ms linear infinite;opacity:.9}@keyframes travel{to{stroke-dashoffset:-26}}.gnode{cursor:pointer}.gnode .node-box{fill:var(--card);stroke:var(--line);stroke-width:1;transition:fill 700ms var(--ease),stroke 700ms var(--ease)}.gnode:hover .node-box{fill:var(--card-hover);stroke:var(--faint)}.gnode.selected .node-box,.gnode.active .node-box{stroke:var(--accent);stroke-width:1.5}.gnode.blocked .node-box{stroke:var(--danger);stroke-width:1.5}.gnode.knowledge .node-box{stroke:var(--knowledge);stroke-width:1.5;fill:color-mix(in srgb,var(--knowledge) 9%,var(--card))}.gnode.human .node-box{stroke:var(--human);stroke-width:1.5;stroke-dasharray:7 5;fill:color-mix(in srgb,var(--human) 7%,var(--card))}.gnode.human .kicker{fill:var(--human)}.gnode.human.obs-live .node-box,:root[data-theme="light"] .gnode.human.obs-live .node-box{fill:color-mix(in srgb,var(--human) 12%,var(--card));stroke:var(--human)}.gnode.human .obs-ring{stroke:var(--human)}.gnode.human .obs-label{fill:var(--human)}.node-url{cursor:pointer}.node-url text{fill:var(--human);font:700 10.5px var(--mono);letter-spacing:.06em}.node-url:hover text{text-decoration:underline}.gnode:not(.human) .node-url text{fill:var(--accent)}.gnode.knowledge .kicker{fill:var(--knowledge)}.node-spin{animation:capsule-spin 1100ms linear infinite;transform-box:fill-box;transform-origin:center}.obs-ring,.obs-label{opacity:0;transition:opacity 400ms var(--ease-out)}.gnode.obs-live .obs-ring{opacity:1;animation:capsule-spin 1300ms linear infinite;transform-box:fill-box;transform-origin:center}.gnode.obs-live .obs-label{opacity:1}:root[data-theme="light"] .gnode.obs-live .node-box{fill:color-mix(in srgb,#e9a23b 11%,var(--card));stroke:var(--accent);stroke-width:2}:root[data-theme="light"] .gnode.obs-live .obs-label{font-weight:700}.gnode.knowledge.obs-live .node-box,:root[data-theme="light"] .gnode.knowledge.obs-live .node-box{fill:color-mix(in srgb,var(--knowledge) 13%,var(--card));stroke:var(--knowledge)}.gnode.knowledge .obs-ring{stroke:var(--knowledge)}.gnode.knowledge .obs-label{fill:var(--knowledge)}.gnode .kicker{fill:var(--faint);font:500 11px var(--mono);letter-spacing:.04em}.gnode .node-title{fill:var(--text);font:600 15px var(--font)}.gnode .node-meta{fill:var(--dim);font:400 12px var(--font);font-variant-numeric:tabular-nums}.gnode .progress-bg{fill:var(--hover)}.gnode .progress{fill:var(--soft)}.gnode.active .progress{fill:var(--accent)}.gnode .port{fill:var(--raised);stroke:var(--faint);stroke-width:1}.gnode .revision{fill:var(--accent)}.expanded-node-card{position:relative;width:100%;height:100%;padding:16px;color:var(--text);font:400 12px/16px var(--font);overflow:hidden;animation:reveal 800ms var(--ease) both}.expanded-node-head{display:grid;grid-template-columns:minmax(0,1fr) auto;gap:12px;align-items:start;padding-bottom:12px}.expanded-node-kicker{color:var(--faint);font:500 11px/16px var(--mono);letter-spacing:.04em}.expanded-node-title{margin-top:2px;font-size:16px;line-height:24px;font-weight:600;letter-spacing:-.02em}.expanded-node-summary{color:var(--dim);font-variant-numeric:tabular-nums}.expanded-node-tasks{display:grid;gap:4px;padding:8px;border-radius:8px;background:var(--raised)}.expanded-node-task{display:grid;grid-template-columns:auto minmax(0,1fr) auto;align-items:center;gap:12px;min-height:40px;padding:8px;border-radius:4px;color:var(--dim)}.expanded-node-task.active{color:var(--text);background:var(--hover)}.expanded-node-task .box{width:12px;height:12px;border-radius:50%}.expanded-node-task .task-title{font-size:12px;line-height:16px}.expanded-node-task .task-meta{white-space:nowrap}.expanded-node-activity{display:grid;grid-template-columns:auto minmax(0,1fr) auto;gap:8px;margin:8px 0 0 32px;padding:8px 12px;border-radius:8px;background:var(--raised);color:var(--dim);font:400 11px/16px var(--mono)}.expanded-node-change{margin-top:12px;padding-top:12px;border-top:1px solid var(--line)}.expanded-node-change-head{display:flex;justify-content:space-between;gap:12px;margin-bottom:4px;color:var(--dim);font-weight:500}.expanded-node-diff{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font:400 11px/16px var(--mono)}.expanded-node-diff.rm{color:var(--danger)}.expanded-node-diff.ad{color:var(--success)}.expanded-node-stable{color:var(--faint)}.expanded-node-collapse{position:absolute;right:16px;bottom:12px;color:var(--faint);font:500 11px/16px var(--mono);letter-spacing:.04em}.canvas-tools{position:absolute;left:24px;top:24px;display:flex;align-items:center;gap:4px;padding:4px;border:1px solid var(--line);border-radius:12px;background:var(--overlay);box-shadow:0 12px 40px var(--shadow-strong)}.icon{display:grid;place-items:center;width:32px;height:32px;padding:0;border:0;border-radius:8px;background:transparent;color:var(--dim);cursor:pointer;transition:color 700ms var(--ease),background 700ms var(--ease),transform 700ms var(--ease)}.icon:hover{color:var(--text);background:var(--hover)}.icon:active{transform:scale(.96)}.zoom{min-width:48px;color:var(--dim);text-align:center;font:500 12px var(--mono);font-variant-numeric:tabular-nums}.divider{width:1px;height:20px;margin:0 4px;background:var(--line)}.canvas-hint{position:absolute;left:24px;bottom:24px;display:flex;gap:16px;color:var(--faint);font-size:12px;pointer-events:none}.hint{display:flex;align-items:center;gap:6px}.key{display:grid;place-items:center;min-width:20px;height:20px;padding:0 4px;border:1px solid var(--line);border-radius:4px;background:var(--surface);color:var(--dim);font:500 11px var(--mono)}
|
|
112
|
+
:root[data-theme="light"] .gnode.active{filter:drop-shadow(0 8px 12px var(--accent-bg))}:root[data-theme="light"] .gnode.active .node-box{fill:color-mix(in srgb,#e9a23b 11%,var(--card));stroke:var(--accent);stroke-width:2}
|
|
113
|
+
.inspector{position:relative;z-index:2;min-width:0;overflow:auto;padding:24px;background:var(--surface);border-left:1px solid var(--hover)}.inspector-top{display:flex;align-items:flex-start;gap:12px;margin-bottom:24px}.inspector-heading{min-width:0;flex:1}.inspector-kicker{color:var(--faint);font-size:12px;font-weight:500;letter-spacing:.04em}.inspector-title{margin-top:4px;font-size:18px;line-height:28px;font-weight:600;letter-spacing:-.02em;text-wrap:balance}.inspector-sub{margin-top:4px;color:var(--dim);font-size:12px;text-wrap:pretty}.section{padding:16px 0;border-top:1px solid var(--hover)}.section-label{margin-bottom:12px;color:var(--faint);font-size:12px;font-weight:500}.stats{display:grid;grid-template-columns:1fr 1fr;gap:8px}.stat{padding:12px;border-radius:8px;background:var(--raised)}.stat-value{font-size:16px;line-height:24px;font-weight:600;font-variant-numeric:tabular-nums}.stat-label{color:var(--faint);font-size:12px}.dependencies,.changes,.detail-tasks{display:grid;gap:8px}.dependency{display:flex;align-items:center;gap:8px;color:var(--soft)}.dependency:before{content:"";width:8px;height:1px;background:var(--faint)}.diff{padding-left:16px;text-indent:-16px;font:400 12px/20px var(--mono);overflow-wrap:anywhere}.diff.rm{color:var(--danger)}.diff.ad{color:var(--success)}.detail-task{display:grid;grid-template-columns:auto minmax(0,1fr) auto;align-items:center;gap:12px;width:100%;min-height:40px;padding:8px;border:0;border-radius:8px;background:var(--raised);color:var(--soft);text-align:left;cursor:pointer;transition:color 700ms var(--ease),background 700ms var(--ease),transform 700ms var(--ease)}.detail-task:hover{color:var(--text);background:var(--hover)}.detail-task:active{transform:scale(.98)}.detail-task .task-title{font-size:12px;line-height:16px}.detail-task .task-meta{white-space:nowrap}.activity-card{padding:12px;border-radius:8px;background:var(--raised);color:var(--dim);font:400 12px/16px var(--mono)}.activity-card strong{display:block;overflow:hidden;text-overflow:ellipsis;color:var(--soft);font-weight:500;white-space:nowrap}.all-clear{padding:24px 0}.clear-mark{display:grid;place-items:center;width:40px;height:40px;margin-bottom:16px;border:1px solid var(--line);border-radius:12px;color:var(--soft)}.all-clear h2{font-size:16px;line-height:24px;font-weight:600}.all-clear p{max-width:28ch;margin-top:4px;color:var(--dim);text-wrap:pretty}.gnode .node-action{fill:var(--faint);font:500 11px var(--mono);letter-spacing:.04em}.gnode:hover .node-action,.gnode.selected .node-action{fill:var(--accent)}
|
|
114
|
+
.detail-tether{fill:none;stroke:var(--faint);stroke-width:1;stroke-dasharray:3 4;vector-effect:non-scaling-stroke}.graph-detail .detail-box{fill:var(--surface);stroke:var(--faint);stroke-width:1}.expanded-plan-head{display:grid;grid-template-columns:minmax(0,1fr) auto;gap:12px;align-items:start;margin-bottom:12px}.expanded-plan-file{font-size:14px;line-height:20px;font-weight:600}.expanded-plan-subtitle{color:var(--dim);font-size:12px;line-height:16px}.expanded-plan-state{padding:2px 8px;border-radius:4px;color:var(--accent);background:var(--accent-bg);font-size:12px;line-height:16px}.expanded-phase-list{display:grid;gap:4px}.expanded-phase{position:relative;--segment-progress:0;padding:8px;border-radius:8px;color:var(--dim)}.expanded-phase:not(:last-child):before,.expanded-phase:not(:last-child):after{content:"";position:absolute;z-index:0;left:13px;top:20px;bottom:-24px;width:2px;border-radius:2px;transform-origin:top}.expanded-phase:not(:last-child):before{background:var(--line)}.expanded-phase:not(:last-child):after{background:var(--soft);transform:scaleY(var(--segment-progress))}.expanded-phase.active:not(:last-child):after{background:var(--accent);animation:rail-live 1800ms linear infinite alternate}.expanded-phase.open{background:var(--raised);color:var(--text);box-shadow:inset 0 0 0 1px var(--line)}.expanded-phase-row{position:relative;z-index:1;display:grid;grid-template-columns:12px minmax(0,1fr) auto;align-items:center;gap:8px;min-height:24px}.expanded-phase-title{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:12px;line-height:16px}.expanded-phase-count{color:var(--faint);font-variant-numeric:tabular-nums}.expanded-phase .expanded-node-tasks{position:relative;z-index:1;margin:4px 0 0 20px;padding:4px;background:transparent}.expanded-phase .expanded-node-task{min-height:24px;padding:4px}.expanded-phase .expanded-node-task.active{background:var(--hover)}@keyframes rail-live{from{opacity:.5}to{opacity:1}}
|
|
115
|
+
.actions{display:flex;gap:8px;margin-top:16px}.btn{display:inline-flex;align-items:center;gap:6px;min-height:32px;padding:4px 14px;border:1px solid var(--line);border-radius:8px;background:transparent;color:var(--soft);cursor:pointer;font-weight:500;transition:color 700ms var(--ease),background 700ms var(--ease),border-color 700ms var(--ease),transform 700ms var(--ease)}.btn:hover{color:var(--text);background:var(--hover)}.btn:active{transform:scale(.98)}.btn.accent{border-color:var(--accent-bg);color:var(--accent);background:var(--accent-bg)}.btn.accent:hover{color:var(--text);background:var(--accent-bg)}
|
|
116
|
+
.toast{position:fixed;left:50%;bottom:24px;z-index:6;transform:translate(-50%,8px);padding:8px 16px;border:1px solid var(--line);border-radius:8px;background:var(--raised);color:var(--soft);font-size:12px;opacity:0;pointer-events:none;transition:opacity 700ms var(--ease),transform 700ms var(--ease)}.toast.show{opacity:1;transform:translate(-50%,0)}
|
|
117
|
+
.task-capsules{display:flex;flex-direction:column;gap:8px;margin:8px 0 0 20px}.task-capsules.list{gap:0;overflow:hidden;border-radius:16px;background:var(--surface);box-shadow:0 2px 12px var(--shadow-soft)}.graph-task-card{width:100%;height:100%}.graph-task-card .task-capsules{width:100%;margin:0}.graph-task-empty{display:grid;place-items:center;min-height:64px;padding:16px;border-radius:16px;background:var(--surface);color:var(--dim)}.task-capsule{align-self:stretch;overflow:hidden;border-radius:22px;background:var(--surface);box-shadow:0 2px 12px var(--shadow-soft);animation:capsule-enter 450ms var(--ease-out) both;transition:border-radius 250ms var(--ease-out),background-color 250ms var(--ease-out)}.task-capsules.list .task-capsule,.task-capsules.list .task-capsule.open{border-bottom:1px solid var(--line);border-radius:0;background:transparent;box-shadow:none}.task-capsules.list .task-capsule:last-child{border-bottom:0}.task-capsule.open{border-radius:14px}.task-capsule-button{display:flex;align-items:center;gap:8px;width:100%;height:44px;padding:0 8px;border:0;background:transparent;color:var(--text);text-align:left;cursor:pointer}.task-capsule-button:active{transform:translateY(1px)}.task-capsule-marker-wrap{display:flex;flex:none;align-items:center;justify-content:center;width:24px;height:24px}.task-capsule-marker{position:relative;display:inline-flex;flex:none;align-items:center;justify-content:center;width:24px;height:24px;color:var(--text);font-size:10px;line-height:16px;font-weight:600;font-variant-numeric:tabular-nums}.task-capsule-marker svg{position:absolute;inset:0;display:block;width:24px;height:24px;fill:none}.task-capsule-marker .ring-track{stroke:var(--line);stroke-width:2}.task-capsule-marker .ring-live{visibility:hidden;stroke:var(--dim);stroke-width:2;stroke-linecap:round;stroke-dasharray:19.35 49.76;animation:capsule-spin 1100ms linear infinite}.task-capsule.active .task-capsule-marker .ring-live,.task-capsule.obs-spin .task-capsule-marker .ring-live{visibility:visible}.task-capsule-badge{display:flex;flex:none;align-items:center;justify-content:center;width:22px;height:22px;border-radius:50%;color:#fff;background:#3f7f57;animation:capsule-pop 300ms var(--ease-out) both}.task-capsule-badge svg{width:13px;height:13px;fill:none;stroke:currentColor;stroke-width:3.5;stroke-linecap:round;stroke-linejoin:round}.task-capsule-copy{min-width:0;flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:12px;line-height:16px;font-weight:500}.task-capsule-amount{flex:none;color:var(--dim);font-size:12px;line-height:16px;font-variant-numeric:tabular-nums}.task-capsule-pill{display:inline-flex;flex:none;align-items:center;height:22px;padding:0 8px;border-radius:999px;color:var(--complete-text);background:var(--complete-bg);font-size:11px;line-height:16px;font-weight:var(--complete-weight)}.task-capsule-pill.revised{color:var(--accent);background:var(--accent-bg)}.task-capsule-pill.blocked{color:#e79490;background:rgba(219,107,103,.16)}.task-capsule-pill.now{color:#e08bb8;background:rgba(215,111,163,.16)}.task-capsule-pill.backlog{color:var(--faint);background:var(--hover)}.task-capsule.sess-ended{opacity:.5}.task-capsule.sess-ended .ring-live{visibility:hidden!important}.task-capsule-badge.blocked{background:var(--danger)}.agent-chip{display:inline-flex;flex:none;align-items:center;justify-content:center;width:16px;height:16px;font-size:11px;line-height:1;font-weight:600}.agent-chip.faded{opacity:var(--done-agent-opacity)}.task-capsule-chevron{display:flex;flex:none;align-items:center;justify-content:center;width:28px;height:28px;margin-left:-8px;border-radius:50%;color:var(--faint)}.task-capsule-chevron svg{width:15px;height:15px;fill:none;stroke:currentColor;stroke-width:2.2;stroke-linecap:round;stroke-linejoin:round;transition:transform 250ms var(--ease-out)}.task-capsule.open .task-capsule-chevron svg{transform:rotate(180deg)}.task-capsule-detail{display:grid;grid-template-rows:0fr;opacity:0;transition:grid-template-rows 250ms var(--ease-out),opacity 200ms var(--ease-out)}.task-capsule.open .task-capsule-detail{grid-template-rows:1fr;opacity:1}.task-capsule-detail-clip{overflow:hidden}.task-capsule-detail-grid{display:grid;grid-template-columns:24px minmax(0,1fr);gap:8px;margin-bottom:12px;padding:0 8px}.task-capsule-detail-line{width:1px;height:100%;margin:auto;background:var(--line)}.task-capsule-detail-list{display:flex;min-width:0;flex-direction:column;gap:4px}.task-capsule-detail-row{display:flex;align-items:center;justify-content:space-between;gap:12px;min-height:20px;color:var(--dim);font-size:12px;line-height:16px;animation:capsule-detail-enter 300ms var(--ease-out) both}.task-capsule-detail-meta{overflow:hidden;color:var(--faint);font:400 11px/16px var(--mono);font-variant-numeric:tabular-nums;text-overflow:ellipsis;white-space:nowrap}.task-capsule.active .task-capsule-copy{color:var(--text)}@media(hover:hover) and (pointer:fine){.task-capsule:hover{background:var(--hover)}}@keyframes capsule-enter{from{opacity:0;transform:translateY(16px)}to{opacity:1;transform:none}}@keyframes capsule-detail-enter{from{opacity:0;transform:translateY(8px)}to{opacity:1;transform:none}}@keyframes capsule-pop{from{opacity:0;transform:scale(.95)}to{opacity:1;transform:none}}@keyframes capsule-spin{to{transform:rotate(360deg)}}
|
|
118
|
+
@media(max-width:960px){.topbar{grid-template-columns:minmax(0,1fr) auto}.switcher{grid-column:1/-1;grid-row:2}.tab{flex:1;justify-content:center}.workspace{height:calc(100dvh - 113px);grid-template-columns:minmax(0,1fr) 288px}.watching{display:none}.plan-view{padding:32px 24px 64px}}@media(max-width:720px){.topbar{padding:8px 12px;gap:12px}.badge{max-width:128px;overflow:hidden;text-overflow:ellipsis}.workspace{display:block;height:auto;min-height:calc(100dvh - 105px);overflow:visible}.primary{min-height:calc(100dvh - 105px)}.plan-view{padding:32px 16px 64px}.intro{display:block}.summary{margin-top:24px;text-align:left}.phase-head{grid-template-columns:auto minmax(0,1fr) auto;gap:12px}.phase-meta{display:none}.phase-body{padding-left:12px}.inspector{position:relative;left:auto;right:auto;bottom:auto;max-height:none;margin:0 12px 12px;border:1px solid var(--line);border-radius:16px}.graph-view{min-height:calc(100dvh - 105px)}.canvas-hint{display:none}}@media(prefers-reduced-motion:reduce){*,*:before,*:after{scroll-behavior:auto!important;animation-duration:1ms!important;animation-iteration-count:1!important;transition-duration:1ms!important}}
|
|
119
|
+
</style>
|
|
120
|
+
</head>
|
|
121
|
+
<body>
|
|
122
|
+
<a class="skip" href="#graph">Skip to diagram</a>
|
|
123
|
+
<div class="app"><header class="topbar">
|
|
124
|
+
<div class="brand"><img class="mark" src="data:image/png;base64,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" alt="" aria-hidden="true"><div class="project"><div class="project-name" id="proj">agenttrail</div><div class="file-name" id="plan-file">workspace / PLAN.md</div></div></div>
|
|
125
|
+
<div class="status"><button class="theme-toggle" id="theme-toggle" type="button" role="switch" aria-checked="false" aria-label="Use light theme" onclick="toggleTheme()"><span id="theme-toggle-label">Light</span><span class="theme-track" aria-hidden="true"><span class="theme-thumb"></span></span></button><span id="state"></span><span class="watching">watching</span></div>
|
|
126
|
+
</header>
|
|
127
|
+
<div class="workspace"><aside class="sidebar" id="tree" aria-label="Repository files"><div class="tree-empty">Connecting to the live monitor…</div></aside><section class="primary graph-view" id="graph" aria-label="Plan flow diagram"></section><div class="runs" id="runs"></div><div class="minimap" id="minimap" title="Jump around the map" style="display:none"></div></div></div>
|
|
128
|
+
<div class="toast" id="toast" role="status"></div>
|
|
129
|
+
<script>
|
|
130
|
+
let M=null,W=[],BP='',lastGraphKey='',graphPos={},regions={},minZoom=0.15,graphZoom=1,graphPan={x:0,y:0},graphSize={width:0,height:0},graphNeedsFit=true,panGesture=null
|
|
131
|
+
const openGraphDetails=new Set(),openGraphTasks=new Set(),closedGraphTasks=new Set(),closedGraphDetails=new Set()
|
|
132
|
+
function compLive(p){return p.status==='active'||(p.touchedAt&&Date.now()-p.touchedAt<60000)||((M&&M.runs)||[]).some(r=>!r.ended&&r.componentId===p.id)}
|
|
133
|
+
function isUnfolded(p){const k=ns(p.id);if(closedGraphDetails.has(k))return false;return openGraphDetails.has(k)||compLive(p)}
|
|
134
|
+
const openFolders=new Set(),closedFolders=new Set();let lastReveal=new Set()
|
|
135
|
+
const ns=id=>BP+':'+id
|
|
136
|
+
const expandedRoots=new Set(),treeCache={}
|
|
137
|
+
let addRepoOpen=false,addRepoFocus=false,addSuggestions=[]
|
|
138
|
+
async function openAddRepo(){addRepoOpen=true;addRepoFocus=true;try{addSuggestions=await(await fetch('/suggest')).json()}catch{addSuggestions=[]}renderTree()}
|
|
139
|
+
function renderAddSuggestions(){const box=document.getElementById('add-sugg');if(!box)return
|
|
140
|
+
const q=(document.getElementById('add-path')?.value||'').trim().toLowerCase()
|
|
141
|
+
const list=addSuggestions.filter(p=>!q||p.toLowerCase().includes(q)).slice(0,8)
|
|
142
|
+
box.innerHTML=list.map(p=>{const name=p.split('/').pop(),dir=p.slice(0,p.length-name.length)
|
|
143
|
+
return`<button type="button" class="sugg-row" onclick="pickSuggestion(this.dataset.p)" data-p="${esc(p)}"><span class="sugg-name">${esc(name)}</span><span class="sugg-dir">${esc(dir)}</span></button>`}).join('')||(q?`<div class="tree-add-msg">no known repo matches — full path works too</div>`:'')}
|
|
144
|
+
function pickSuggestion(p){const i=document.getElementById('add-path');if(i)i.value=p;submitAddRepo()}
|
|
145
|
+
function toast(msg,ms){let t=document.getElementById('toast');if(!t){t=document.createElement('div');t.id='toast';document.body.appendChild(t)}t.textContent=msg;t.classList.add('show');clearTimeout(t._h);t._h=setTimeout(()=>t.classList.remove('show'),ms||6000)}
|
|
146
|
+
let setupBusy=false
|
|
147
|
+
async function setupBoard(port){if(setupBusy)return;setupBusy=true;toast('setting up…')
|
|
148
|
+
try{const r=await(await fetch('/setup-board',{method:'POST',body:JSON.stringify({port})})).json()
|
|
149
|
+
if(r.ok&&r.prompt){try{await navigator.clipboard.writeText(r.prompt)
|
|
150
|
+
toast('done — a note for your agent is on your clipboard. paste it to the agent working in this repo and the map draws itself.',12000)}
|
|
151
|
+
catch{toast('set up. now ask the agent working in this repo to fill in PLAN.md — the map draws itself.',12000)}
|
|
152
|
+
setTimeout(pollWorld,1500)}
|
|
153
|
+
else toast(r.error||'could not set it up — that board may be running an older version; restart it and try again',9000)
|
|
154
|
+
}catch{toast('could not reach the board',6000)}
|
|
155
|
+
setTimeout(()=>setupBusy=false,4000)}
|
|
156
|
+
let addRepoBusy=false
|
|
157
|
+
async function submitAddRepo(){const i=document.getElementById('add-path'),msg=document.getElementById('add-msg');if(!i||!i.value.trim()||addRepoBusy)return
|
|
158
|
+
addRepoBusy=true;setTimeout(()=>addRepoBusy=false,8000)
|
|
159
|
+
msg.textContent='starting the board…'
|
|
160
|
+
try{const r=await(await fetch('/spawn',{method:'POST',body:JSON.stringify({path:i.value.trim()})})).json()
|
|
161
|
+
if(r.ok&&r.already)msg.textContent='already on the map — flying there';if(r.ok&&r.already)setTimeout(()=>{flyToRegion(r.already);addRepoOpen=false;renderTree()},600)
|
|
162
|
+
else if(r.ok){msg.textContent='started — appearing shortly…';setTimeout(()=>{addRepoOpen=false;pollWorld()},5000)}
|
|
163
|
+
else msg.textContent=r.error||'could not start it'
|
|
164
|
+
}catch{msg.textContent='could not reach the daemon'}}
|
|
165
|
+
const openRuns=new Set()
|
|
166
|
+
const $=id=>document.getElementById(id)
|
|
167
|
+
function syncTheme(){const light=document.documentElement.dataset.theme==='light',toggle=$('theme-toggle'),label=$('theme-toggle-label');if(toggle){toggle.setAttribute('aria-checked',String(light));toggle.setAttribute('aria-label',light?'Use dark theme':'Use light theme')}if(label)label.textContent=light?'Dark':'Light';document.querySelector('meta[name="theme-color"]')?.setAttribute('content',light?'#fff':'#000')}
|
|
168
|
+
function toggleTheme(){const next=document.documentElement.dataset.theme==='light'?'dark':'light';document.documentElement.dataset.theme=next;try{localStorage.setItem('at-theme',next)}catch{}syncTheme()}
|
|
169
|
+
syncTheme()
|
|
170
|
+
const esc=s=>String(s??'').replace(/[&<>"']/g,c=>({'&':'&','<':'<','>':'>','"':'"',"'":'''}[c]))
|
|
171
|
+
const ago=ms=>{const s=Math.max(0,Math.round((Date.now()-ms)/1000));return s<60?`${s}s`:s<3600?`${Math.round(s/60)} min`:`${Math.round(s/3600)} h`}
|
|
172
|
+
const AGENT_COLORS={claude:'#d97757',codex:'var(--agent-openai)',chatgpt:'var(--agent-openai)',openai:'var(--agent-openai)',cursor:'#5b7fd6',human:'#8b8b93',k:'#8b8b93'}
|
|
173
|
+
const AGENT_ICONS={claude:'m4.7144 15.9555 4.7174-2.6471.079-.2307-.079-.1275h-.2307l-.7893-.0486-2.6956-.0729-2.3375-.0971-2.2646-.1214-.5707-.1215-.5343-.7042.0546-.3522.4797-.3218.686.0608 1.5179.1032 2.2767.1578 1.6514.0972 2.4468.255h.3886l.0546-.1579-.1336-.0971-.1032-.0972L6.973 9.8356l-2.55-1.6879-1.3356-.9714-.7225-.4918-.3643-.4614-.1578-1.0078.6557-.7225.8803.0607.2246.0607.8925.686 1.9064 1.4754 2.4893 1.8336.3643.3035.1457-.1032.0182-.0728-.164-.2733-1.3539-2.4467-1.445-2.4893-.6435-1.032-.17-.6194c-.0607-.255-.1032-.4674-.1032-.7285L6.287.1335 6.6997 0l.9957.1336.419.3642.6192 1.4147 1.0018 2.2282 1.5543 3.0296.4553.8985.2429.8318.091.255h.1579v-.1457l.1275-1.706.2368-2.0947.2307-2.6957.0789-.7589.3764-.9107.7468-.4918.5828.2793.4797.686-.0668.4433-.2853 1.8517-.5586 2.9021-.3643 1.9429h.2125l.2429-.2429.9835-1.3053 1.6514-2.0643.7286-.8196.85-.9046.5464-.4311h1.0321l.759 1.1293-.34 1.1657-1.0625 1.3478-.8804 1.1414-1.2628 1.7-.7893 1.36.0729.1093.1882-.0183 2.8535-.607 1.5421-.2794 1.8396-.3157.8318.3886.091.3946-.3278.8075-1.967.4857-2.3072.4614-3.4364.8136-.0425.0304.0486.0607 1.5482.1457.6618.0364h1.621l3.0175.2247.7892.522.4736.6376-.079.4857-1.2142.6193-1.6393-.3886-3.825-.9107-1.3113-.3279h-.1822v.1093l1.0929 1.0686 2.0035 1.8092 2.5075 2.3314.1275.5768-.3218.4554-.34-.0486-2.2039-1.6575-.85-.7468-1.9246-1.621h-.1275v.17l.4432.6496 2.3436 3.5214.1214 1.0807-.17.3521-.6071.2125-.6679-.1214-1.3721-1.9246L14.38 17.959l-1.1414-1.9428-.1397.079-.674 7.2552-.3156.3703-.7286.2793-.6071-.4614-.3218-.7468.3218-1.4753.3886-1.9246.3157-1.53.2853-1.9004.17-.6314-.0121-.0425-.1397.0182-1.4328 1.9672-2.1796 2.9446-1.7243 1.8456-.4128.164-.7164-.3704.0667-.6618.4008-.5889 2.386-3.0357 1.4389-1.882.929-1.0868-.0062-.1579h-.0546l-6.3385 4.1164-1.1293.1457-.4857-.4554.0608-.7467.2307-.2429 1.9064-1.3114Z',codex:'M22.2819 9.8211a5.9847 5.9847 0 0 0-.5157-4.9108 6.0462 6.0462 0 0 0-6.5098-2.9A6.0651 6.0651 0 0 0 4.9807 4.1818a5.9847 5.9847 0 0 0-3.9977 2.9 6.0462 6.0462 0 0 0 .7427 7.0966 5.98 5.98 0 0 0 .511 4.9107 6.051 6.051 0 0 0 6.5146 2.9001A5.9847 5.9847 0 0 0 13.2599 24a6.0557 6.0557 0 0 0 5.7718-4.2058 5.9894 5.9894 0 0 0 3.9977-2.9001 6.0557 6.0557 0 0 0-.7475-7.0729zm-9.022 12.6081a4.4755 4.4755 0 0 1-2.8764-1.0408l.1419-.0804 4.7783-2.7582a.7948.7948 0 0 0 .3927-.6813v-6.7369l2.02 1.1686a.071.071 0 0 1 .038.052v5.5826a4.504 4.504 0 0 1-4.4945 4.4944zm-9.6607-4.1254a4.4708 4.4708 0 0 1-.5346-3.0137l.142.0852 4.783 2.7582a.7712.7712 0 0 0 .7806 0l5.8428-3.3685v2.3324a.0804.0804 0 0 1-.0332.0615L9.74 19.9502a4.4992 4.4992 0 0 1-6.1408-1.6464zM2.3408 7.8956a4.485 4.485 0 0 1 2.3655-1.9728V11.6a.7664.7664 0 0 0 .3879.6765l5.8144 3.3543-2.0201 1.1685a.0757.0757 0 0 1-.071 0l-4.8303-2.7865A4.504 4.504 0 0 1 2.3408 7.872zm16.5963 3.8558L13.1038 8.364 15.1192 7.2a.0757.0757 0 0 1 .071 0l4.8303 2.7913a4.4944 4.4944 0 0 1-.6765 8.1042v-5.6772a.79.79 0 0 0-.407-.667zm2.0107-3.0231l-.142-.0852-4.7735-2.7818a.7759.7759 0 0 0-.7854 0L9.409 9.2297V6.8974a.0662.0662 0 0 1 .0284-.0615l4.8303-2.7866a4.4992 4.4992 0 0 1 6.6802 4.66zM8.3065 12.863l-2.02-1.1638a.0804.0804 0 0 1-.038-.0567V6.0742a4.4992 4.4992 0 0 1 7.3757-3.4537l-.142.0805L8.704 5.459a.7948.7948 0 0 0-.3927.6813zm1.0976-2.3654l2.602-1.4998 2.6069 1.4998v2.9994l-2.5974 1.4997-2.6067-1.4997Z',chatgpt:'M22.2819 9.8211a5.9847 5.9847 0 0 0-.5157-4.9108 6.0462 6.0462 0 0 0-6.5098-2.9A6.0651 6.0651 0 0 0 4.9807 4.1818a5.9847 5.9847 0 0 0-3.9977 2.9 6.0462 6.0462 0 0 0 .7427 7.0966 5.98 5.98 0 0 0 .511 4.9107 6.051 6.051 0 0 0 6.5146 2.9001A5.9847 5.9847 0 0 0 13.2599 24a6.0557 6.0557 0 0 0 5.7718-4.2058 5.9894 5.9894 0 0 0 3.9977-2.9001 6.0557 6.0557 0 0 0-.7475-7.0729zm-9.022 12.6081a4.4755 4.4755 0 0 1-2.8764-1.0408l.1419-.0804 4.7783-2.7582a.7948.7948 0 0 0 .3927-.6813v-6.7369l2.02 1.1686a.071.071 0 0 1 .038.052v5.5826a4.504 4.504 0 0 1-4.4945 4.4944zm-9.6607-4.1254a4.4708 4.4708 0 0 1-.5346-3.0137l.142.0852 4.783 2.7582a.7712.7712 0 0 0 .7806 0l5.8428-3.3685v2.3324a.0804.0804 0 0 1-.0332.0615L9.74 19.9502a4.4992 4.4992 0 0 1-6.1408-1.6464zM2.3408 7.8956a4.485 4.485 0 0 1 2.3655-1.9728V11.6a.7664.7664 0 0 0 .3879.6765l5.8144 3.3543-2.0201 1.1685a.0757.0757 0 0 1-.071 0l-4.8303-2.7865A4.504 4.504 0 0 1 2.3408 7.872zm16.5963 3.8558L13.1038 8.364 15.1192 7.2a.0757.0757 0 0 1 .071 0l4.8303 2.7913a4.4944 4.4944 0 0 1-.6765 8.1042v-5.6772a.79.79 0 0 0-.407-.667zm2.0107-3.0231l-.142-.0852-4.7735-2.7818a.7759.7759 0 0 0-.7854 0L9.409 9.2297V6.8974a.0662.0662 0 0 1 .0284-.0615l4.8303-2.7866a4.4992 4.4992 0 0 1 6.6802 4.66zM8.3065 12.863l-2.02-1.1638a.0804.0804 0 0 1-.038-.0567V6.0742a4.4992 4.4992 0 0 1 7.3757-3.4537l-.142.0805L8.704 5.459a.7948.7948 0 0 0-.3927.6813zm1.0976-2.3654l2.602-1.4998 2.6069 1.4998v2.9994l-2.5974 1.4997-2.6067-1.4997Z',openai:'M22.2819 9.8211a5.9847 5.9847 0 0 0-.5157-4.9108 6.0462 6.0462 0 0 0-6.5098-2.9A6.0651 6.0651 0 0 0 4.9807 4.1818a5.9847 5.9847 0 0 0-3.9977 2.9 6.0462 6.0462 0 0 0 .7427 7.0966 5.98 5.98 0 0 0 .511 4.9107 6.051 6.051 0 0 0 6.5146 2.9001A5.9847 5.9847 0 0 0 13.2599 24a6.0557 6.0557 0 0 0 5.7718-4.2058 5.9894 5.9894 0 0 0 3.9977-2.9001 6.0557 6.0557 0 0 0-.7475-7.0729zm-9.022 12.6081a4.4755 4.4755 0 0 1-2.8764-1.0408l.1419-.0804 4.7783-2.7582a.7948.7948 0 0 0 .3927-.6813v-6.7369l2.02 1.1686a.071.071 0 0 1 .038.052v5.5826a4.504 4.504 0 0 1-4.4945 4.4944zm-9.6607-4.1254a4.4708 4.4708 0 0 1-.5346-3.0137l.142.0852 4.783 2.7582a.7712.7712 0 0 0 .7806 0l5.8428-3.3685v2.3324a.0804.0804 0 0 1-.0332.0615L9.74 19.9502a4.4992 4.4992 0 0 1-6.1408-1.6464zM2.3408 7.8956a4.485 4.485 0 0 1 2.3655-1.9728V11.6a.7664.7664 0 0 0 .3879.6765l5.8144 3.3543-2.0201 1.1685a.0757.0757 0 0 1-.071 0l-4.8303-2.7865A4.504 4.504 0 0 1 2.3408 7.872zm16.5963 3.8558L13.1038 8.364 15.1192 7.2a.0757.0757 0 0 1 .071 0l4.8303 2.7913a4.4944 4.4944 0 0 1-.6765 8.1042v-5.6772a.79.79 0 0 0-.407-.667zm2.0107-3.0231l-.142-.0852-4.7735-2.7818a.7759.7759 0 0 0-.7854 0L9.409 9.2297V6.8974a.0662.0662 0 0 1 .0284-.0615l4.8303-2.7866a4.4992 4.4992 0 0 1 6.6802 4.66zM8.3065 12.863l-2.02-1.1638a.0804.0804 0 0 1-.038-.0567V6.0742a4.4992 4.4992 0 0 1 7.3757-3.4537l-.142.0805L8.704 5.459a.7948.7948 0 0 0-.3927.6813zm1.0976-2.3654l2.602-1.4998 2.6069 1.4998v2.9994l-2.5974 1.4997-2.6067-1.4997Z'}
|
|
174
|
+
function agentIcon(n){return AGENT_ICONS[n.toLowerCase()]||null}
|
|
175
|
+
const AGENT_GLYPHS={cursor:'\u25c6'}
|
|
176
|
+
function agentGlyph(n){return AGENT_GLYPHS[n.toLowerCase()]||n[0].toUpperCase()}
|
|
177
|
+
function agentColor(n){const k=n.toLowerCase();if(AGENT_COLORS[k])return AGENT_COLORS[k];let h=0;for(const c of k)h=(h*31+c.charCodeAt(0))%360;return`hsl(${h} 45% 45%)`}
|
|
178
|
+
const statusLabel=s=>s==='done'?'Complete':s==='active'?'In progress':s==='blocked'?'Blocked':'Not started',pct=(done,total)=>total?Math.round(done/total*100):0
|
|
179
|
+
const es=new EventSource('/events');let connectionTimer=null
|
|
180
|
+
es.onopen=()=>clearTimeout(connectionTimer)
|
|
181
|
+
// AOS patch: a daemon restart mints a new session.id — reload this stale page instead of waiting for Cmd+R
|
|
182
|
+
let seenSessionId=null
|
|
183
|
+
function checkSessionId(id){if(!id)return;if(seenSessionId===null)seenSessionId=id;else if(id!==seenSessionId)location.reload()}
|
|
184
|
+
async function pollWorld(){try{const d=await(await fetch('/world')).json();if(Array.isArray(d)&&d.length){W=d;if(M){const self=d.find(x=>x.port===M.port);if(self)checkSessionId(self.session&&self.session.id)}render()}}catch{}}
|
|
185
|
+
setInterval(pollWorld,2000)
|
|
186
|
+
function syncSelfIntoW(){if(!M)return;const i=W.findIndex(b=>b.port===M.port);const lite={...M};delete lite.tree;if(i>=0)W[i]=lite;else{W.push(lite);W.sort((a,b)=>a.port-b.port)}}
|
|
187
|
+
// AOS patch: asks panel — open questions from the daemon, GO/Nein to answer, decisions log
|
|
188
|
+
let askDecisions=[],answering=new Set(),askSig=''
|
|
189
|
+
function selfAsks(){return((M&&M.asks)||[]).slice().sort((a,b)=>(a.askedAt||0)-(b.askedAt||0))}
|
|
190
|
+
function askStatusLabel(a){return a.expired||a.status==='expired'?'Abgelaufen':a.status==='go'?'GO':a.status==='deny'?'Nein':'Wartet auf dich'}
|
|
191
|
+
function askCardHtml(a){
|
|
192
|
+
const dim=!!(a.expired||a.status==='expired'||a.status==='go'||a.status==='deny'),busy=answering.has(String(a.id))
|
|
193
|
+
const btn=(d,cls)=>`<button class="btn ${cls}" type="button" data-id="${esc(String(a.id))}" data-decision="${d}" onclick="answerAsk(this.dataset.id,this.dataset.decision)"${dim||busy?' disabled':''}>${d==='go'?'GO':'Nein'}</button>`
|
|
194
|
+
return`<div class="run-card${dim?' ended':''}" data-ask="${esc(String(a.id))}"${dim?' style="opacity:.6;cursor:default"':''}>
|
|
195
|
+
<div class="run-head"><span class="run-live-dot"></span><span class="run-name">${esc(askStatusLabel(a))}</span>${a.askedBy?`<span style="color:var(--faint);font-size:11.5px">· ${esc(a.askedBy)}</span>`:''}<span class="run-elapsed"><span class="t-ago" data-at="${a.askedAt||Date.now()}"></span></span></div>
|
|
196
|
+
<div class="run-task" style="white-space:normal">${esc(a.question)}</div>
|
|
197
|
+
${dim?'':`<div class="actions" style="margin-top:10px">${btn('go','accent')}${btn('deny','')}</div>`}
|
|
198
|
+
</div>`}
|
|
199
|
+
function decisionsHtml(){
|
|
200
|
+
return`<div class="run-card ended" style="cursor:default"><div class="run-task" style="margin-top:0;white-space:normal;color:var(--faint)">Entscheidungen</div>${askDecisions.map(d=>{const ts=d.at?Date.parse(d.at):NaN
|
|
201
|
+
return`<div class="run-hist"${d.question?` title="${esc(d.question)}"`:''}><span class="hd">${esc(String(d.by||'?'))} · ${isFinite(ts)?`<span class="t-ago" data-at="${ts}"></span>`:esc(String(d.at||''))} · ${esc(String(d.decision||''))}</span></div>`}).join('')}</div>`}
|
|
202
|
+
async function refreshDecisions(){
|
|
203
|
+
const sig=selfAsks().map(a=>`${String(a.id)}:${a.status}:${a.expired?1:0}`).join('|')
|
|
204
|
+
if(sig===askSig)return;askSig=sig
|
|
205
|
+
const asks=selfAsks(),newest=asks[asks.length-1]
|
|
206
|
+
if(!newest){askDecisions=[];renderRuns();return}
|
|
207
|
+
try{const d=await(await fetch('/ask/'+encodeURIComponent(String(newest.id)))).json();if(Array.isArray(d.decisions))askDecisions=d.decisions}catch{}
|
|
208
|
+
renderRuns();renderTimes()}
|
|
209
|
+
async function answerAsk(key,decision){
|
|
210
|
+
const ask=selfAsks().find(x=>String(x.id)===String(key));if(!ask)return
|
|
211
|
+
if(answering.has(String(ask.id)))return;answering.add(String(ask.id));renderRuns()
|
|
212
|
+
try{const r=await fetch('/answer',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({id:ask.id,decision})})
|
|
213
|
+
if(!r.ok){const e=await r.json().catch(()=>({}));toast(e.error||'answer rejected',6000)}}
|
|
214
|
+
catch{toast('could not reach the daemon',6000)}
|
|
215
|
+
answering.delete(String(ask.id));askSig='';renderRuns();renderTimes();refreshDecisions()}
|
|
216
|
+
es.onmessage=e=>{clearTimeout(connectionTimer);const d=JSON.parse(e.data)
|
|
217
|
+
if(d.partial&&M){M.activity=d.activity;M.recentActivity=d.recentActivity;M.runs=d.runs||M.runs||[];M.handoffs=d.handoffs||M.handoffs||[];M.hotFiles=d.hotFiles||{};M.asks=d.asks||M.asks||[];for(const n of M.plan)if(n.level==='component'){n.touchedAt=d.touched[n.id]||null;n.recent=(d.compRecent||{})[n.id]||n.recent||[]}syncSelfIntoW();render();refreshDecisions();return}
|
|
218
|
+
M=d;checkSessionId(d.session&&d.session.id);syncSelfIntoW();if(!W.length)pollWorld();render();refreshDecisions()}
|
|
219
|
+
es.onerror=()=>{if(!M){clearTimeout(connectionTimer);connectionTimer=setTimeout(renderConnectionError,1200)}}
|
|
220
|
+
setInterval(()=>{if(M)renderTimes()},1000)
|
|
221
|
+
function isOpen(p){if(userClosed.has(p.id))return false;if(userOpen.has(p.id))return true;return p.status==='active'}
|
|
222
|
+
function kids(id){return M.plan.filter(n=>n.parent===id)}
|
|
223
|
+
function renderBadge(){
|
|
224
|
+
const working=M.activity&&(Date.now()-M.activity.at)<60000
|
|
225
|
+
let html=working?`<span class="badge working"><span class="dot"></span>Working · <span class="t-ago" data-at="${M.activity.at}"></span> ago</span>`:'<span class="badge"><span class="dot"></span>Plan is live</span>'
|
|
226
|
+
if(M.planStale&&M.planStale.stale)html+=` <span class="badge changed" title="Code has moved for ${M.planStale.minutes} minutes without a plan update. Tell your agent: re-verify PLAN.md against the code."><span class="dot"></span>Plan behind · ${M.planStale.minutes}m</span>`
|
|
227
|
+
if(M.lints&&M.lints.length)html+=` <span class="badge" style="cursor:help" title="${esc(M.lints.join('\n'))}">⚠ ${M.lints.length} plan note${M.lints.length===1?'':'s'}</span>`
|
|
228
|
+
$('state').innerHTML=html
|
|
229
|
+
}
|
|
230
|
+
function render(){
|
|
231
|
+
$('proj').textContent=M.session.project;renderBadge()
|
|
232
|
+
// AOS patch: update the plan file subtitle when planFile is present
|
|
233
|
+
const planFileEl = $('plan-file')
|
|
234
|
+
if (planFileEl) {
|
|
235
|
+
if (M.planFile) {
|
|
236
|
+
planFileEl.textContent = M.planFile
|
|
237
|
+
} else {
|
|
238
|
+
planFileEl.textContent = 'workspace / PLAN.md'
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
document.title=`agenttrail · ${M.session.project}`
|
|
242
|
+
renderTree();renderRuns()
|
|
243
|
+
const bs=W.length?W:(M?[M]:[])
|
|
244
|
+
const graphKey=JSON.stringify(bs.map(b=>[b.port,JSON.stringify(b.plan,(k,v)=>(k==='touchedAt'||k==='recent')?undefined:v),(b.runs||[]).map(r=>[r.componentId,r.ended,r.todos,(r.path||[]).length,(r.subagents||[]).map(x=>x.name+(x.ended?1:0)).join()]),(b.handoffs||[]).length,b.plan.filter(n=>n.level==='component').map(n=>n.touchedAt&&Date.now()-n.touchedAt<60000?1:0).join('')]))+'|'+String(graphZoom<CARD_LOD)
|
|
245
|
+
if(graphKey!==lastGraphKey){lastGraphKey=graphKey;renderGraph()}
|
|
246
|
+
renderTimes()
|
|
247
|
+
}
|
|
248
|
+
function renderConnectionError(){
|
|
249
|
+
$('state').innerHTML='<span class="badge changed"><span class="dot"></span>Not connected</span>'
|
|
250
|
+
$('tree').innerHTML='<div class="tree-empty">Could not reach the live monitor.</div>'
|
|
251
|
+
$('graph').innerHTML='<div class="empty"><div><strong>Could not reach the live monitor</strong><br>Start agenttrail, then open this page from its local address.</div></div>'
|
|
252
|
+
}
|
|
253
|
+
function isFolderOpen(p){if(closedFolders.has(p))return false;if(openFolders.has(p))return true;return lastReveal.has(p)}
|
|
254
|
+
function renderTree(){
|
|
255
|
+
if(addRepoOpen&&document.getElementById('add-path'))return
|
|
256
|
+
const boards=boardsAll()
|
|
257
|
+
if(!boards.length){$('tree').innerHTML='<div class="tree-empty">Connecting to the live monitor…</div>';return}
|
|
258
|
+
if(!expandedRoots.size&&M)expandedRoots.add(M.port)
|
|
259
|
+
let html=''
|
|
260
|
+
for(const b of boards){
|
|
261
|
+
const open=expandedRoots.has(b.port)
|
|
262
|
+
html+=`<button class="tree-row dir root ${open?'open':''}" type="button" data-root="${b.port}" style="padding-left:8px;min-height:28px"><span class="tree-chev"><svg viewBox="0 0 24 24"><path d="m9 6 6 6-6 6"/></svg></span><span class="tree-name" style="font-weight:600;color:var(--text)">${esc(b.project)}</span></button>`
|
|
263
|
+
if(open)html+=renderRootTree(b)
|
|
264
|
+
}
|
|
265
|
+
html+=addRepoOpen?`<div class="tree-add-form"><input id="add-path" placeholder="/path/to/repo — or pick below" autocomplete="off" spellcheck="false" oninput="renderAddSuggestions()" onkeydown="if(event.key==='Enter')submitAddRepo()"><div class="tree-sugg" id="add-sugg"></div><div class="row"><button class="btn small primary" type="button" onclick="submitAddRepo()">Add board</button><button class="btn small" type="button" onclick="addRepoOpen=false;renderTree()">Cancel</button></div><div class="tree-add-msg" id="add-msg">Its region appears on the map within a few seconds.</div></div>`:`<button class="tree-add" type="button" onclick="openAddRepo()">+ add repo</button>`
|
|
266
|
+
html+=`<div class="tree-legend"><span class="tree-legend-dot"></span>most recent edit · time since last write</div>`
|
|
267
|
+
$('tree').innerHTML=html
|
|
268
|
+
if(addRepoOpen){renderAddSuggestions();const i=document.getElementById('add-path');if(i&&addRepoFocus){i.focus();addRepoFocus=false}}
|
|
269
|
+
}
|
|
270
|
+
function renderRootTree(b){
|
|
271
|
+
const isSelf=M&&b.port===M.port
|
|
272
|
+
const tr=isSelf?M.tree:(treeCache[b.port]&&treeCache[b.port].tree)
|
|
273
|
+
if(!tr){if(!isSelf)loadTree(b.port);return '<div class="tree-empty">loading…</div>'}
|
|
274
|
+
const act=new Map((b.recentActivity||[]).map(a=>[a.file,a.at]))
|
|
275
|
+
lastReveal=new Set()
|
|
276
|
+
if(b.recentActivity&&b.recentActivity[0]){const parts=b.recentActivity[0].file.split('/');let p='';for(let i=0;i<parts.length-1;i++){p=p?p+'/'+parts[i]:parts[i];lastReveal.add(b.port+'|'+p)}}
|
|
277
|
+
const rows=[]
|
|
278
|
+
const walk=(nodes,depth)=>{for(const n of nodes){const pad=22+depth*14
|
|
279
|
+
const key=b.port+'|'+n.path
|
|
280
|
+
if(n.dir){const open=isFolderOpen(key)
|
|
281
|
+
rows.push(`<button class="tree-row dir ${open?'open':''}" type="button" data-path="${esc(key)}" style="padding-left:${pad}px"><span class="tree-chev"><svg viewBox="0 0 24 24"><path d="m9 6 6 6-6 6"/></svg></span><span class="tree-name">${esc(n.name)}</span></button>`)
|
|
282
|
+
if(open)walk(n.children||[],depth+1)
|
|
283
|
+
}else{const at=act.get(n.path)
|
|
284
|
+
rows.push(`<div class="tree-row file${at?' hot':''}" style="padding-left:${pad+20}px"><span class="tree-name">${esc(n.name)}</span>${at?`<span class="tree-ago"><span class="t-ago" data-at="${at}"></span></span>`:''}</div>`)}}}
|
|
285
|
+
walk(tr,0)
|
|
286
|
+
return rows.join('')
|
|
287
|
+
}
|
|
288
|
+
async function loadTree(port){if(treeCache[port])return;treeCache[port]={}
|
|
289
|
+
try{const d=await(await fetch('/tree-of?port='+port)).json();treeCache[port]={tree:d.tree||[]};renderTree();renderTimes()}catch{treeCache[port]={tree:[]}}}
|
|
290
|
+
$('tree').addEventListener('click',e=>{
|
|
291
|
+
const root=e.target.closest('.tree-row.root')
|
|
292
|
+
if(root){const p=+root.dataset.root
|
|
293
|
+
if(e.target.closest('.tree-chev')){expandedRoots.has(p)?expandedRoots.delete(p):expandedRoots.add(p)}
|
|
294
|
+
else{expandedRoots.add(p);flyToRegion(p)}
|
|
295
|
+
renderTree();renderTimes();return}
|
|
296
|
+
const bEl=e.target.closest('.tree-row.dir');if(!bEl)return;const p=bEl.dataset.path
|
|
297
|
+
if(isFolderOpen(p)){openFolders.delete(p);closedFolders.add(p)}else{closedFolders.delete(p);openFolders.add(p)}
|
|
298
|
+
renderTree();renderTimes()})
|
|
299
|
+
const overlays={activity:true,runs:true}
|
|
300
|
+
try{Object.assign(overlays,JSON.parse(localStorage.getItem('at-overlays')||'{}'))}catch{}
|
|
301
|
+
function applyOverlays(){document.body.classList.toggle('hide-activity',!overlays.activity);document.body.classList.toggle('hide-runs',!overlays.runs)
|
|
302
|
+
const a=$('ov-activity'),r=$('ov-runs');if(a)a.classList.toggle('ov-off',!overlays.activity);if(r)r.classList.toggle('ov-off',!overlays.runs)}
|
|
303
|
+
function toggleOverlay(k){overlays[k]=!overlays[k];try{localStorage.setItem('at-overlays',JSON.stringify(overlays))}catch{};applyOverlays()}
|
|
304
|
+
function fitWorld(){const stage=$('graph-stage');if(!stage||!graphSize.width)return;const box=stage.getBoundingClientRect();graphZoom=Math.max(minZoom,Math.min((box.width-80)/graphSize.width,(box.height-80)/graphSize.height));graphPan={x:(box.width-graphSize.width*graphZoom)/2,y:(box.height-graphSize.height*graphZoom)/2};updateTransform()}
|
|
305
|
+
function flyToRegion(port){const r=regions[port];if(!r)return;const stage=$('graph-stage');if(!stage)return;const box=stage.getBoundingClientRect()
|
|
306
|
+
const z=Math.max(.45,Math.min(1.1,(box.width-128)/r.w,(box.height-128)/r.h))
|
|
307
|
+
const world=$('world');if(world){world.style.transition='transform 600ms cubic-bezier(.23,1,.32,1)';setTimeout(()=>{if(world)world.style.transition=''},650)}
|
|
308
|
+
graphZoom=z;graphPan={x:(box.width-r.w*z)/2-r.ox*z,y:(box.height-r.h*z)/2-r.oy*z};updateTransform()}
|
|
309
|
+
function renderZero(){
|
|
310
|
+
const feed=(M.recentActivity||[]).slice(0,8).map(a=>`<div class="zero-row"><span class="dot"></span><span class="zf">${esc(a.file)}${a.n>1?` ×${a.n}`:''}</span><span class="za t-ago" data-at="${a.at}"></span></div>`).join('')
|
|
311
|
+
// AOS patch: show AOS-specific message when aosPlan is true, otherwise show upstream init/backfill block
|
|
312
|
+
const emptyStateBlock = M.aosPlan
|
|
313
|
+
? `<div class="firstrun inline"><p>Waiting for the plan — the pipeline's Architect writes ${esc(M.planFile||'the plan file')}; the map appears as soon as it exists.</p></div>`
|
|
314
|
+
: `<div class="firstrun inline"><p><strong>Level 2 — the map.</strong> You never write PLAN.md by hand: copy this prompt, paste it to your agent in this repo, and it draws the component map the board runs on.</p><button class="btn accent" type="button" onclick="copyBackfill()">Copy backfill prompt</button></div>`
|
|
315
|
+
$('graph').innerHTML=`<div class="zero">
|
|
316
|
+
<div><h1>Watching ${esc(M.session.project)}</h1><p class="zero-sub">Every file an agent touches shows up below, live — and hooked Claude Code sessions appear as run cards with their plan and current tool. No setup needed for any of that.</p></div>
|
|
317
|
+
<div class="zero-feed">${feed||'<div class="zero-empty">Waiting for the first file write — start your agent and watch this fill in.</div>'}</div>
|
|
318
|
+
${emptyStateBlock}
|
|
319
|
+
</div>`
|
|
320
|
+
}
|
|
321
|
+
function isSkeletonPlan(){return M.plan.filter(n=>n.level==='task').length<=1}
|
|
322
|
+
function boardLayout(b){
|
|
323
|
+
const phases=b.plan.filter(n=>n.level==='component')
|
|
324
|
+
const byId=Object.fromEntries(phases.map(p=>[p.id,p])),depth={},visiting=new Set()
|
|
325
|
+
function getDepth(p){if(depth[p.id]!=null)return depth[p.id];if(visiting.has(p.id))return 0;visiting.add(p.id);const preds=p.needs.filter(id=>byId[id]);depth[p.id]=preds.length?1+Math.max(...preds.map(id=>getDepth(byId[id]))):0;visiting.delete(p.id);return depth[p.id]}
|
|
326
|
+
phases.forEach(getDepth);const cols={};phases.forEach(p=>(cols[depth[p.id]]??=[]).push(p));const gx=120,gy=48,pad=64,pos={},metrics=Object.fromEntries(phases.map(p=>[p.id,graphNodeMetrics(p)])),columnWidths={}
|
|
327
|
+
for(const[col,list]of Object.entries(cols))columnWidths[col]=Math.max(...list.map(p=>metrics[p.id].w))
|
|
328
|
+
const orderedColumns=Object.keys(cols).map(Number).sort((a,b)=>a-b),columnX={};let nextX=pad
|
|
329
|
+
for(const col of orderedColumns){columnX[col]=nextX;nextX+=columnWidths[col]+gx}
|
|
330
|
+
let maxBottom=pad
|
|
331
|
+
for(const[col,list]of Object.entries(cols)){let nextY=pad;for(const p of list){const size=metrics[p.id],x=columnX[col]+(columnWidths[col]-size.w)/2;pos[p.id]={x,y:nextY,...size,nodeX:x+(size.w-size.nodeW)/2,nodeY:nextY};nextY+=size.h+gy}maxBottom=Math.max(maxBottom,nextY-gy+pad)}
|
|
332
|
+
return{phases,byId,pos,width:Math.max(nextX-gx+pad,560),height:Math.max(maxBottom,300)}
|
|
333
|
+
}
|
|
334
|
+
function boardInnerHtml(b,L){
|
|
335
|
+
const {phases,byId,pos}=L
|
|
336
|
+
const seenLinks=new Set(),linkPairs=[]
|
|
337
|
+
for(const p of phases)for(const l of p.links||[]){if(!byId[l])continue;const key=[p.id,l].sort().join('|');if(seenLinks.has(key))continue;seenLinks.add(key);linkPairs.push([byId[l],p])}
|
|
338
|
+
const traveled={}
|
|
339
|
+
for(const r of (b.runs||[])){
|
|
340
|
+
const path=(r.path||[])
|
|
341
|
+
for(let i=1;i<path.length;i++){
|
|
342
|
+
if(Date.now()-path[i].at>45*60e3)continue
|
|
343
|
+
traveled[path[i-1].c+'|'+path[i].c]={agent:r.agent,at:path[i].at}
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
const trails=(b.runs||[]).map(r=>{
|
|
347
|
+
const ids=[...(r.path||[]).map(x=>x.c)];if(r.componentId&&ids[ids.length-1]!==r.componentId)ids.push(r.componentId)
|
|
348
|
+
const pts=ids.map(id=>pos[id]).filter(Boolean).map(q=>`${q.nodeX+200},${q.nodeY+64}`)
|
|
349
|
+
return pts.length>1?`<polyline class="trail" points="${pts.join(' ')}" stroke="${agentColor(r.agent)}" style="opacity:${r.ended?0.2:0.55}"/>`:''
|
|
350
|
+
}).join('')
|
|
351
|
+
const edges=trails+phases.flatMap(p=>p.needs.map(need=>graphEdge(byId[need],p,pos,traveled[need+'|'+p.id]))).join('')+linkPairs.map(([a,b2])=>graphLinkEdge(a,b2,pos,traveled[a.id+'|'+b2.id]||traveled[b2.id+'|'+a.id])).join('')
|
|
352
|
+
const nodes=phases.map((p,i)=>graphNode(p,i,pos[p.id])).join('')
|
|
353
|
+
return edges+nodes
|
|
354
|
+
}
|
|
355
|
+
function regionCardHtml(b,L){
|
|
356
|
+
const w=L.width,h=L.height
|
|
357
|
+
const live=(b.runs||[]).filter(r=>!r.ended)
|
|
358
|
+
const sq=b.plan.filter(n=>n.level==='component').map((c,i)=>`<rect x="${40+i*30}" y="${h/2+4}" width="20" height="20" rx="5" fill="${c.status==='done'?'#3f7f57':c.status==='blocked'?'var(--danger)':'none'}" stroke="${c.status==='active'?'var(--accent)':'var(--faint)'}" stroke-width="2.5"/>`).join('')
|
|
359
|
+
const marks=live.map((r,i)=>{const ic=agentIcon(r.agent);return ic?`<g transform="translate(${40+i*34} ${h/2+44}) scale(1.1)"><path fill="${agentColor(r.agent)}" d="${ic}"/></g>`:''}).join('')
|
|
360
|
+
return`<text x="40" y="${h/2-36}" fill="var(--text)" font-size="44" font-weight="650" font-family="var(--font)" letter-spacing="-1">${esc(b.project)}</text>${sq}${marks}${!b.hasPlan?`<text x="40" y="${h/2+44}" fill="var(--faint)" font-size="16" font-family="var(--font)">nothing mapped yet</text><g class="setup-btn" onclick="setupBoard(${b.port})" style="cursor:pointer"><rect x="40" y="${h/2+64}" width="172" height="40" rx="11" fill="var(--accent-bg)" stroke="var(--accent)" stroke-width="1.2"/><text x="126" y="${h/2+89}" text-anchor="middle" fill="var(--accent)" font-size="15.5" font-weight="600" font-family="var(--font)">Set up the map</text></g>`:''}${live.length?`<text x="${w-40}" y="${h/2-36}" text-anchor="end" fill="var(--dim)" font-size="16" font-family="var(--font)">${live.length} running</text>`:''}`
|
|
361
|
+
}
|
|
362
|
+
const CARD_LOD=0.3
|
|
363
|
+
let _saved=null
|
|
364
|
+
function setBoardGlobals(b){_saved={M,BP};M={...b,session:{project:b.project},tree:(M&&M.port===b.port)?M.tree:[]};BP=String(b.port)}
|
|
365
|
+
function restoreBoardGlobals(){if(_saved){M=_saved.M;BP=_saved.BP;_saved=null}}
|
|
366
|
+
function renderGraph(){
|
|
367
|
+
const boards=boardsAll()
|
|
368
|
+
if(!boards.length)return
|
|
369
|
+
if(boards.length===1&&M&&(!M.hasPlan||isSkeletonPlan())){renderZero();return}
|
|
370
|
+
regions={};graphPos={}
|
|
371
|
+
const GAP=160,ROWMAX=3600;let cx=80,cy=80,rowH=0
|
|
372
|
+
let content=''
|
|
373
|
+
const realM=M,realBP=BP
|
|
374
|
+
for(const b of boards){
|
|
375
|
+
const eff=(realM&&b.port===realM.port)?{...realM,project:realM.session.project}:b
|
|
376
|
+
M=eff;BP=String(b.port)
|
|
377
|
+
const L=boardLayout(eff)
|
|
378
|
+
if(cx>80&&cx+L.width>ROWMAX){cx=80;cy+=rowH+GAP;rowH=0}
|
|
379
|
+
const ox=cx,oy=cy
|
|
380
|
+
regions[b.port]={ox,oy,w:L.width,h:L.height+56,project:eff.project||b.project}
|
|
381
|
+
for(const[id,q]of Object.entries(L.pos))graphPos[BP+':'+id]={...q,nodeX:q.nodeX+ox,nodeY:q.nodeY+56+oy}
|
|
382
|
+
const hot=eff.activity&&Date.now()-eff.activity.at<60000
|
|
383
|
+
const cardMode=graphZoom<CARD_LOD||!eff.hasPlan
|
|
384
|
+
content+=`<g transform="translate(${ox} ${oy})"><rect width="${L.width}" height="${L.height+56}" rx="26" fill="var(--region)" stroke="${hot?'rgba(233,162,59,.5)':'var(--line)'}" stroke-width="1.5"/><text x="28" y="38" fill="var(--faint)" font-size="15" font-weight="600" font-family="var(--mono)" letter-spacing=".08em">${esc((eff.project||'').toUpperCase())}${eff.planStale&&eff.planStale.stale?`<tspan fill="var(--accent)"> · PLAN BEHIND</tspan>`:''}</text>${cardMode?regionCardHtml(eff,L):`<g transform="translate(0 56)">${boardInnerHtml(eff,L)}</g>`}</g>`
|
|
385
|
+
cx+=L.width+GAP;rowH=Math.max(rowH,L.height+56)
|
|
386
|
+
}
|
|
387
|
+
M=realM;BP=realBP
|
|
388
|
+
const width=Math.max(...Object.values(regions).map(r=>r.ox+r.w))+80
|
|
389
|
+
const height=Math.max(...Object.values(regions).map(r=>r.oy+r.h))+80
|
|
390
|
+
graphSize={width,height}
|
|
391
|
+
minZoom=Math.max(0.06,Math.min(920/width,700/height)*0.75)
|
|
392
|
+
$('graph').innerHTML=`<svg class="graph-grid"><defs><pattern id="grid" width="24" height="24" patternUnits="userSpaceOnUse"><circle cx="1" cy="1" r="1" fill="var(--grid)"/></pattern></defs><rect width="100%" height="100%" fill="url(#grid)"/></svg><div class="graph-stage" id="graph-stage" onpointerdown="startPan(event)" onpointermove="movePan(event)" onpointerup="endPan(event)" onpointercancel="endPan(event)"><svg class="graph-svg"><title>World map</title><defs><marker id="arrow" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto"><path d="M0 0 10 5 0 10z" fill="var(--grid)"/></marker></defs><g class="world" id="world" transform="translate(${graphPan.x} ${graphPan.y}) scale(${graphZoom})">${content}</g></svg></div><div class="canvas-tools"><button class="icon" aria-label="Zoom out" onclick="zoomBy(.85)"><svg viewBox="0 0 24 24"><path d="M6 12h12"/></svg></button><span class="zoom" id="zoom">${Math.round(graphZoom*100)}%</span><button class="icon" aria-label="Zoom in" onclick="zoomBy(1.18)"><svg viewBox="0 0 24 24"><path d="M12 6v12M6 12h12"/></svg></button><span class="divider"></span><button class="icon" id="ov-activity" aria-label="Activity overlay" title="Activity overlay" onclick="toggleOverlay('activity')"><svg viewBox="0 0 24 24"><circle cx="12" cy="12" r="8"/><circle cx="12" cy="12" r="2.5" fill="currentColor" stroke="none"/></svg></button><button class="icon" id="ov-runs" aria-label="Runs overlay" title="Runs overlay" onclick="toggleOverlay('runs')"><svg viewBox="0 0 24 24"><rect x="4" y="5" width="16" height="6" rx="2"/><rect x="4" y="14" width="10" height="5" rx="2"/></svg></button><span class="divider"></span><button class="icon" aria-label="Fit world" title="Fit the whole world" onclick="fitWorld()"><svg viewBox="0 0 24 24"><rect x="3" y="3" width="7" height="7" rx="1.5"/><rect x="14" y="3" width="7" height="7" rx="1.5"/><rect x="3" y="14" width="7" height="7" rx="1.5"/><rect x="14" y="14" width="7" height="7" rx="1.5"/></svg></button><button class="icon" aria-label="Fit my repo" title="Fit this repo" onclick="fitGraph()"><svg viewBox="0 0 24 24"><path d="M9 4H4v5M15 4h5v5M20 15v5h-5M4 15v5h5"/></svg></button></div>`
|
|
393
|
+
renderMinimap();applyOverlays();$('graph-stage').addEventListener('wheel',onGraphWheel,{passive:false});if(graphNeedsFit){graphNeedsFit=false;requestAnimationFrame(fitGraph)}
|
|
394
|
+
}
|
|
395
|
+
function graphDetailHeight(p){const list=kids(p.id),openRows=list.filter(graphTaskIsOpen).reduce((a,t)=>a+16+Math.max(graphTaskDetails(t).rows.length,5)*20,0);const sessN=(M.runs||[]).filter(r=>r.componentId===p.id).reduce((a,r)=>a+(r.todos||[]).length,0)+((p.touchedAt&&Date.now()-p.touchedAt<60000&&!list.some(k=>k.status==='active'||k.status==='blocked'))?1:0);return (list.length+sessN)?(list.length+sessN)*44+Math.max(0,list.length+sessN-1)+openRows:64}
|
|
396
|
+
function graphNodeMetrics(p){const nodeW=400;let nodeH=128;if(fileMode&&(p.filesList||[]).length)nodeH=110+Math.ceil(Math.min(p.filesList.length,8)/2)*24+(p.filesList.length>8?30:10);if(!isUnfolded(p))return{w:nodeW,h:nodeH,nodeW,nodeH,detailW:0,detailH:0};const detailW=400,detailH=graphDetailHeight(p);return{w:detailW,h:nodeH+24+detailH,nodeW,nodeH,detailW,detailH}}
|
|
397
|
+
function graphEdge(source,target,pos,handoff){if(!source||!pos[source.id]||!pos[target.id])return'';const a=pos[source.id],b=pos[target.id],x1=a.nodeX+a.nodeW,y1=a.nodeY+a.nodeH/2,x2=b.nodeX,y2=b.nodeY+b.nodeH/2,bend=Math.max(48,(x2-x1)/2),active=source.status==='active'||target.status==='active',d=`M${x1} ${y1}C${x1+bend} ${y1} ${x2-bend} ${y2} ${x2} ${y2}`
|
|
398
|
+
if(handoff){const c=agentColor(handoff.agent),mx=(x1+x2)/2,my=(y1+y2)/2-10
|
|
399
|
+
return`<path class="edge handoff" d="${d}" style="stroke:${c}" marker-end="url(#arrow)"/><text x="${mx}" y="${my}" text-anchor="middle" fill="${c}" font-size="10.5" font-weight="650" font-family="var(--mono)">${esc(handoff.agent)} ⇢</text>`}
|
|
400
|
+
return`<path class="edge ${active?'active':''}" d="${d}" marker-end="url(#arrow)"/>`}
|
|
401
|
+
function graphLinkEdge(a,b,pos){const A=pos[a.id],B=pos[b.id];if(!A||!B)return''
|
|
402
|
+
let x1,y1,x2,y2,d
|
|
403
|
+
if(Math.abs(A.nodeX-B.nodeX)>24){const L=A.nodeX<B.nodeX?A:B,R=A.nodeX<B.nodeX?B:A;x1=L.nodeX+L.nodeW;y1=L.nodeY+L.nodeH/2;x2=R.nodeX;y2=R.nodeY+R.nodeH/2;const mx=(x1+x2)/2;d=`M${x1} ${y1}C${mx} ${y1} ${mx} ${y2} ${x2} ${y2}`}
|
|
404
|
+
else{const T=A.nodeY<B.nodeY?A:B,Bt=A.nodeY<B.nodeY?B:A;x1=T.nodeX+T.nodeW/2;y1=T.y+T.h;x2=Bt.nodeX+Bt.nodeW/2;y2=Bt.nodeY;const my=(y1+y2)/2;d=`M${x1} ${y1}C${x1} ${my} ${x2} ${my} ${x2} ${y2}`}
|
|
405
|
+
if(arguments[3]){const h=arguments[3],c=agentColor(h.agent)
|
|
406
|
+
return`<path class="edge handoff" d="${d}" style="stroke:${c}"/><text x="${(x1+x2)/2}" y="${(y1+y2)/2-8}" text-anchor="middle" fill="${c}" font-size="10.5" font-weight="650" font-family="var(--mono)">${esc(h.agent)} ⇢</text>`}
|
|
407
|
+
return`<path class="edge link" d="${d}"/>`}
|
|
408
|
+
function graphNode(p,index,pos){return collapsedGraphNode(p,index,pos)+(!fileMode&&isUnfolded(p)?expandedGraphNode(p,pos):'')}
|
|
409
|
+
function graphTaskIsOpen(task){if(openGraphTasks.has(ns(task.id)))return true;if(closedGraphTasks.has(ns(task.id)))return false;return task.status==='active'}
|
|
410
|
+
function graphTaskMarker(task,index){if(task.status==='done')return`<span class="task-capsule-badge" aria-hidden="true"><svg viewBox="0 0 24 24"><path d="M20 6 9 17l-5-5"/></svg></span>`;if(task.status==='blocked')return`<span class="task-capsule-badge blocked" aria-hidden="true">!</span>`;return`<span class="task-capsule-marker" aria-hidden="true"><svg viewBox="0 0 24 24"><circle class="ring-track" cx="12" cy="12" r="11"/><circle class="ring-live" cx="12" cy="12" r="11"/></svg><span>${index+1}</span></span>`}
|
|
411
|
+
function graphTaskDetails(task){const comp=M.plan.find(x=>x.id===task.parent),compActive=M.plan.some(x=>x.parent===task.parent&&x.status==='active')
|
|
412
|
+
const live=!!(comp&&(comp.recent||[]).length&&(task.status==='active'||task.status==='blocked'||(task.status==='done'&&!compActive)))
|
|
413
|
+
const rows=live?comp.recent.slice(0,4).map(a=>({label:a.file+(a.n>1?` ×${a.n}`:''),meta:ago(a.at)})):[]
|
|
414
|
+
if(task.tech)rows.push({label:task.tech,meta:''})
|
|
415
|
+
if(!rows.length)rows.push({label:'No activity recorded yet',meta:''})
|
|
416
|
+
return{rows,live,comp}}
|
|
417
|
+
function graphSessionRow(t,i,ended){const st=t.status==='completed'?'done':t.status==='in_progress'?'active':'pending'
|
|
418
|
+
const marker=st==='done'?`<span class="task-capsule-badge" aria-hidden="true"><svg viewBox="0 0 24 24"><path d="M20 6 9 17l-5-5"/></svg></span>`:`<span class="task-capsule-marker" aria-hidden="true"><svg viewBox="0 0 24 24"><circle class="ring-track" cx="12" cy="12" r="11"/><circle class="ring-live" cx="12" cy="12" r="11"/></svg><span>${i+1}</span></span>`
|
|
419
|
+
return`<div class="task-capsule sess ${st}${ended?' sess-ended':''}" role="listitem"><button class="task-capsule-button" type="button" style="cursor:default"><span class="task-capsule-marker-wrap">${marker}</span><span class="task-capsule-copy">${esc(t.content)}</span><span class="task-capsule-pill now">Session plan</span></button></div>`}
|
|
420
|
+
function graphTaskRow(task,index){const open=graphTaskIsOpen(task),details=graphTaskDetails(task),pill=task.status==='blocked'?'<span class="task-capsule-pill blocked">Blocked</span>':task.status==='done'?'<span class="task-capsule-pill">Completed</span>':task.status==='pending'&&task.src==='agent'?'<span class="task-capsule-pill now">Session plan</span>':task.status==='pending'&&task.src==='roadmap'?'<span class="task-capsule-pill backlog">Roadmap</span>':'',amount=task.status==='active'?'Editing now':'';return`<div class="task-capsule ${task.status} ${open?'open':''}" data-task="${ns(task.id)}" role="listitem" style="animation-delay:${index*80}ms"><button class="task-capsule-button" type="button" aria-expanded="${open}" onclick="toggleGraphTask('${ns(task.id)}',event)"><span class="task-capsule-marker-wrap">${graphTaskMarker(task,index)}</span><span class="task-capsule-copy">${esc(task.title)}</span>${task.by?`<span class="agent-chip${task.status==='done'?' faded':''}" style="color:${agentColor(task.by)}" title="${esc(task.by)}">${agentIcon(task.by)?`<svg viewBox="0 0 24 24" width="14" height="14"><path fill="${agentColor(task.by)}" d="${agentIcon(task.by)}"/></svg>`:agentGlyph(task.by)}</span>`:''}${amount?`<span class="task-capsule-amount">${amount}</span>`:''}${pill}<span class="task-capsule-chevron" aria-hidden="true"><svg viewBox="0 0 24 24"><path d="m6 9 6 6 6-6"/></svg></span></button><div class="task-capsule-detail"><div class="task-capsule-detail-clip"><div class="task-capsule-detail-grid"><span class="task-capsule-detail-line" aria-hidden="true"></span><div class="task-capsule-detail-list" data-dl-task="${ns(task.id)}" data-dl-comp="${task.parent?ns(task.parent):''}" data-tech="${esc(task.tech||'')}">${details.rows.map((detail,i)=>`<div class="task-capsule-detail-row" style="animation-delay:${120+i*80}ms"><span>${esc(detail.label)}</span><span class="task-capsule-detail-meta">${esc(detail.meta)}</span></div>`).join('')}</div></div></div></div></div>`}
|
|
421
|
+
function nodeStatusIcon(p,w){const cx=w-26,cy=26
|
|
422
|
+
if(p.kind==='human'&&p.status!=='done')return`<g><circle cx="${cx}" cy="${cy}" r="9" fill="var(--accent)"/><text x="${cx}" y="${cy+4}" text-anchor="middle" fill="#141414" font-size="12" font-weight="700" font-family="var(--font)">!</text><title>waiting on a human</title></g>`
|
|
423
|
+
if(p.status==='done')return`<g><circle cx="${cx}" cy="${cy}" r="9" fill="#3f7f57"/><path d="M${cx-4.5} ${cy} l3.2 3.2 l6-6.5" stroke="#fff" stroke-width="2.2" fill="none" stroke-linecap="round" stroke-linejoin="round"/></g>`
|
|
424
|
+
if(p.status==='active')return`<g><circle cx="${cx}" cy="${cy}" r="9" fill="none" stroke="var(--line)" stroke-width="2"/><circle class="node-spin" cx="${cx}" cy="${cy}" r="9" fill="none" stroke="var(--accent)" stroke-width="2" stroke-linecap="round" stroke-dasharray="16 41"/></g>`
|
|
425
|
+
if(p.status==='blocked')return`<g><circle cx="${cx}" cy="${cy}" r="9" fill="var(--danger)"/><text x="${cx}" y="${cy+4}" text-anchor="middle" fill="#fff" font-size="12" font-weight="700" font-family="var(--font)">!</text></g>`
|
|
426
|
+
return`<circle cx="${cx}" cy="${cy}" r="9" fill="none" stroke="var(--faint)" stroke-width="2"/>`}
|
|
427
|
+
function nodeAgentChips(p,w){const names=[...new Set(M.plan.filter(n=>n.parent===p.id&&(n.status==='active'||n.status==='blocked')&&n.by).map(n=>n.by))].slice(0,3)
|
|
428
|
+
return names.map((n,i)=>{const cx=w-26-24*(i+1),icon=agentIcon(n),c=agentColor(n)
|
|
429
|
+
const mark=icon?`<g transform="translate(${cx-6.5} ${19.5}) scale(0.5417)"><path fill="${c}" d="${icon}"/></g>`:`<text x="${cx}" y="30" text-anchor="middle" fill="${c}" font-size="11" font-weight="700" font-family="var(--font)">${agentGlyph(n)}</text>`
|
|
430
|
+
return`<g>${mark}<title>${esc(n)}</title></g>`}).join('')}
|
|
431
|
+
const FILE_ICONS={
|
|
432
|
+
code:'<path d="m9 8-4 4 4 4M15 8l4 4-4 4"/>',
|
|
433
|
+
image:'<rect x="3" y="5" width="18" height="14" rx="2"/><circle cx="9" cy="10" r="1.6"/><path d="m5 17 5-5 4 4 2-2 3 3"/>',
|
|
434
|
+
page:'<rect x="3" y="4" width="18" height="16" rx="2"/><path d="M3 9h18"/><path d="M6 6.5h.01M9 6.5h.01"/>',
|
|
435
|
+
doc:'<path d="M6 3h9l4 4v14H6z"/><path d="M9 11h7M9 15h7"/>',
|
|
436
|
+
data:'<path d="M9 4c-2 0-3 1-3 3v2c0 1-.7 2-2 2 1.3 0 2 1 2 2v3c0 2 1 3 3 3M15 4c2 0 3 1 3 3v2c0 1 .7 2 2 2-1.3 0-2 1-2 2v3c0 2-1 3-3 3"/>',
|
|
437
|
+
shell:'<rect x="3" y="4" width="18" height="16" rx="2"/><path d="m7 9 3 3-3 3M13 15h4"/>',
|
|
438
|
+
file:'<path d="M6 3h9l4 4v14H6z"/>'
|
|
439
|
+
}
|
|
440
|
+
function fileIcon(f){
|
|
441
|
+
const e=(f.split('.').pop()||'').toLowerCase()
|
|
442
|
+
if(['html','htm'].includes(e))return FILE_ICONS.page
|
|
443
|
+
if(['png','jpg','jpeg','gif','svg','webp','ico','icns'].includes(e))return FILE_ICONS.image
|
|
444
|
+
if(['py','js','mjs','ts','tsx','jsx','swift','go','rb','rs','c','cc','cpp','h','java','kt','css','scss'].includes(e))return FILE_ICONS.code
|
|
445
|
+
if(['md','txt','rst'].includes(e))return FILE_ICONS.doc
|
|
446
|
+
if(['json','yaml','yml','toml','plist','xml'].includes(e))return FILE_ICONS.data
|
|
447
|
+
if(['sh','bash','zsh','fish'].includes(e))return FILE_ICONS.shell
|
|
448
|
+
return FILE_ICONS.file
|
|
449
|
+
}
|
|
450
|
+
function fileFamily(f){
|
|
451
|
+
const base=f.split('/').pop(),di=base.lastIndexOf('.')
|
|
452
|
+
const ext=di>0?base.slice(di):'',stem=di>0?base.slice(0,di):base
|
|
453
|
+
const toks=stem.split(/[-_.]/)
|
|
454
|
+
while(toks.length>1&&/^(\d+x?\d*|dark|light|black|white|master|small|large|\dx|[0-9a-f]{6,})$/i.test(toks[toks.length-1]))toks.pop()
|
|
455
|
+
return toks.join('-')+ext
|
|
456
|
+
}
|
|
457
|
+
function fileConstellation(p,w){
|
|
458
|
+
const files=(p.filesList||[]).slice()
|
|
459
|
+
if(!files.length)return''
|
|
460
|
+
const hot=M.hotFiles||{}
|
|
461
|
+
const isHot=f=>hot[f]&&Date.now()-hot[f]<60000
|
|
462
|
+
// cluster variant families (3+ files sharing a stem) into one distinct row
|
|
463
|
+
const fam={}
|
|
464
|
+
for(const f of files)(fam[fileFamily(f)]=fam[fileFamily(f)]||[]).push(f)
|
|
465
|
+
const items=[]
|
|
466
|
+
for(const[key,members]of Object.entries(fam)){
|
|
467
|
+
if(members.length>=3){
|
|
468
|
+
const di=key.lastIndexOf('.'),label=(di>0?key.slice(0,di)+'-*'+key.slice(di):key+'-*')
|
|
469
|
+
items.push({label:`${label}`,count:members.length,files:members,heat:Math.max(...members.map(f=>hot[f]||0)),hot:members.some(isHot),icon:members[0]})
|
|
470
|
+
}else for(const f of members){
|
|
471
|
+
const base=f.split('/').pop()
|
|
472
|
+
items.push({label:base.length>21?base.slice(0,12)+'…'+base.slice(-7):base,count:1,files:[f],heat:hot[f]||0,hot:isHot(f),icon:f})
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
items.sort((a,b)=>b.heat-a.heat)
|
|
476
|
+
const shown=items.slice(0,8)
|
|
477
|
+
const restFiles=items.slice(8).reduce((a,x)=>a+x.count,0)
|
|
478
|
+
const run=(M.runs||[]).find(r=>!r.ended&&r.componentId===p.id)
|
|
479
|
+
const hottest=shown.filter(x=>x.hot).sort((a,b)=>b.heat-a.heat)[0]
|
|
480
|
+
let out=''
|
|
481
|
+
shown.forEach((it,i)=>{
|
|
482
|
+
const col=i%2,row=Math.floor(i/2),x=16+col*186,y=104+row*24
|
|
483
|
+
out+=`<g class="file-ic${it.hot?' hot':''}" transform="translate(${x-1} ${y-1}) scale(0.62)">${fileIcon(it.icon)}<title>${esc(it.files.join('\n'))}</title></g>`
|
|
484
|
+
out+=`<text class="file-label${it.hot?' hot':''}" x="${x+19}" y="${y+11}">${esc(it.label)}${it.count>1?` <tspan style="opacity:.6">×${it.count}</tspan>`:''}</text>`
|
|
485
|
+
if(it===hottest&&run){const ic=agentIcon(run.agent);if(ic)out+=`<g transform="translate(${x} ${y-15}) scale(0.5)"><path fill="${agentColor(run.agent)}" d="${ic}"/></g>`}
|
|
486
|
+
})
|
|
487
|
+
if(restFiles>0)out+=`<text class="file-label" x="16" y="${104+Math.ceil(shown.length/2)*24+16}" style="opacity:.55">+ ${restFiles} more files</text>`
|
|
488
|
+
return out
|
|
489
|
+
}
|
|
490
|
+
function handoffLabel(p,w,h){
|
|
491
|
+
const hs=(M.handoffs||[]).filter(x=>x.c===p.id&&Date.now()-x.at<10*60e3)
|
|
492
|
+
if(!hs.length)return''
|
|
493
|
+
const x=hs[hs.length-1]
|
|
494
|
+
return`<text class="handoff-label" x="${w-16}" y="${h-12}" text-anchor="end"><tspan fill="${agentColor(x.from)}">${esc(x.from)}</tspan><tspan fill="var(--faint)"> ⇄ </tspan><tspan fill="${agentColor(x.to)}">${esc(x.to)}</tspan></text>`
|
|
495
|
+
}
|
|
496
|
+
function collapsedGraphNode(p,index,pos){const list=kids(p.id),done=list.filter(n=>n.status==='done').length,progress=pct(done,list.length),label=p.title.length>28?`${p.title.slice(0,27)}…`:p.title,isOpen=isUnfolded(p),w=pos.nodeW,h=pos.nodeH,obsWord=p.status==='done'?'Revising':p.status==='blocked'?'Retrying':'Editing',chipN=new Set(M.plan.filter(n=>n.parent===p.id&&(n.status==='active'||n.status==='blocked')&&n.by).map(n=>n.by)).size;return`<g class="gnode ${p.status} ${p.kind==='knowledge'?'knowledge':p.kind==='human'?'human':''} ${isOpen?'selected':''}" data-id="${ns(p.id)}" transform="translate(${pos.nodeX} ${pos.nodeY})" role="button" tabindex="0" aria-expanded="${isOpen}" aria-label="${isOpen?'Hide':'Show'} tasks for ${esc(p.title)}" onclick="toggleGraphNode('${ns(p.id)}',event)" onkeydown="if(event.key==='Enter'||event.key===' '){event.preventDefault();toggleGraphNode('${ns(p.id)}',event)}"><rect class="node-box" width="${w}" height="${h}" rx="12"/><circle class="port" cx="0" cy="${h/2}" r="4"/><circle class="port" cx="${w}" cy="${h/2}" r="4"/><text class="kicker" x="16" y="24">${esc((p.tech||'#'+p.id).slice(0,Math.min(42,Math.floor((w-170)/6.8))))}</text><text class="obs-label obs-file" x="${w-16}" y="76" text-anchor="end" fill="var(--accent)" font-size="11" font-family="var(--mono)"></text>${nodeStatusIcon(p,w)}<circle class="obs-ring" cx="${w-26}" cy="26" r="13" fill="none" stroke="var(--accent)" stroke-width="2" stroke-linecap="round" stroke-dasharray="23 59"/><text class="obs-label" x="${w-44-24*Math.min(chipN,3)}" y="30" text-anchor="end" fill="var(--accent)" font-size="11" font-weight="600" font-family="var(--font)">${obsWord}</text>${nodeAgentChips(p,w)}<text class="node-title" x="16" y="52">${esc(label)}</text><text class="node-meta" x="16" y="76">${done} of ${list.length} tasks · ${p.status==='pending'&&done>0?'Underway':statusLabel(p.status)}</text><rect class="progress-bg" x="16" y="92" width="${w-32}" height="4" rx="2"/><rect class="progress" x="16" y="92" width="${(w-32)*progress/100}" height="4" rx="2"/>${fileMode?fileConstellation(p,w):`<text class="node-action" x="16" y="116">${isOpen?'HIDE TASKS ↑':'SHOW TASKS ↓'}</text>`}${p.url?`<g class="node-url" onclick="event.stopPropagation();window.open(${JSON.stringify(p.url)},'_blank')"><rect x="${w-96}" y="104" width="84" height="18" rx="5" fill="transparent"/><text x="${w-16}" y="117" text-anchor="end">OPEN ↗</text></g>`:''}${handoffLabel(p,w,h)}${list.some(k=>k.status==='pending'&&k.src==='agent')?`<text x="${w-16}" y="116" text-anchor="end" fill="#e08bb8" font-size="10" font-weight="700" font-family="var(--mono)" letter-spacing=".06em">SESSION PLAN</text>`:''}</g>`}
|
|
497
|
+
function expandedGraphNode(p,pos){
|
|
498
|
+
const list=kids(p.id),detailY=pos.nodeY+pos.nodeH+24,centerX=pos.nodeX+pos.nodeW/2
|
|
499
|
+
const sessRows=(M.runs||[]).filter(r=>r.componentId===p.id).flatMap(r=>(r.todos||[]).map((t,i)=>graphSessionRow(t,i,r.ended))).join('')
|
|
500
|
+
const liveNow=p.touchedAt&&Date.now()-p.touchedAt<60000,declaredKid=list.some(k=>k.status==='active'||k.status==='blocked')
|
|
501
|
+
const liveSess=(M.runs||[]).some(r=>r.componentId===p.id&&!r.ended&&(r.todos||[]).length)
|
|
502
|
+
const a0=(p.recent||[])[0]
|
|
503
|
+
const obsRow=liveNow&&!declaredKid&&!liveSess&&a0?`<div class="task-capsule active obsrow" role="listitem"><button class="task-capsule-button" type="button" style="cursor:default"><span class="task-capsule-marker-wrap"><span class="task-capsule-marker" aria-hidden="true"><svg viewBox="0 0 24 24"><circle class="ring-track" cx="12" cy="12" r="11"/><circle class="ring-live" cx="12" cy="12" r="11"/></svg></span></span><span class="task-capsule-copy" data-obs-copy="${ns(p.id)}">Revising · ${esc(a0.file.split('/').pop())}</span><span class="task-capsule-amount" data-obs-ago="${ns(p.id)}">${ago(a0.at)}</span></button></div>`:''
|
|
504
|
+
const tasks=(list.length||sessRows||obsRow)?`<div class="task-capsules list" role="list" aria-label="${esc(p.title)} tasks">${list.map(graphTaskRow).join('')}${sessRows}${obsRow}</div>`:'<div class="graph-task-empty">No tasks in this phase.</div>'
|
|
505
|
+
return`<path class="detail-tether" d="M${centerX} ${pos.nodeY+pos.nodeH}V${detailY}"/><g class="graph-detail" data-detail="${ns(p.id)}" transform="translate(${pos.x} ${detailY})"><foreignObject width="${pos.detailW}" height="${pos.detailH}"><div xmlns="http://www.w3.org/1999/xhtml" class="graph-task-card">${tasks}</div></foreignObject></g>`
|
|
506
|
+
}
|
|
507
|
+
function toggleGraphNode(id,event){event?.stopPropagation()
|
|
508
|
+
const boards=boardsAll(),ci=id.indexOf(':'),b=boards.find(x=>String(x.port)===id.slice(0,ci))
|
|
509
|
+
const p=b&&b.plan.find(x=>x.id===id.slice(ci+1)&&x.level==='component')
|
|
510
|
+
const auto=p&&(p.status==='active'||(p.touchedAt&&Date.now()-p.touchedAt<60000)||((b.runs||[]).some(r=>!r.ended&&r.componentId===p.id)))
|
|
511
|
+
if(openGraphDetails.has(id)){openGraphDetails.delete(id);if(auto)closedGraphDetails.add(id)}
|
|
512
|
+
else if(closedGraphDetails.has(id)){closedGraphDetails.delete(id);openGraphDetails.add(id)}
|
|
513
|
+
else if(auto)closedGraphDetails.add(id)
|
|
514
|
+
else openGraphDetails.add(id)
|
|
515
|
+
renderGraph();renderTimes()}
|
|
516
|
+
function toggleGraphTask(id,event){event?.stopPropagation();const ci=id.indexOf(':'),port=id.slice(0,ci),raw=id.slice(ci+1);const b=W.find(x=>String(x.port)===port)||M;const task=b&&b.plan.find(n=>n.id===raw&&n.level==='task');if(!task)return
|
|
517
|
+
const save=BP;BP=port
|
|
518
|
+
if(graphTaskIsOpen(task)){openGraphTasks.delete(id);closedGraphTasks.add(id)}else{closedGraphTasks.delete(id);openGraphTasks.add(id)}
|
|
519
|
+
BP=save;renderGraph();renderTimes()}
|
|
520
|
+
let zoomUnfolded=false,fileMode=false,cardMode=false
|
|
521
|
+
function updateTransform(){const world=$('world');if(world)world.setAttribute('transform',`translate(${graphPan.x} ${graphPan.y}) scale(${graphZoom})`);if($('zoom'))$('zoom').textContent=`${Math.round(graphZoom*100)}%`
|
|
522
|
+
let dirty=false
|
|
523
|
+
if(!zoomUnfolded&&graphZoom>=1.05){zoomUnfolded=true;for(const b of (W.length?W:(M?[M]:[])))for(const n of b.plan)if(n.level==='component')openGraphDetails.add(String(b.port)+':'+n.id);dirty=true}
|
|
524
|
+
else if(zoomUnfolded&&graphZoom<=0.85){zoomUnfolded=false;openGraphDetails.clear();dirty=true}
|
|
525
|
+
if(!fileMode&&graphZoom>=1.5){fileMode=true;dirty=true}
|
|
526
|
+
else if(fileMode&&graphZoom<=1.3){fileMode=false;dirty=true}
|
|
527
|
+
const cm=graphZoom<CARD_LOD;if(cm!==cardMode){cardMode=cm;dirty=true}
|
|
528
|
+
if(dirty){renderGraph();renderTimes()}
|
|
529
|
+
renderMinimapViewport()}
|
|
530
|
+
function fitGraph(){if(!M||!regions[M.port]){fitWorld();return}const r=regions[M.port];const stage=$('graph-stage');if(!stage)return;const box=stage.getBoundingClientRect();graphZoom=Math.max(.45,Math.min(1.2,(box.width-128)/r.w,(box.height-128)/r.h));graphPan={x:(box.width-r.w*graphZoom)/2-r.ox*graphZoom,y:(box.height-r.h*graphZoom)/2-r.oy*graphZoom};updateTransform()}
|
|
531
|
+
function zoomBy(factor,origin){const stage=$('graph-stage');if(!stage)return;const box=stage.getBoundingClientRect(),point=origin||{x:box.width/2,y:box.height/2},next=Math.max(minZoom,Math.min(1.8,graphZoom*factor)),wx=(point.x-graphPan.x)/graphZoom,wy=(point.y-graphPan.y)/graphZoom;graphPan={x:point.x-wx*next,y:point.y-wy*next};graphZoom=next;updateTransform()}
|
|
532
|
+
function onGraphWheel(e){e.preventDefault();const box=e.currentTarget.getBoundingClientRect();if(e.metaKey||e.ctrlKey)zoomBy(e.deltaY<0?1.08:.92,{x:e.clientX-box.left,y:e.clientY-box.top});else{graphPan.x-=e.deltaX;graphPan.y-=e.deltaY;updateTransform()}}
|
|
533
|
+
function startPan(e){if(e.target.closest('.gnode,.graph-detail'))return;const stage=$('graph-stage');stage.setPointerCapture(e.pointerId);stage.classList.add('dragging');panGesture={id:e.pointerId,x:e.clientX,y:e.clientY,panX:graphPan.x,panY:graphPan.y}}
|
|
534
|
+
function movePan(e){if(!panGesture||panGesture.id!==e.pointerId)return;graphPan={x:panGesture.panX+e.clientX-panGesture.x,y:panGesture.panY+e.clientY-panGesture.y};updateTransform()}
|
|
535
|
+
function endPan(e){if(!panGesture||panGesture.id!==e.pointerId)return;$('graph-stage')?.classList.remove('dragging');panGesture=null}
|
|
536
|
+
function copy(q){navigator.clipboard.writeText(q).then(()=>{const t=$('toast');t.textContent='Prompt copied — paste it to your agent in this repo';t.classList.add('show');setTimeout(()=>t.classList.remove('show'),2600)})}
|
|
537
|
+
function copyBackfill(){copy('Read the "agenttrail plan convention" section in CLAUDE.md or AGENTS.md. Then study this repo in trust order — the code and directory layout first (what exists), git log for what is actually recent, decision logs and any in-progress build/handoff docs for what is in flight, and README/roadmap prose LAST and only for open intent (founding docs rot; cross-check their claims against git log) — and rewrite PLAN.md as the real map of this codebase: components with stable {#id}s, needs:/links: edges between them, files: globs for the paths each owns, and verb-led concrete titles with tech: sublines. Keep it to 5-9 components no matter how big the repo — a component is a part one agent could own for a session, with its own doneness and at least one edge; anything smaller is a task inside one, and stable plumbing folds into a neighbor while active or planned areas get their own card. Verify every status against the CODE, not the docs — READMEs and roadmaps rot; mark [x] only after finding the implementing source or artifact, and cite that evidence on its tech: line (e.g. tech: TurnView.swift) so a human can audit every tick; if you cannot find evidence, leave it unchecked and say so in the decisions note. Statuses must be honest — [x] only for what verifiably exists, [~] only for what you are working on right now, everything else [ ]. Tag open tasks with an indented from: line naming provenance — from: agent when an in-progress build/handoff doc or your own declared intent claims it imminently, from: roadmap when sourced only from planning docs; omit when unsure. Record the backfill under ## decisions.')}
|
|
538
|
+
let lastRunsHtml=''
|
|
539
|
+
function renderRuns(){
|
|
540
|
+
const boards=boardsAll()
|
|
541
|
+
// AOS patch: pin open asks ("Wartet auf dich") oldest-first, with the decisions log, above the session cards
|
|
542
|
+
const asksHtml=selfAsks().map(askCardHtml).join('')+(askDecisions.length?decisionsHtml():'')
|
|
543
|
+
const runs=boards.flatMap(b=>(b.runs||[]).map(x=>({...x,__port:b.port,__project:b.project,__plan:b.plan})))
|
|
544
|
+
const groups=[]
|
|
545
|
+
for(const b of boards){
|
|
546
|
+
const rs=(b.runs||[]).map(x=>runs.find(r=>r.__port===b.port&&r.id===x.id)).filter(Boolean).sort((a,c)=>a.startedAt-c.startedAt)
|
|
547
|
+
let g=null
|
|
548
|
+
for(const r of rs){
|
|
549
|
+
if(g&&r.startedAt-(g.end||g.start)<=30*60e3){g.runs.push(r);g.end=Math.max(g.end,r.lastEventAt||r.startedAt)}
|
|
550
|
+
else{g={project:b.project,start:r.startedAt,end:r.lastEventAt||r.startedAt,runs:[r]};groups.push(g)}
|
|
551
|
+
}
|
|
552
|
+
}
|
|
553
|
+
groups.sort((a,c)=>a.start-c.start)
|
|
554
|
+
const cycleLabel=g=>{
|
|
555
|
+
const d=new Date(g.start),now=new Date()
|
|
556
|
+
const day=d.toDateString()===now.toDateString()?'Today':d.toLocaleDateString([],{weekday:'short',month:'short',day:'numeric'})
|
|
557
|
+
const live=g.runs.some(r=>!r.ended)
|
|
558
|
+
return `${live?'Now':day} · ${esc(g.project)} · ${g.runs.length} sessions`
|
|
559
|
+
}
|
|
560
|
+
const html=groups.map(g=>(g.runs.length>1?`<div class="run-cycle">${cycleLabel(g)}</div>`:'')+g.runs.map(r=>{
|
|
561
|
+
const cur=(r.todos||[]).find(t=>t.status==='in_progress')
|
|
562
|
+
const tool=r.currentTool||r.recentTools[0]
|
|
563
|
+
const toolLine=tool?`<div class="run-tool"><span class="tn">${esc(tool.name)}</span><span class="td">${esc(tool.detail||'')}</span><span class="ta t-ago" data-at="${tool.at}"></span></div>`:''
|
|
564
|
+
const comp=r.componentId?r.__plan.find(x=>x.id===r.componentId):null
|
|
565
|
+
const todos=(r.todos||[]).map(t=>`<div class="run-todo ${t.status}"><span class="box ${t.status==='completed'?'done':t.status==='in_progress'?'active':''}"></span><span>${esc(t.content)}</span></div>`).join('')
|
|
566
|
+
const hist=r.recentTools.slice(0,5).map(t=>`<div class="run-hist"><span class="hd">${esc(t.name)} · ${esc(t.detail||'')}</span><span>${t.ms>1200?Math.round(t.ms/1000)+'s':t.ms+'ms'}</span></div>`).join('')
|
|
567
|
+
const icon=agentIcon(r.agent)
|
|
568
|
+
const key=r.__port+':'+r.id
|
|
569
|
+
// AOS patch: SessionEnd runs show Finished, Stop runs show Wartet auf dich (Notification runs append their waiting reason), and a live run with no tool line shows Working…
|
|
570
|
+
return`<div class="run-card ${r.ended?'ended':''} ${openRuns.has(key)?'open':''}" data-run="${esc(key)}">
|
|
571
|
+
<div class="run-head"><span class="run-agent"><svg viewBox="0 0 24 24"><path fill="${agentColor(r.agent)}" d="${icon||''}"/></svg></span><span class="run-name">${esc(r.agent)}</span><span style="color:var(--faint);font-size:11.5px">· ${esc(r.__project)}</span><span class="run-live-dot"></span><span class="run-elapsed"><span class="t-ago" data-at="${r.startedAt}"></span>${comp?` · in <a href="#" style="color:var(--accent);text-decoration:none" onclick="event.preventDefault();event.stopPropagation();focusComp('${r.__port}:${comp.id}')">${esc(comp.title)}</a>`:''}</span></div>
|
|
572
|
+
${cur?`<div class="run-task">${esc(cur.content)}</div>`:r.ended&&r.done?`<div class="run-task" style="color:var(--dim)">Finished</div>`:r.ended?`<div class="run-task" style="color:var(--dim)">Wartet auf dich${r.waiting?': '+esc(r.waiting):''}</div>`:!toolLine?`<div class="run-task" style="color:var(--dim)">Working…</div>`:''}
|
|
573
|
+
${toolLine}
|
|
574
|
+
${(r.subagents||[]).filter(s=>!s.ended).map(s=>`<div class="run-sub"><span class="run-sub-dot"></span><span class="run-sub-name">${esc(s.name)}</span><span class="run-sub-ago t-ago" data-at="${s.startedAt}"></span></div>`).join('')}
|
|
575
|
+
<div class="run-detail">${(r.subagents||[]).filter(s=>s.ended).map(s=>`<div class="run-sub ended"><span class="run-sub-tick">✓</span><span class="run-sub-name">${esc(s.name)}</span></div>`).join('')}${todos}${hist?`<div style="margin-top:8px">${hist}</div>`:''}</div>
|
|
576
|
+
</div>`}).join('')).join('')
|
|
577
|
+
let hint=''
|
|
578
|
+
if(M){
|
|
579
|
+
const selfLive=(M.runs||[]).some(r=>!r.ended)
|
|
580
|
+
const hot=M.activity&&Date.now()-M.activity.at<120000
|
|
581
|
+
// AOS patch: AOS relays events via its global trail-relay hook, so the upstream init hint does not apply
|
|
582
|
+
if(M.hooksInstalled===false&&!M.aosPlan)hint=`<div class="run-card ended" style="cursor:default"><div class="run-task" style="margin-top:0;white-space:normal;color:var(--dim)">Watching file changes. For each session's live plan and tool calls too, run <b style="color:var(--text)">npx agenttrail init</b> here once — local only, gitignored.</div></div>`
|
|
583
|
+
}
|
|
584
|
+
const past=boards.flatMap(b=>(b.cycles||[]).map(c=>({...c,__project:b.project,__plan:b.plan||[]})))
|
|
585
|
+
.sort((a,c)=>c.start-a.start).slice(0,10)
|
|
586
|
+
.map(c=>{
|
|
587
|
+
const d=new Date(c.start)
|
|
588
|
+
const day=d.toDateString()===new Date().toDateString()?'Today':d.toLocaleDateString([],{weekday:'short',month:'short',day:'numeric'})
|
|
589
|
+
const stages=c.sessions.map(x=>{const comp=c.__plan.find(p=>p.id===x.componentId);return comp?comp.title:null}).filter(Boolean)
|
|
590
|
+
const path=stages.filter((t,i)=>t!==stages[i-1]).join(' → ')
|
|
591
|
+
return `<div class="cycle-row"><span class="cycle-when">${day}</span><span class="cycle-what">${esc(c.__project)} · ${c.sessions.length} session${c.sessions.length>1?'s':''}${path?` · ${esc(path)}`:''}</span></div>`
|
|
592
|
+
}).join('')
|
|
593
|
+
const out=asksHtml+html+hint+(past?`<div class="run-cycle" style="margin-top:10px">Past cycles</div>${past}`:'')
|
|
594
|
+
if(out!==lastRunsHtml){lastRunsHtml=out;$('runs').innerHTML=out}
|
|
595
|
+
}
|
|
596
|
+
$('runs').addEventListener('click',e=>{if(e.target.closest('[data-ask]'))return;const c=e.target.closest('.run-card');if(!c)return;const id=c.dataset.run
|
|
597
|
+
openRuns.has(id)?openRuns.delete(id):openRuns.add(id);renderRuns();renderTimes()})
|
|
598
|
+
const MM_W=150
|
|
599
|
+
function renderMinimap(){
|
|
600
|
+
const mm=$('minimap')
|
|
601
|
+
if(!mm||!graphSize.width||M.plan.filter(n=>n.level==='component').length<2){if(mm)mm.style.display='none';return}
|
|
602
|
+
mm.style.display=''
|
|
603
|
+
const sc=MM_W/graphSize.width,h=Math.max(40,Math.round(graphSize.height*sc))
|
|
604
|
+
const nodes=Object.entries(graphPos).map(([id,q])=>{
|
|
605
|
+
const c=M.plan.find(n=>n.id===id)
|
|
606
|
+
return`<rect class="mm-node ${c?c.status:''}" x="${q.nodeX*sc}" y="${q.nodeY*sc}" width="${q.nodeW*sc}" height="${Math.max(3,q.h*sc)}" rx="1.5"/>`
|
|
607
|
+
}).join('')
|
|
608
|
+
mm.innerHTML=`<svg width="${MM_W}" height="${h}" viewBox="0 0 ${MM_W} ${h}">${nodes}<rect class="mm-view" id="mm-view" x="0" y="0" width="10" height="10"/></svg>`
|
|
609
|
+
mm.onclick=e=>{const r=mm.getBoundingClientRect(),wx=(e.clientX-r.left)/sc,wy=(e.clientY-r.top)/sc
|
|
610
|
+
const stage=$('graph-stage'),box=stage.getBoundingClientRect()
|
|
611
|
+
graphPan={x:box.width/2-wx*graphZoom,y:box.height/2-wy*graphZoom};updateTransform()}
|
|
612
|
+
renderMinimapViewport()
|
|
613
|
+
}
|
|
614
|
+
function renderMinimapViewport(){
|
|
615
|
+
const v=document.getElementById('mm-view');if(!v||!graphSize.width)return
|
|
616
|
+
const stage=$('graph-stage');if(!stage)return
|
|
617
|
+
const box=stage.getBoundingClientRect(),sc=MM_W/graphSize.width
|
|
618
|
+
v.setAttribute('x',(-graphPan.x/graphZoom)*sc);v.setAttribute('y',(-graphPan.y/graphZoom)*sc)
|
|
619
|
+
v.setAttribute('width',(box.width/graphZoom)*sc);v.setAttribute('height',(box.height/graphZoom)*sc)
|
|
620
|
+
}
|
|
621
|
+
function focusComp(id){const p=graphPos[id];const stage=$('graph-stage');if(!p||!stage)return
|
|
622
|
+
const box=stage.getBoundingClientRect(),z=1.1
|
|
623
|
+
const world=$('world');if(world){world.style.transition='transform 600ms cubic-bezier(.23,1,.32,1)';setTimeout(()=>{if(world)world.style.transition=''},650)}
|
|
624
|
+
graphZoom=z;graphPan={x:box.width/2-(p.nodeX+200)*z,y:box.height/2-(p.nodeY+120)*z};updateTransform()}
|
|
625
|
+
function renderTimes(){if(M)renderBadge();if(M)renderObserved();for(const el of document.querySelectorAll('.t-ago'))el.textContent=ago(+el.dataset.at)}
|
|
626
|
+
function boardsAll(){return (W.length?W:(M?[M]:[])).map(b=>({...b,project:b.project||(b.session&&b.session.project)||''}))}
|
|
627
|
+
function renderObserved(){const now=Date.now(),boards=boardsAll()
|
|
628
|
+
const findB=port=>boards.find(x=>String(x.port)===port)
|
|
629
|
+
for(const g of document.querySelectorAll('g.gnode[data-id]')){const key=g.dataset.id,ci=key.indexOf(':'),b=findB(key.slice(0,ci));if(!b)continue
|
|
630
|
+
const comp=b.plan.find(x=>x.id===key.slice(ci+1)&&x.level==='component');if(!comp)continue
|
|
631
|
+
const live=comp.touchedAt&&now-comp.touchedAt<60000
|
|
632
|
+
g.classList.toggle('obs-live',!!live)
|
|
633
|
+
const ft=g.querySelector('.obs-file'),a=(comp.recent||[])[0]
|
|
634
|
+
if(ft)ft.textContent=live&&a?`${a.file.split('/').pop()}${a.n>1?` ×${a.n}`:''} · ${ago(a.at)}`:''}
|
|
635
|
+
for(const g of document.querySelectorAll('g.graph-detail[data-detail]')){const key=g.dataset.detail,ci=key.indexOf(':'),port=key.slice(0,ci),b=findB(port);if(!b)continue
|
|
636
|
+
const raw=key.slice(ci+1),comp=b.plan.find(x=>x.id===raw);if(!comp)continue
|
|
637
|
+
const kidsOf=b.plan.filter(x=>x.parent===raw)
|
|
638
|
+
const declared=kidsOf.some(x=>x.status==='active'||x.status==='blocked')
|
|
639
|
+
const live=comp.touchedAt&&now-comp.touchedAt<60000
|
|
640
|
+
const inferred=live&&!declared?(kidsOf.find(x=>x.status!=='done')||null):null
|
|
641
|
+
for(const cap of g.querySelectorAll('.task-capsule[data-task]'))cap.classList.toggle('obs-spin',!!inferred&&cap.dataset.task===port+':'+inferred.id)
|
|
642
|
+
for(const el of g.querySelectorAll('[data-dl-task]')){const traw=el.dataset.dlTask.slice(el.dataset.dlTask.indexOf(':')+1)
|
|
643
|
+
const t=kidsOf.find(x=>x.id===traw);if(!t)continue
|
|
644
|
+
const isLive=!!(comp.recent||[]).length&&(t.status==='active'||t.status==='blocked'||(inferred&&t.id===inferred.id)||(live&&!declared&&t.status==='done'&&!inferred))
|
|
645
|
+
if(isLive){const rows=(comp.recent||[]).slice(0,4).map(a=>`<div class="task-capsule-detail-row"><span>${esc(a.file)}${a.n>1?` ×${a.n}`:''}</span><span class="task-capsule-detail-meta">${ago(a.at)}</span></div>`)
|
|
646
|
+
if(el.dataset.tech)rows.push(`<div class="task-capsule-detail-row"><span style="opacity:.6">${esc(el.dataset.tech)}</span><span class="task-capsule-detail-meta"></span></div>`)
|
|
647
|
+
el.innerHTML=rows.join('');el.dataset.obsLines='1'}
|
|
648
|
+
else if(el.dataset.obsLines){const rows=[]
|
|
649
|
+
if(el.dataset.tech)rows.push(`<div class="task-capsule-detail-row"><span>${esc(el.dataset.tech)}</span><span class="task-capsule-detail-meta"></span></div>`)
|
|
650
|
+
if(!rows.length)rows.push('<div class="task-capsule-detail-row"><span>No activity recorded yet</span><span class="task-capsule-detail-meta"></span></div>')
|
|
651
|
+
el.innerHTML=rows.join('');delete el.dataset.obsLines}}}
|
|
652
|
+
for(const el of document.querySelectorAll('[data-obs-copy]')){const key=el.dataset.obsCopy,ci=key.indexOf(':'),b=findB(key.slice(0,ci))
|
|
653
|
+
const comp=b&&b.plan.find(x=>x.id===key.slice(ci+1)),a=comp&&(comp.recent||[])[0]
|
|
654
|
+
if(a)el.textContent=`Revising · ${a.file.split('/').pop()}${a.n>1?` ×${a.n}`:''}`}
|
|
655
|
+
for(const el of document.querySelectorAll('[data-obs-ago]')){const key=el.dataset.obsAgo,ci=key.indexOf(':'),b=findB(key.slice(0,ci))
|
|
656
|
+
const comp=b&&b.plan.find(x=>x.id===key.slice(ci+1)),a=comp&&(comp.recent||[])[0]
|
|
657
|
+
if(a)el.textContent=ago(a.at)}}
|
|
658
|
+
window.addEventListener('resize',()=>{renderMinimapViewport()})
|
|
659
|
+
</script>
|
|
660
|
+
</body>
|
|
661
|
+
</html>
|