@agentdeck/bridge 0.2.2 → 1.0.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/assets/fm-helper/agentdeck-fm-helper +0 -0
- package/assets/fonts/IBMPlexSans-Bold.ttf +0 -0
- package/assets/fonts/IBMPlexSans-Regular.ttf +0 -0
- package/assets/fonts/JetBrainsMono-Bold.ttf +0 -0
- package/assets/fonts/JetBrainsMono-Regular.ttf +0 -0
- package/assets/fonts/LICENSES.md +22 -0
- package/dist/adapters/claude-code.d.ts +6 -0
- package/dist/adapters/claude-code.d.ts.map +1 -1
- package/dist/adapters/claude-code.js +8 -0
- package/dist/adapters/claude-code.js.map +1 -1
- package/dist/adapters/openclaw.d.ts +24 -0
- package/dist/adapters/openclaw.d.ts.map +1 -1
- package/dist/adapters/openclaw.js +291 -35
- package/dist/adapters/openclaw.js.map +1 -1
- package/dist/adapters/opencode-adapter.d.ts +59 -0
- package/dist/adapters/opencode-adapter.d.ts.map +1 -1
- package/dist/adapters/opencode-adapter.js +297 -35
- package/dist/adapters/opencode-adapter.js.map +1 -1
- package/dist/adb-reverse.d.ts +1 -1
- package/dist/adb-reverse.js +1 -1
- package/dist/antigravity-local.d.ts +1 -0
- package/dist/antigravity-local.d.ts.map +1 -1
- package/dist/antigravity-local.js +33 -0
- package/dist/antigravity-local.js.map +1 -1
- package/dist/apme/adapters/claude-hook.d.ts.map +1 -1
- package/dist/apme/adapters/claude-hook.js +5 -0
- package/dist/apme/adapters/claude-hook.js.map +1 -1
- package/dist/apme/adapters/codex-hook.d.ts +17 -0
- package/dist/apme/adapters/codex-hook.d.ts.map +1 -0
- package/dist/apme/adapters/codex-hook.js +95 -0
- package/dist/apme/adapters/codex-hook.js.map +1 -0
- package/dist/apme/adapters/codex-turn-manager.d.ts +86 -0
- package/dist/apme/adapters/codex-turn-manager.d.ts.map +1 -0
- package/dist/apme/adapters/codex-turn-manager.js +496 -0
- package/dist/apme/adapters/codex-turn-manager.js.map +1 -0
- package/dist/apme/adapters/openclaw-hook.d.ts +99 -0
- package/dist/apme/adapters/openclaw-hook.d.ts.map +1 -0
- package/dist/apme/adapters/openclaw-hook.js +234 -0
- package/dist/apme/adapters/openclaw-hook.js.map +1 -0
- package/dist/apme/adapters/opencode-hook.d.ts +56 -0
- package/dist/apme/adapters/opencode-hook.d.ts.map +1 -0
- package/dist/apme/adapters/opencode-hook.js +182 -0
- package/dist/apme/adapters/opencode-hook.js.map +1 -0
- package/dist/apme/adapters/timeline.d.ts.map +1 -1
- package/dist/apme/adapters/timeline.js +6 -1
- package/dist/apme/adapters/timeline.js.map +1 -1
- package/dist/apme/antigravity-transcript.d.ts +18 -0
- package/dist/apme/antigravity-transcript.d.ts.map +1 -0
- package/dist/apme/antigravity-transcript.js +94 -0
- package/dist/apme/antigravity-transcript.js.map +1 -0
- package/dist/apme/classify-turn.d.ts +13 -0
- package/dist/apme/classify-turn.d.ts.map +1 -0
- package/dist/apme/classify-turn.js +49 -0
- package/dist/apme/classify-turn.js.map +1 -0
- package/dist/apme/claude-transcript-reader.d.ts +10 -0
- package/dist/apme/claude-transcript-reader.d.ts.map +1 -1
- package/dist/apme/claude-transcript-reader.js +35 -0
- package/dist/apme/claude-transcript-reader.js.map +1 -1
- package/dist/apme/collector.d.ts +133 -6
- package/dist/apme/collector.d.ts.map +1 -1
- package/dist/apme/collector.js +471 -23
- package/dist/apme/collector.js.map +1 -1
- package/dist/apme/dashboard-html.d.ts.map +1 -1
- package/dist/apme/dashboard-html.js +117 -11
- package/dist/apme/dashboard-html.js.map +1 -1
- package/dist/apme/http.d.ts.map +1 -1
- package/dist/apme/http.js +52 -6
- package/dist/apme/http.js.map +1 -1
- package/dist/apme/index.d.ts +26 -5
- package/dist/apme/index.d.ts.map +1 -1
- package/dist/apme/index.js +146 -9
- package/dist/apme/index.js.map +1 -1
- package/dist/apme/judge-detect.d.ts +26 -0
- package/dist/apme/judge-detect.d.ts.map +1 -0
- package/dist/apme/judge-detect.js +69 -0
- package/dist/apme/judge-detect.js.map +1 -0
- package/dist/apme/pareto.d.ts +39 -0
- package/dist/apme/pareto.d.ts.map +1 -0
- package/dist/apme/pareto.js +59 -0
- package/dist/apme/pareto.js.map +1 -0
- package/dist/apme/recommend.d.ts +3 -0
- package/dist/apme/recommend.d.ts.map +1 -1
- package/dist/apme/recommend.js +43 -1
- package/dist/apme/recommend.js.map +1 -1
- package/dist/apme/runner.d.ts +99 -4
- package/dist/apme/runner.d.ts.map +1 -1
- package/dist/apme/runner.js +743 -72
- package/dist/apme/runner.js.map +1 -1
- package/dist/apme/sample-to-timeline.d.ts +26 -0
- package/dist/apme/sample-to-timeline.d.ts.map +1 -0
- package/dist/apme/sample-to-timeline.js +95 -0
- package/dist/apme/sample-to-timeline.js.map +1 -0
- package/dist/apme/scorers/index.d.ts +47 -0
- package/dist/apme/scorers/index.d.ts.map +1 -0
- package/dist/apme/scorers/index.js +96 -0
- package/dist/apme/scorers/index.js.map +1 -0
- package/dist/apme/settings.d.ts +17 -11
- package/dist/apme/settings.d.ts.map +1 -1
- package/dist/apme/settings.js +30 -9
- package/dist/apme/settings.js.map +1 -1
- package/dist/apme/store.d.ts +33 -1
- package/dist/apme/store.d.ts.map +1 -1
- package/dist/apme/store.js +334 -25
- package/dist/apme/store.js.map +1 -1
- package/dist/awaiting-overlay.d.ts +112 -0
- package/dist/awaiting-overlay.d.ts.map +1 -0
- package/dist/awaiting-overlay.js +179 -0
- package/dist/awaiting-overlay.js.map +1 -0
- package/dist/ble-sync-spawn.d.ts +63 -0
- package/dist/ble-sync-spawn.d.ts.map +1 -0
- package/dist/ble-sync-spawn.js +196 -0
- package/dist/ble-sync-spawn.js.map +1 -0
- package/dist/bridge-core.d.ts +34 -1
- package/dist/bridge-core.d.ts.map +1 -1
- package/dist/bridge-core.js +207 -24
- package/dist/bridge-core.js.map +1 -1
- package/dist/broadcast.d.ts +3 -0
- package/dist/broadcast.d.ts.map +1 -0
- package/dist/broadcast.js +122 -0
- package/dist/broadcast.js.map +1 -0
- package/dist/check-deps.d.ts.map +1 -1
- package/dist/check-deps.js +25 -9
- package/dist/check-deps.js.map +1 -1
- package/dist/claude-permission-rules.d.ts +55 -0
- package/dist/claude-permission-rules.d.ts.map +1 -0
- package/dist/claude-permission-rules.js +197 -0
- package/dist/claude-permission-rules.js.map +1 -0
- package/dist/cli.d.ts +15 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +1030 -93
- package/dist/cli.js.map +1 -1
- package/dist/codex-auth.d.ts +21 -0
- package/dist/codex-auth.d.ts.map +1 -1
- package/dist/codex-auth.js +52 -1
- package/dist/codex-auth.js.map +1 -1
- package/dist/codex-output-parser.d.ts +8 -0
- package/dist/codex-output-parser.d.ts.map +1 -1
- package/dist/codex-output-parser.js +110 -25
- package/dist/codex-output-parser.js.map +1 -1
- package/dist/codex-rate-limits.d.ts +16 -0
- package/dist/codex-rate-limits.d.ts.map +1 -0
- package/dist/codex-rate-limits.js +197 -0
- package/dist/codex-rate-limits.js.map +1 -0
- package/dist/codex-rollout-response.d.ts +31 -0
- package/dist/codex-rollout-response.d.ts.map +1 -0
- package/dist/codex-rollout-response.js +135 -0
- package/dist/codex-rollout-response.js.map +1 -0
- package/dist/d200h/image-renderer.d.ts +4 -16
- package/dist/d200h/image-renderer.d.ts.map +1 -1
- package/dist/d200h/image-renderer.js +46 -212
- package/dist/d200h/image-renderer.js.map +1 -1
- package/dist/daemon-server.d.ts +85 -0
- package/dist/daemon-server.d.ts.map +1 -1
- package/dist/daemon-server.js +1975 -159
- package/dist/daemon-server.js.map +1 -1
- package/dist/daemon.js +15 -6
- package/dist/daemon.js.map +1 -1
- package/dist/display-dim.d.ts +9 -0
- package/dist/display-dim.d.ts.map +1 -0
- package/dist/display-dim.js +53 -0
- package/dist/display-dim.js.map +1 -0
- package/dist/display-monitor.d.ts +15 -2
- package/dist/display-monitor.d.ts.map +1 -1
- package/dist/display-monitor.js +104 -26
- package/dist/display-monitor.js.map +1 -1
- package/dist/esp32-serial.d.ts +190 -1
- package/dist/esp32-serial.d.ts.map +1 -1
- package/dist/esp32-serial.js +1090 -156
- package/dist/esp32-serial.js.map +1 -1
- package/dist/fallback-task-timeline.d.ts +42 -0
- package/dist/fallback-task-timeline.d.ts.map +1 -0
- package/dist/fallback-task-timeline.js +119 -0
- package/dist/fallback-task-timeline.js.map +1 -0
- package/dist/foundation-models-helper.d.ts +11 -0
- package/dist/foundation-models-helper.d.ts.map +1 -0
- package/dist/foundation-models-helper.js +241 -0
- package/dist/foundation-models-helper.js.map +1 -0
- package/dist/hook-migration.js +1 -1
- package/dist/hook-migration.js.map +1 -1
- package/dist/hook-server.d.ts +2 -3
- package/dist/hook-server.d.ts.map +1 -1
- package/dist/hook-server.js +23 -10
- package/dist/hook-server.js.map +1 -1
- package/dist/idotmatrix/idotmatrix-daemon-sync.d.ts +33 -0
- package/dist/idotmatrix/idotmatrix-daemon-sync.d.ts.map +1 -0
- package/dist/idotmatrix/idotmatrix-daemon-sync.js +164 -0
- package/dist/idotmatrix/idotmatrix-daemon-sync.js.map +1 -0
- package/dist/idotmatrix/idotmatrix-discover.d.ts +18 -0
- package/dist/idotmatrix/idotmatrix-discover.d.ts.map +1 -0
- package/dist/idotmatrix/idotmatrix-discover.js +85 -0
- package/dist/idotmatrix/idotmatrix-discover.js.map +1 -0
- package/dist/idotmatrix/idotmatrix-settings.d.ts +20 -0
- package/dist/idotmatrix/idotmatrix-settings.d.ts.map +1 -0
- package/dist/idotmatrix/idotmatrix-settings.js +57 -0
- package/dist/idotmatrix/idotmatrix-settings.js.map +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +217 -179
- package/dist/index.js.map +1 -1
- package/dist/log-stream.d.ts +15 -16
- package/dist/log-stream.d.ts.map +1 -1
- package/dist/log-stream.js +18 -96
- package/dist/log-stream.js.map +1 -1
- package/dist/logger.d.ts +3 -1
- package/dist/logger.d.ts.map +1 -1
- package/dist/logger.js +18 -5
- package/dist/logger.js.map +1 -1
- package/dist/mdns.d.ts +17 -0
- package/dist/mdns.d.ts.map +1 -1
- package/dist/mdns.js +52 -18
- package/dist/mdns.js.map +1 -1
- package/dist/model-catalog.d.ts +1 -1
- package/dist/model-catalog.d.ts.map +1 -1
- package/dist/model-catalog.js +2 -4
- package/dist/model-catalog.js.map +1 -1
- package/dist/modules/adb-module.d.ts +1 -1
- package/dist/modules/adb-module.d.ts.map +1 -1
- package/dist/modules/adb-module.js +4 -3
- package/dist/modules/adb-module.js.map +1 -1
- package/dist/modules/broadcast-module.d.ts +17 -0
- package/dist/modules/broadcast-module.d.ts.map +1 -0
- package/dist/modules/broadcast-module.js +26 -0
- package/dist/modules/broadcast-module.js.map +1 -0
- package/dist/modules/d200h-module.d.ts +22 -0
- package/dist/modules/d200h-module.d.ts.map +1 -1
- package/dist/modules/d200h-module.js +200 -65
- package/dist/modules/d200h-module.js.map +1 -1
- package/dist/modules/idotmatrix-module.d.ts +9 -0
- package/dist/modules/idotmatrix-module.d.ts.map +1 -0
- package/dist/modules/idotmatrix-module.js +46 -0
- package/dist/modules/idotmatrix-module.js.map +1 -0
- package/dist/modules/index.d.ts +3 -1
- package/dist/modules/index.d.ts.map +1 -1
- package/dist/modules/index.js +9 -3
- package/dist/modules/index.js.map +1 -1
- package/dist/modules/pixoo-module.d.ts.map +1 -1
- package/dist/modules/pixoo-module.js +14 -4
- package/dist/modules/pixoo-module.js.map +1 -1
- package/dist/modules/serial-module.d.ts +4 -0
- package/dist/modules/serial-module.d.ts.map +1 -1
- package/dist/modules/serial-module.js +14 -12
- package/dist/modules/serial-module.js.map +1 -1
- package/dist/modules/timebox-module.d.ts +9 -0
- package/dist/modules/timebox-module.d.ts.map +1 -0
- package/dist/modules/timebox-module.js +46 -0
- package/dist/modules/timebox-module.js.map +1 -0
- package/dist/modules/trmnl-module.d.ts +38 -0
- package/dist/modules/trmnl-module.d.ts.map +1 -0
- package/dist/modules/trmnl-module.js +111 -0
- package/dist/modules/trmnl-module.js.map +1 -0
- package/dist/modules/types.d.ts +4 -2
- package/dist/modules/types.d.ts.map +1 -1
- package/dist/observed-steering.d.ts +93 -0
- package/dist/observed-steering.d.ts.map +1 -0
- package/dist/observed-steering.js +247 -0
- package/dist/observed-steering.js.map +1 -0
- package/dist/openclaw-session.d.ts +18 -0
- package/dist/openclaw-session.d.ts.map +1 -0
- package/dist/openclaw-session.js +34 -0
- package/dist/openclaw-session.js.map +1 -0
- package/dist/opencode-steering.d.ts +36 -0
- package/dist/opencode-steering.d.ts.map +1 -0
- package/dist/opencode-steering.js +84 -0
- package/dist/opencode-steering.js.map +1 -0
- package/dist/output-parser.d.ts +30 -0
- package/dist/output-parser.d.ts.map +1 -1
- package/dist/output-parser.js +172 -18
- package/dist/output-parser.js.map +1 -1
- package/dist/passive-observer.d.ts +32 -0
- package/dist/passive-observer.d.ts.map +1 -1
- package/dist/passive-observer.js +288 -49
- package/dist/passive-observer.js.map +1 -1
- package/dist/permission-resolver.d.ts +55 -0
- package/dist/permission-resolver.d.ts.map +1 -0
- package/dist/permission-resolver.js +112 -0
- package/dist/permission-resolver.js.map +1 -0
- package/dist/pixoo/micro-glyphs.d.ts +18 -0
- package/dist/pixoo/micro-glyphs.d.ts.map +1 -0
- package/dist/pixoo/micro-glyphs.js +143 -0
- package/dist/pixoo/micro-glyphs.js.map +1 -0
- package/dist/pixoo/official-dot-glyphs.generated.d.ts +8 -0
- package/dist/pixoo/official-dot-glyphs.generated.d.ts.map +1 -0
- package/dist/pixoo/official-dot-glyphs.generated.js +247 -0
- package/dist/pixoo/official-dot-glyphs.generated.js.map +1 -0
- package/dist/pixoo/pixoo-bridge.d.ts +16 -8
- package/dist/pixoo/pixoo-bridge.d.ts.map +1 -1
- package/dist/pixoo/pixoo-bridge.js +127 -35
- package/dist/pixoo/pixoo-bridge.js.map +1 -1
- package/dist/pixoo/pixoo-camera.d.ts +19 -3
- package/dist/pixoo/pixoo-camera.d.ts.map +1 -1
- package/dist/pixoo/pixoo-camera.js +62 -27
- package/dist/pixoo/pixoo-camera.js.map +1 -1
- package/dist/pixoo/pixoo-client.d.ts +13 -3
- package/dist/pixoo/pixoo-client.d.ts.map +1 -1
- package/dist/pixoo/pixoo-client.js +51 -22
- package/dist/pixoo/pixoo-client.js.map +1 -1
- package/dist/pixoo/pixoo-discover.d.ts +37 -0
- package/dist/pixoo/pixoo-discover.d.ts.map +1 -0
- package/dist/pixoo/pixoo-discover.js +105 -0
- package/dist/pixoo/pixoo-discover.js.map +1 -0
- package/dist/pixoo/pixoo-renderer.d.ts +27 -3
- package/dist/pixoo/pixoo-renderer.d.ts.map +1 -1
- package/dist/pixoo/pixoo-renderer.js +533 -135
- package/dist/pixoo/pixoo-renderer.js.map +1 -1
- package/dist/pixoo/pixoo-settings.d.ts +6 -0
- package/dist/pixoo/pixoo-settings.d.ts.map +1 -1
- package/dist/pixoo/pixoo-settings.js +9 -0
- package/dist/pixoo/pixoo-settings.js.map +1 -1
- package/dist/pixoo/pixoo-sprites.d.ts +84 -11
- package/dist/pixoo/pixoo-sprites.d.ts.map +1 -1
- package/dist/pixoo/pixoo-sprites.js +620 -209
- package/dist/pixoo/pixoo-sprites.js.map +1 -1
- package/dist/pty-manager.d.ts.map +1 -1
- package/dist/pty-manager.js +8 -2
- package/dist/pty-manager.js.map +1 -1
- package/dist/review-runner.d.ts +113 -0
- package/dist/review-runner.d.ts.map +1 -0
- package/dist/review-runner.js +428 -0
- package/dist/review-runner.js.map +1 -0
- package/dist/session-activity.d.ts +14 -0
- package/dist/session-activity.d.ts.map +1 -0
- package/dist/session-activity.js +160 -0
- package/dist/session-activity.js.map +1 -0
- package/dist/session-aggregator.d.ts +25 -0
- package/dist/session-aggregator.d.ts.map +1 -1
- package/dist/session-aggregator.js +48 -9
- package/dist/session-aggregator.js.map +1 -1
- package/dist/session-focus-relay.d.ts.map +1 -1
- package/dist/session-focus-relay.js +7 -1
- package/dist/session-focus-relay.js.map +1 -1
- package/dist/session-registry.d.ts +74 -3
- package/dist/session-registry.d.ts.map +1 -1
- package/dist/session-registry.js +140 -8
- package/dist/session-registry.js.map +1 -1
- package/dist/session-timeline-relay.d.ts.map +1 -1
- package/dist/session-timeline-relay.js +13 -4
- package/dist/session-timeline-relay.js.map +1 -1
- package/dist/session-transcript-timeline.d.ts +49 -0
- package/dist/session-transcript-timeline.d.ts.map +1 -0
- package/dist/session-transcript-timeline.js +276 -0
- package/dist/session-transcript-timeline.js.map +1 -0
- package/dist/state-machine.d.ts +6 -1
- package/dist/state-machine.d.ts.map +1 -1
- package/dist/state-machine.js +86 -28
- package/dist/state-machine.js.map +1 -1
- package/dist/timebox/timebox-daemon-sync.d.ts +19 -0
- package/dist/timebox/timebox-daemon-sync.d.ts.map +1 -0
- package/dist/timebox/timebox-daemon-sync.js +135 -0
- package/dist/timebox/timebox-daemon-sync.js.map +1 -0
- package/dist/timebox/timebox-discover.d.ts +19 -0
- package/dist/timebox/timebox-discover.d.ts.map +1 -0
- package/dist/timebox/timebox-discover.js +87 -0
- package/dist/timebox/timebox-discover.js.map +1 -0
- package/dist/timebox/timebox-settings.d.ts +32 -0
- package/dist/timebox/timebox-settings.d.ts.map +1 -0
- package/dist/timebox/timebox-settings.js +80 -0
- package/dist/timebox/timebox-settings.js.map +1 -0
- package/dist/timeline-store.d.ts +112 -3
- package/dist/timeline-store.d.ts.map +1 -1
- package/dist/timeline-store.js +460 -14
- package/dist/timeline-store.js.map +1 -1
- package/dist/timeline-summarizer.d.ts.map +1 -1
- package/dist/timeline-summarizer.js +29 -5
- package/dist/timeline-summarizer.js.map +1 -1
- package/dist/trmnl/byos-server.d.ts +39 -0
- package/dist/trmnl/byos-server.d.ts.map +1 -0
- package/dist/trmnl/byos-server.js +301 -0
- package/dist/trmnl/byos-server.js.map +1 -0
- package/dist/trmnl/frame-cache.d.ts +37 -0
- package/dist/trmnl/frame-cache.d.ts.map +1 -0
- package/dist/trmnl/frame-cache.js +157 -0
- package/dist/trmnl/frame-cache.js.map +1 -0
- package/dist/trmnl/image-renderer.d.ts +27 -0
- package/dist/trmnl/image-renderer.d.ts.map +1 -0
- package/dist/trmnl/image-renderer.js +235 -0
- package/dist/trmnl/image-renderer.js.map +1 -0
- package/dist/trmnl/trmnl-settings.d.ts +96 -0
- package/dist/trmnl/trmnl-settings.d.ts.map +1 -0
- package/dist/trmnl/trmnl-settings.js +177 -0
- package/dist/trmnl/trmnl-settings.js.map +1 -0
- package/dist/trmnl/trmnl-telemetry.d.ts +47 -0
- package/dist/trmnl/trmnl-telemetry.d.ts.map +1 -0
- package/dist/trmnl/trmnl-telemetry.js +56 -0
- package/dist/trmnl/trmnl-telemetry.js.map +1 -0
- package/dist/tui/ansi.d.ts +0 -4
- package/dist/tui/ansi.d.ts.map +1 -1
- package/dist/tui/ansi.js +14 -7
- package/dist/tui/ansi.js.map +1 -1
- package/dist/tui/dashboard.d.ts +1 -0
- package/dist/tui/dashboard.d.ts.map +1 -1
- package/dist/tui/dashboard.js +38 -11
- package/dist/tui/dashboard.js.map +1 -1
- package/dist/tui/renderer.d.ts +36 -0
- package/dist/tui/renderer.d.ts.map +1 -1
- package/dist/tui/renderer.js +355 -121
- package/dist/tui/renderer.js.map +1 -1
- package/dist/tui/terrarium.d.ts.map +1 -1
- package/dist/tui/terrarium.js +44 -51
- package/dist/tui/terrarium.js.map +1 -1
- package/dist/types.d.ts +2 -2
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +2 -2
- package/dist/types.js.map +1 -1
- package/dist/usage-api.d.ts.map +1 -1
- package/dist/usage-api.js +7 -1
- package/dist/usage-api.js.map +1 -1
- package/dist/usage-event.d.ts +10 -3
- package/dist/usage-event.d.ts.map +1 -1
- package/dist/usage-event.js +98 -13
- package/dist/usage-event.js.map +1 -1
- package/dist/utility-proxy.d.ts.map +1 -1
- package/dist/utility-proxy.js +8 -0
- package/dist/utility-proxy.js.map +1 -1
- package/dist/utils/project-name.d.ts +3 -0
- package/dist/utils/project-name.d.ts.map +1 -1
- package/dist/utils/project-name.js +44 -2
- package/dist/utils/project-name.js.map +1 -1
- package/dist/version-check.d.ts.map +1 -1
- package/dist/version-check.js +1 -17
- package/dist/version-check.js.map +1 -1
- package/dist/windows-service.d.ts +26 -0
- package/dist/windows-service.d.ts.map +1 -0
- package/dist/windows-service.js +145 -0
- package/dist/windows-service.js.map +1 -0
- package/dist/ws-server.d.ts +17 -3
- package/dist/ws-server.d.ts.map +1 -1
- package/dist/ws-server.js +78 -8
- package/dist/ws-server.js.map +1 -1
- package/fm-helper/AgentDeckFMHelper.swift +112 -0
- package/package.json +10 -4
- package/scripts/build-fm-helper.mjs +26 -0
- package/LICENSE +0 -21
- package/dist/apme/tuner.d.ts +0 -64
- package/dist/apme/tuner.d.ts.map +0 -1
- package/dist/apme/tuner.js +0 -297
- package/dist/apme/tuner.js.map +0 -1
|
@@ -18,17 +18,23 @@
|
|
|
18
18
|
* 4. Screen-space overlays (danger flash) → output buffer
|
|
19
19
|
*/
|
|
20
20
|
import { State } from '../types.js';
|
|
21
|
+
import { PASSIVE_OFFLINE_LABEL } from '@agentdeck/shared';
|
|
22
|
+
import { hasOpenClawSession, foldCodexSessionsForDisplay } from '@agentdeck/shared';
|
|
23
|
+
import { layoutOctopuses, layoutCloudCreatures, layoutOpenCodeCreatures, layoutAntigravityCreatures, } from '@agentdeck/shared';
|
|
21
24
|
import { drawTextCentered } from './pixoo-font.js';
|
|
22
|
-
import { COLORS, setPixel, blendPixel, glowPixel, lerpColor,
|
|
23
|
-
import { blitWithCamera, updateDirector, } from './pixoo-camera.js';
|
|
24
|
-
|
|
25
|
+
import { COLORS, setPixel, blendPixel, glowPixel, lerpColor, drawOfficialDotGlyph, drawTetra, drawText, getOctopusPaletteForSession, getJellyfishPaletteForSession, getOpenCodePaletteForSession, getAntigravityPaletteForSession, } from './pixoo-sprites.js';
|
|
26
|
+
import { blitWithCamera, quantizeCameraPixels, updateDirector, WORLD_SIZE, ACTIVE_SIZE, } from './pixoo-camera.js';
|
|
27
|
+
import { MICRO_SIZE, paintTimeboxBeacon, } from './micro-glyphs.js';
|
|
28
|
+
import { OFFICIAL_DOT_GLYPHS, OFFICIAL_DOT_GLYPH_SIZE, } from './official-dot-glyphs.generated.js';
|
|
29
|
+
const W = WORLD_SIZE;
|
|
30
|
+
const ACTIVE_OFFSET = (WORLD_SIZE - ACTIVE_SIZE) / 2; // 16
|
|
25
31
|
// Track last render time for accurate dt calculation
|
|
26
32
|
let lastRenderTime = 0;
|
|
27
33
|
// ===== Layout (world-buffer pixel coords) =====
|
|
28
|
-
const SAND_TOP = 54;
|
|
29
|
-
const SAND_BOT = 59;
|
|
30
|
-
const SUBSTRATE_TOP = 60;
|
|
31
|
-
const SURFACE_Y = 2;
|
|
34
|
+
const SAND_TOP = ACTIVE_OFFSET + 54; // 70
|
|
35
|
+
const SAND_BOT = ACTIVE_OFFSET + 59; // 75
|
|
36
|
+
const SUBSTRATE_TOP = ACTIVE_OFFSET + 60; // 76
|
|
37
|
+
const SURFACE_Y = ACTIVE_OFFSET + 2; // 18
|
|
32
38
|
// ===== Creature World Positions (normalized 0~1) =====
|
|
33
39
|
const CF_DEFAULT_X = 0.72;
|
|
34
40
|
const CF_DEFAULT_Y = 0.76; // just above sand line (sitting on ground)
|
|
@@ -36,28 +42,74 @@ const CF_DEFAULT_Y = 0.76; // just above sand line (sitting on ground)
|
|
|
36
42
|
const PHI = (1 + Math.sqrt(5)) / 2;
|
|
37
43
|
/** Active creature instances keyed by sessionId. */
|
|
38
44
|
const creatureInstances = new Map();
|
|
39
|
-
/** Agent types that represent coding agents (draw as octopus). */
|
|
45
|
+
/** Agent types that represent coding agents (draw as octopus/robot). */
|
|
40
46
|
const CODING_AGENTS = new Set(['claude-code']);
|
|
41
47
|
/** Agent types drawn as jellyfish (cloud creature). */
|
|
42
|
-
const JELLYFISH_AGENTS = new Set(['codex-cli']);
|
|
48
|
+
const JELLYFISH_AGENTS = new Set(['codex-cli', 'codex-app']);
|
|
43
49
|
/** Agent types drawn as nested-square opencode. */
|
|
44
50
|
const OPENCODE_AGENTS = new Set(['opencode']);
|
|
45
|
-
|
|
46
|
-
const
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
51
|
+
/** Agent types drawn as the Antigravity peak/arc mark. */
|
|
52
|
+
const ANTIGRAVITY_AGENTS = new Set(['antigravity']);
|
|
53
|
+
function clamp(value, min, max) {
|
|
54
|
+
return Math.min(max, Math.max(min, value));
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Vertical stratification per creature type, applied on top of the band's base
|
|
58
|
+
* Y. Each type keeps its own water layer so a mixed Claude+Codex+OpenCode tank
|
|
59
|
+
* stays readable: clouds ride near the surface while working and sink to answer,
|
|
60
|
+
* octopuses hold mid-water, and everything settles to the sand when idle.
|
|
61
|
+
*
|
|
62
|
+
* Mirror of `stateY(_:kind:baseY:)` in apple/AgentDeck/Daemon/Modules/PixooRenderer.swift.
|
|
63
|
+
*/
|
|
64
|
+
function stateYForType(state, creatureType, baseY) {
|
|
65
|
+
switch (creatureType) {
|
|
66
|
+
case 'octopus':
|
|
67
|
+
if (state === 'processing')
|
|
68
|
+
return clamp(baseY, 0.40, 0.54);
|
|
69
|
+
if (state === 'awaiting')
|
|
70
|
+
return clamp(baseY - 0.04, 0.35, 0.48);
|
|
71
|
+
return 0.80;
|
|
72
|
+
case 'jellyfish':
|
|
73
|
+
if (state === 'processing')
|
|
74
|
+
return clamp(baseY, 0.16, 0.28);
|
|
75
|
+
if (state === 'awaiting')
|
|
76
|
+
return clamp(baseY + 0.26, 0.52, 0.64);
|
|
77
|
+
return clamp(baseY + 0.40, 0.80, 0.82);
|
|
78
|
+
case 'opencode':
|
|
79
|
+
if (state === 'processing')
|
|
80
|
+
return clamp(baseY - 0.02, 0.20, 0.34);
|
|
81
|
+
if (state === 'awaiting')
|
|
82
|
+
return clamp(baseY + 0.22, 0.50, 0.62);
|
|
83
|
+
return clamp(baseY + 0.36, 0.79, 0.81);
|
|
84
|
+
case 'antigravity':
|
|
85
|
+
if (state === 'processing')
|
|
86
|
+
return clamp(baseY - 0.04, 0.16, 0.30);
|
|
87
|
+
if (state === 'awaiting')
|
|
88
|
+
return clamp(baseY + 0.22, 0.46, 0.54);
|
|
89
|
+
return clamp(baseY + 0.34, 0.56, 0.64);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
function slotsForType(creatureType, count) {
|
|
93
|
+
switch (creatureType) {
|
|
94
|
+
case 'octopus': return layoutOctopuses(count);
|
|
95
|
+
case 'jellyfish': return layoutCloudCreatures(count);
|
|
96
|
+
case 'opencode': return layoutOpenCodeCreatures(count);
|
|
97
|
+
case 'antigravity': return layoutAntigravityCreatures(count);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
const FALLBACK_SLOT = { x: 0.38, y: 0.42, scale: 1.0 };
|
|
101
|
+
function slotAt(slots, index) {
|
|
102
|
+
if (slots.length === 0)
|
|
103
|
+
return FALLBACK_SLOT;
|
|
104
|
+
return slots[Math.min(index, slots.length - 1)];
|
|
55
105
|
}
|
|
56
106
|
/** Check if agent type gets a creature. */
|
|
57
107
|
function isCreatureAgent(agentType) {
|
|
58
|
-
return CODING_AGENTS.has(agentType) || JELLYFISH_AGENTS.has(agentType) || OPENCODE_AGENTS.has(agentType);
|
|
108
|
+
return CODING_AGENTS.has(agentType) || JELLYFISH_AGENTS.has(agentType) || OPENCODE_AGENTS.has(agentType) || ANTIGRAVITY_AGENTS.has(agentType);
|
|
59
109
|
}
|
|
60
110
|
function creatureTypeFor(agentType) {
|
|
111
|
+
if (ANTIGRAVITY_AGENTS.has(agentType))
|
|
112
|
+
return 'antigravity';
|
|
61
113
|
if (JELLYFISH_AGENTS.has(agentType))
|
|
62
114
|
return 'jellyfish';
|
|
63
115
|
if (OPENCODE_AGENTS.has(agentType))
|
|
@@ -65,11 +117,15 @@ function creatureTypeFor(agentType) {
|
|
|
65
117
|
return 'octopus';
|
|
66
118
|
}
|
|
67
119
|
function syncCreatures(sessions, stateEvent) {
|
|
68
|
-
// Determine which sessions are alive creature agents (octopus or jellyfish)
|
|
120
|
+
// Determine which sessions are alive creature agents (octopus or jellyfish).
|
|
121
|
+
// Codex rows fold by project first: each Claude Code rescue/stop-gate spawns a
|
|
122
|
+
// fresh codex thread, so without folding one workspace lights up 4-5 cloud
|
|
123
|
+
// marks at once. Octopus / opencode are NOT folded — running several of those
|
|
124
|
+
// against one project is a deliberate user pattern.
|
|
69
125
|
const aliveCoding = [];
|
|
70
126
|
if (sessions) {
|
|
71
|
-
for (const s of sessions) {
|
|
72
|
-
if (s.
|
|
127
|
+
for (const s of foldCodexSessionsForDisplay(sessions.filter(s => s.alive))) {
|
|
128
|
+
if (s.agentType && isCreatureAgent(s.agentType)) {
|
|
73
129
|
aliveCoding.push({ id: s.id, agentType: s.agentType, state: s.state ?? 'idle' });
|
|
74
130
|
}
|
|
75
131
|
}
|
|
@@ -89,45 +145,70 @@ function syncCreatures(sessions, stateEvent) {
|
|
|
89
145
|
creatureInstances.delete(id);
|
|
90
146
|
}
|
|
91
147
|
}
|
|
148
|
+
// Each agent type owns an X band and wraps to a second/third row as it fills,
|
|
149
|
+
// so a mixed tank clusters by agent instead of interleaving along one line.
|
|
150
|
+
const typeCounts = new Map();
|
|
151
|
+
for (const s of aliveCoding) {
|
|
152
|
+
const t = creatureTypeFor(s.agentType);
|
|
153
|
+
typeCounts.set(t, (typeCounts.get(t) ?? 0) + 1);
|
|
154
|
+
}
|
|
155
|
+
const slotsByType = new Map();
|
|
156
|
+
for (const [type, count] of typeCounts)
|
|
157
|
+
slotsByType.set(type, slotsForType(type, count));
|
|
158
|
+
const typeIndices = new Map();
|
|
159
|
+
// Band base Y per session, kept so the `_primary` refresh below can re-derive
|
|
160
|
+
// its stratified Y from the band instead of compounding onto an already
|
|
161
|
+
// stratified worldY.
|
|
162
|
+
const baseYById = new Map();
|
|
92
163
|
// Add/update creatures
|
|
93
164
|
for (let i = 0; i < aliveCoding.length; i++) {
|
|
94
165
|
const s = aliveCoding[i];
|
|
95
166
|
const existing = creatureInstances.get(s.id);
|
|
96
167
|
const sessionState = mapSessionState(s.state);
|
|
168
|
+
const creatureType = creatureTypeFor(s.agentType);
|
|
169
|
+
const slotIndex = typeIndices.get(creatureType) ?? 0;
|
|
170
|
+
typeIndices.set(creatureType, slotIndex + 1);
|
|
171
|
+
const slot = slotAt(slotsByType.get(creatureType) ?? [], slotIndex);
|
|
172
|
+
const x = slot.x;
|
|
173
|
+
baseYById.set(s.id, slot.y);
|
|
174
|
+
const worldY = stateYForType(sessionState, creatureType, slot.y);
|
|
97
175
|
if (existing) {
|
|
98
176
|
existing.state = sessionState;
|
|
99
177
|
existing.agentType = s.agentType;
|
|
100
|
-
existing.creatureType =
|
|
101
|
-
// Update
|
|
102
|
-
existing.worldY =
|
|
178
|
+
existing.creatureType = creatureType;
|
|
179
|
+
existing.worldX = x; // Update X dynamically to maintain even spacing
|
|
180
|
+
existing.worldY = worldY;
|
|
181
|
+
existing.sizeScale = slot.scale;
|
|
103
182
|
}
|
|
104
183
|
else {
|
|
105
|
-
// Golden ratio X distribution, Y by state
|
|
106
|
-
const x = aliveCoding.length === 1
|
|
107
|
-
? 0.38 // single session: classic center-left
|
|
108
|
-
: 0.15 + ((i * PHI) % 1) * 0.70;
|
|
109
184
|
creatureInstances.set(s.id, {
|
|
110
185
|
sessionId: s.id,
|
|
111
186
|
agentType: s.agentType,
|
|
112
|
-
creatureType
|
|
187
|
+
creatureType,
|
|
113
188
|
state: sessionState,
|
|
114
189
|
worldX: x,
|
|
115
|
-
worldY
|
|
190
|
+
worldY,
|
|
116
191
|
phaseOffset: i * 5,
|
|
192
|
+
sizeScale: slot.scale,
|
|
117
193
|
});
|
|
118
194
|
}
|
|
119
195
|
}
|
|
120
|
-
//
|
|
121
|
-
//
|
|
196
|
+
// Refresh the synthetic `_primary` creature from stateEvent — it *is* the
|
|
197
|
+
// stateEvent, so this keeps it precise before the first sessions_list lands.
|
|
198
|
+
//
|
|
199
|
+
// Real sessions are deliberately left alone. stateEvent.sessionId can name a
|
|
200
|
+
// row that folding has already collapsed into a representative, and stamping
|
|
201
|
+
// that member's state onto the representative would downgrade a group the fold
|
|
202
|
+
// resolved to `processing` down to the one idle sibling the event came from.
|
|
203
|
+
// The sessions list is daemon-pushed and already carries per-session state.
|
|
122
204
|
const aType = stateEvent?.agentType;
|
|
123
205
|
const isCreature = isCreatureAgent(aType ?? '');
|
|
124
|
-
if (stateEvent && isCreature
|
|
125
|
-
const
|
|
126
|
-
const primary = creatureInstances.get(primaryId);
|
|
206
|
+
if (stateEvent && isCreature) {
|
|
207
|
+
const primary = creatureInstances.get('_primary');
|
|
127
208
|
if (primary) {
|
|
128
209
|
const st = simplifiedState(stateEvent.state ?? State.IDLE);
|
|
129
210
|
primary.state = st;
|
|
130
|
-
primary.worldY =
|
|
211
|
+
primary.worldY = stateYForType(st, primary.creatureType, baseYById.get('_primary') ?? primary.worldY);
|
|
131
212
|
}
|
|
132
213
|
}
|
|
133
214
|
}
|
|
@@ -187,7 +268,7 @@ function waterColorAt(palette, surfaceY, y) {
|
|
|
187
268
|
const waterDepth = SAND_TOP - surfaceY;
|
|
188
269
|
if (waterDepth <= 0)
|
|
189
270
|
return palette.deep;
|
|
190
|
-
const t = (y - surfaceY) / waterDepth;
|
|
271
|
+
const t = Math.max(0, (y - surfaceY) / waterDepth);
|
|
191
272
|
if (t < 0.25)
|
|
192
273
|
return lerpColor(palette.surface, palette.light, t / 0.25);
|
|
193
274
|
if (t < 0.6)
|
|
@@ -200,8 +281,8 @@ function initTetras() {
|
|
|
200
281
|
const result = [];
|
|
201
282
|
for (let i = 0; i < NUM_TETRAS; i++) {
|
|
202
283
|
result.push({
|
|
203
|
-
x: 12 + Math.random() *
|
|
204
|
-
y:
|
|
284
|
+
x: 12 + Math.random() * (WORLD_SIZE - 24),
|
|
285
|
+
y: SURFACE_Y + 4 + Math.random() * 22,
|
|
205
286
|
heading: Math.random() > 0.5 ? 1 : -1,
|
|
206
287
|
speed: 0.08 + Math.random() * 0.12, // slower — prevents teleporting at ~1fps
|
|
207
288
|
phase: Math.random() * Math.PI * 2,
|
|
@@ -213,11 +294,11 @@ function initTetras() {
|
|
|
213
294
|
function updateTetras(frame, surfaceY, maxY) {
|
|
214
295
|
if (!tetras)
|
|
215
296
|
tetras = initTetras();
|
|
216
|
-
// Two school centers via Lissajous (meet and diverge every ~25s)
|
|
217
|
-
const sc0X =
|
|
218
|
-
const sc0Y = Math.max(surfaceY + 8,
|
|
219
|
-
const sc1X =
|
|
220
|
-
const sc1Y = Math.max(surfaceY + 8,
|
|
297
|
+
// Two school centers via Lissajous (meet and diverge every ~25s), scaled to WORLD_SIZE
|
|
298
|
+
const sc0X = (WORLD_SIZE / 2 - 12) + Math.sin(frame * 0.02) * 24;
|
|
299
|
+
const sc0Y = Math.max(surfaceY + 8, SURFACE_Y + 12) + Math.cos(frame * 0.015) * 8;
|
|
300
|
+
const sc1X = (WORLD_SIZE / 2 + 12) + Math.sin(frame * 0.0175 + 2) * 24;
|
|
301
|
+
const sc1Y = Math.max(surfaceY + 8, SURFACE_Y + 14) + Math.cos(frame * 0.0225 + 1) * 8;
|
|
221
302
|
const centers = [{ x: sc0X, y: sc0Y }, { x: sc1X, y: sc1Y }];
|
|
222
303
|
for (const t of tetras) {
|
|
223
304
|
const sc = centers[t.schoolId];
|
|
@@ -228,9 +309,9 @@ function updateTetras(frame, surfaceY, maxY) {
|
|
|
228
309
|
t.y += dy * 0.025 + Math.sin(frame * 0.05 + t.phase) * 0.2;
|
|
229
310
|
// Boundary
|
|
230
311
|
const minY = surfaceY + 3;
|
|
231
|
-
if (t.x < 3 || t.x >
|
|
312
|
+
if (t.x < 3 || t.x > WORLD_SIZE - 3) {
|
|
232
313
|
t.heading *= -1;
|
|
233
|
-
t.x = Math.max(3, Math.min(
|
|
314
|
+
t.x = Math.max(3, Math.min(WORLD_SIZE - 3, t.x));
|
|
234
315
|
}
|
|
235
316
|
if (t.y < minY)
|
|
236
317
|
t.y = minY;
|
|
@@ -247,12 +328,17 @@ function getSchoolCenter() {
|
|
|
247
328
|
sx += t.x;
|
|
248
329
|
sy += t.y;
|
|
249
330
|
}
|
|
250
|
-
|
|
331
|
+
const avgX = sx / tetras.length;
|
|
332
|
+
const avgY = sy / tetras.length;
|
|
333
|
+
return {
|
|
334
|
+
x: (avgX - ACTIVE_OFFSET) / ACTIVE_SIZE,
|
|
335
|
+
y: (avgY - ACTIVE_OFFSET) / ACTIVE_SIZE,
|
|
336
|
+
};
|
|
251
337
|
}
|
|
252
338
|
let bubbles = [];
|
|
253
339
|
function spawnBubble() {
|
|
254
340
|
return {
|
|
255
|
-
x: 4 + Math.random() *
|
|
341
|
+
x: 4 + Math.random() * (WORLD_SIZE - 8),
|
|
256
342
|
y: SAND_TOP - 1 - Math.random() * 4,
|
|
257
343
|
speed: 0.3 + Math.random() * 0.4,
|
|
258
344
|
wobblePhase: Math.random() * Math.PI * 2,
|
|
@@ -275,7 +361,7 @@ let dataParticles = [];
|
|
|
275
361
|
function updateDataParticles(frame, surfaceY, active) {
|
|
276
362
|
if (active && frame % 6 === 0) { // spawn half as often
|
|
277
363
|
dataParticles.push({
|
|
278
|
-
x: 10 + Math.random() *
|
|
364
|
+
x: 10 + Math.random() * (WORLD_SIZE - 20),
|
|
279
365
|
y: surfaceY + 2 + Math.random() * 3,
|
|
280
366
|
vy: 0.2 + Math.random() * 0.15,
|
|
281
367
|
life: 60 + Math.random() * 40,
|
|
@@ -293,12 +379,20 @@ function updateDataParticles(frame, surfaceY, active) {
|
|
|
293
379
|
}
|
|
294
380
|
// ===== Seaweed =====
|
|
295
381
|
const SEAWEED_POSITIONS = [
|
|
382
|
+
// Outer left edge (padding region)
|
|
296
383
|
{ x: 2, h: 13, phase: 0 },
|
|
297
384
|
{ x: 5, h: 9, phase: 1.2 },
|
|
298
385
|
{ x: 8, h: 6, phase: 2.5 },
|
|
299
|
-
|
|
300
|
-
{ x:
|
|
301
|
-
{ x:
|
|
386
|
+
// Active region left edge
|
|
387
|
+
{ x: ACTIVE_OFFSET + 2, h: 11, phase: 0.5 },
|
|
388
|
+
{ x: ACTIVE_OFFSET + 5, h: 7, phase: 1.7 },
|
|
389
|
+
// Active region right edge
|
|
390
|
+
{ x: ACTIVE_OFFSET + 58, h: 8, phase: 2.2 },
|
|
391
|
+
{ x: ACTIVE_OFFSET + 61, h: 10, phase: 0.9 },
|
|
392
|
+
// Outer right edge (padding region)
|
|
393
|
+
{ x: WORLD_SIZE - 9, h: 12, phase: 0.8 },
|
|
394
|
+
{ x: WORLD_SIZE - 6, h: 8, phase: 1.9 },
|
|
395
|
+
{ x: WORLD_SIZE - 3, h: 7, phase: 3.1 },
|
|
302
396
|
];
|
|
303
397
|
function drawSeaweed(buf, frame, surfaceY) {
|
|
304
398
|
for (const sw of SEAWEED_POSITIONS) {
|
|
@@ -320,9 +414,11 @@ function drawSeaweed(buf, frame, surfaceY) {
|
|
|
320
414
|
// ===== Light Rays =====
|
|
321
415
|
function drawLightRays(buf, frame, surfaceY) {
|
|
322
416
|
const rays = [
|
|
323
|
-
{ baseX:
|
|
324
|
-
{ baseX:
|
|
325
|
-
{ baseX:
|
|
417
|
+
{ baseX: 8 + Math.sin(frame * 0.018) * 3, angle: 0.1 }, // left padding ray
|
|
418
|
+
{ baseX: ACTIVE_OFFSET + 15 + Math.sin(frame * 0.02) * 5, angle: 0.15 },
|
|
419
|
+
{ baseX: ACTIVE_OFFSET + 35 + Math.sin(frame * 0.015 + 1) * 6, angle: -0.1 },
|
|
420
|
+
{ baseX: ACTIVE_OFFSET + 50 + Math.sin(frame * 0.025 + 2) * 4, angle: 0.2 },
|
|
421
|
+
{ baseX: WORLD_SIZE - 10 + Math.sin(frame * 0.022) * 3, angle: -0.15 }, // right padding ray
|
|
326
422
|
];
|
|
327
423
|
for (const ray of rays) {
|
|
328
424
|
const depth = SAND_TOP - surfaceY;
|
|
@@ -385,7 +481,8 @@ function drawTerrain(buf) {
|
|
|
385
481
|
setPixel(buf, x, y, color);
|
|
386
482
|
}
|
|
387
483
|
}
|
|
388
|
-
|
|
484
|
+
// Distribute gravel positions across the entire 96 width
|
|
485
|
+
const gravelPositions = [4, 12, 20, 24, 31, 38, 45, 53, 60, 67, 74, 80, 87, 92];
|
|
389
486
|
for (const gx of gravelPositions)
|
|
390
487
|
setPixel(buf, gx, SAND_TOP, COLORS.gravel);
|
|
391
488
|
for (let y = SUBSTRATE_TOP; y < W; y++) {
|
|
@@ -395,9 +492,11 @@ function drawTerrain(buf) {
|
|
|
395
492
|
}
|
|
396
493
|
}
|
|
397
494
|
const rocks = [
|
|
398
|
-
{ x:
|
|
399
|
-
{ x:
|
|
400
|
-
{ x:
|
|
495
|
+
{ x: 4, y: SAND_BOT + 1, w: 3, h: 2 }, // left padding rock
|
|
496
|
+
{ x: ACTIVE_OFFSET + 12, y: SAND_BOT, w: 4, h: 2 },
|
|
497
|
+
{ x: ACTIVE_OFFSET + 30, y: SAND_BOT + 1, w: 3, h: 2 },
|
|
498
|
+
{ x: ACTIVE_OFFSET + 48, y: SAND_BOT, w: 5, h: 3 },
|
|
499
|
+
{ x: WORLD_SIZE - 8, y: SAND_BOT, w: 4, h: 2 }, // right padding rock
|
|
401
500
|
];
|
|
402
501
|
for (const r of rocks) {
|
|
403
502
|
for (let dy = 0; dy < r.h; dy++) {
|
|
@@ -438,7 +537,7 @@ function simplifiedState(state) {
|
|
|
438
537
|
}
|
|
439
538
|
// ===== Usage HUD Helpers =====
|
|
440
539
|
/** Gauge bar color based on usage percentage. */
|
|
441
|
-
function gaugeColor(pct, animFrame) {
|
|
540
|
+
function gaugeColor(pct, animFrame, brand) {
|
|
442
541
|
if (pct >= 90) {
|
|
443
542
|
// Red with pulse
|
|
444
543
|
const pulse = (Math.sin(animFrame * 0.2) + 1) * 0.3;
|
|
@@ -446,12 +545,10 @@ function gaugeColor(pct, animFrame) {
|
|
|
446
545
|
}
|
|
447
546
|
if (pct >= 70)
|
|
448
547
|
return COLORS.stateAwaiting; // amber
|
|
449
|
-
|
|
450
|
-
return [0x00, 0xC8, 0xB4]; // teal
|
|
451
|
-
return COLORS.stateProcessing; // blue
|
|
548
|
+
return brand;
|
|
452
549
|
}
|
|
453
550
|
/** Pixoo HUD reset time: "1h23", "4d6", "59m". */
|
|
454
|
-
function formatResetDetailed(resetsAt) {
|
|
551
|
+
export function formatResetDetailed(resetsAt) {
|
|
455
552
|
if (!resetsAt)
|
|
456
553
|
return '';
|
|
457
554
|
const ms = new Date(resetsAt).getTime() - Date.now();
|
|
@@ -472,81 +569,348 @@ function formatResetDetailed(resetsAt) {
|
|
|
472
569
|
return `${hours}h`;
|
|
473
570
|
return `${mins}m`;
|
|
474
571
|
}
|
|
475
|
-
/** Draw Usage HUD in screen space (bottom
|
|
476
|
-
*
|
|
477
|
-
*
|
|
478
|
-
*
|
|
572
|
+
/** Draw Usage HUD in screen space (bottom, zoom-independent).
|
|
573
|
+
* Each provider uses the same seven-pixel band with its official creature
|
|
574
|
+
* silhouette, full-height usage fill, percentage and reset countdown. When both are present Claude occupies
|
|
575
|
+
* rows 50-56 and Codex rows 57-63; a lone provider stays on rows 57-63.
|
|
576
|
+
* Primary/5h is the left zone and secondary/7d is the right zone.
|
|
577
|
+
* When Codex only reports 7d, its subscription date occupies the otherwise
|
|
578
|
+
* empty primary zone.
|
|
479
579
|
*/
|
|
480
580
|
function drawUsageHUD(buf, usageEvent, animFrame) {
|
|
481
|
-
|
|
482
|
-
// an old "12%" frozen on-screen looks authoritative to the user even though
|
|
483
|
-
// the CLI daemon isn't live to produce a fresh fetch. Matches the collapse
|
|
484
|
-
// behavior on every other surface (macOS dashboard, plugin, Android, D200H).
|
|
485
|
-
if (!usageEvent || usageEvent.fiveHourPercent == null)
|
|
581
|
+
if (!usageEvent)
|
|
486
582
|
return;
|
|
487
|
-
|
|
583
|
+
const providers = [];
|
|
584
|
+
if (usageEvent.usageStale !== true && usageEvent.fiveHourPercent != null) {
|
|
585
|
+
providers.push({
|
|
586
|
+
glyph: 'claudeCode', brand: [255, 112, 76],
|
|
587
|
+
primary: { percent: usageEvent.fiveHourPercent, resetsAt: usageEvent.fiveHourResetsAt },
|
|
588
|
+
secondary: usageEvent.sevenDayPercent == null ? undefined : {
|
|
589
|
+
percent: usageEvent.sevenDayPercent, resetsAt: usageEvent.sevenDayResetsAt,
|
|
590
|
+
},
|
|
591
|
+
});
|
|
592
|
+
}
|
|
593
|
+
// A window counts as renderable only when it is both fresh AND carries a
|
|
594
|
+
// number. "Not stale but no percent" is a malformed frame, not a zero — the
|
|
595
|
+
// gauge must stay absent rather than draw an authoritative-looking 0%.
|
|
596
|
+
// Mirror of `freshCodexWindow` in apple/AgentDeck/Daemon/Modules/PixooRenderer.swift.
|
|
597
|
+
const freshCodexWindow = (w) => w && w.stale !== true && w.usedPercent != null
|
|
598
|
+
? { percent: w.usedPercent, resetsAt: w.resetsAt }
|
|
599
|
+
: undefined;
|
|
600
|
+
const codexPrimaryWindow = freshCodexWindow(usageEvent.codexRateLimits?.primary);
|
|
601
|
+
const codexSecondaryWindow = freshCodexWindow(usageEvent.codexRateLimits?.secondary);
|
|
602
|
+
if (codexPrimaryWindow || codexSecondaryWindow) {
|
|
603
|
+
providers.push({
|
|
604
|
+
glyph: 'codex', brand: [126, 116, 255],
|
|
605
|
+
primary: codexPrimaryWindow,
|
|
606
|
+
secondary: codexSecondaryWindow,
|
|
607
|
+
subscriptionUntil: usageEvent.codexSubscriptionActiveUntil,
|
|
608
|
+
});
|
|
609
|
+
}
|
|
610
|
+
if (providers.length === 0)
|
|
488
611
|
return;
|
|
489
|
-
const
|
|
490
|
-
const
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
612
|
+
const timeColor = [0x60, 0x70, 0x80];
|
|
613
|
+
const firstY = providers.length > 1 ? 50 : 57;
|
|
614
|
+
function drawCreatureMarker(provider, rowY) {
|
|
615
|
+
const mask = OFFICIAL_DOT_GLYPHS[provider.glyph];
|
|
616
|
+
const sourceSize = OFFICIAL_DOT_GLYPH_SIZE;
|
|
617
|
+
// Sample the canonical square canvas instead of cropping its occupied
|
|
618
|
+
// bounds. Claude's source silhouette is intentionally wider than it is
|
|
619
|
+
// tall; cropping and stretching those bounds to 7x7 turns the robot into
|
|
620
|
+
// a square. The 9px slot leaves one physical LED on both sides.
|
|
621
|
+
const markerSize = 7;
|
|
622
|
+
const markerX = 1;
|
|
623
|
+
for (let dy = 0; dy < 7; dy++) {
|
|
624
|
+
const sy = Math.min(sourceSize - 1, Math.floor((dy + 0.5) * sourceSize / markerSize));
|
|
625
|
+
for (let dx = 0; dx < markerSize; dx++) {
|
|
626
|
+
const sx = Math.min(sourceSize - 1, Math.floor((dx + 0.5) * sourceSize / markerSize));
|
|
627
|
+
const alpha = mask[sy * sourceSize + sx];
|
|
628
|
+
if (alpha <= 16)
|
|
629
|
+
continue;
|
|
630
|
+
const intensity = alpha >= 96 ? 1 : 0.56;
|
|
631
|
+
setPixel(buf, markerX + dx, rowY + dy, lerpColor(COLORS.black, provider.brand, intensity));
|
|
632
|
+
}
|
|
496
633
|
}
|
|
497
634
|
}
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
const
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
635
|
+
function fittedReset(resetsAt, pctText, zoneWidth) {
|
|
636
|
+
const detailed = formatResetDetailed(resetsAt);
|
|
637
|
+
if (!detailed)
|
|
638
|
+
return '';
|
|
639
|
+
const maxChars = Math.floor(zoneWidth / 4);
|
|
640
|
+
if (pctText.length + detailed.length <= maxChars)
|
|
641
|
+
return detailed;
|
|
642
|
+
const unit = detailed.match(/^(\d+)[dhm]/);
|
|
643
|
+
return unit ? `${unit[1]}${unit[0].at(-1)}` : detailed.slice(0, Math.max(0, maxChars - pctText.length));
|
|
644
|
+
}
|
|
645
|
+
function subscriptionDate(until) {
|
|
646
|
+
const value = until?.trim();
|
|
647
|
+
if (!value)
|
|
648
|
+
return '';
|
|
649
|
+
const iso = value.match(/^\d{4}-(\d{1,2})-(\d{1,2})/);
|
|
650
|
+
if (iso)
|
|
651
|
+
return `${Number(iso[1])}/${Number(iso[2])}`;
|
|
652
|
+
const compact = value.match(/^~?(\d{1,2})\/(\d{1,2})$/);
|
|
653
|
+
return compact ? `${Number(compact[1])}/${Number(compact[2])}` : '';
|
|
654
|
+
}
|
|
655
|
+
function drawCenteredLabel(text, leftX, rightX, rowY) {
|
|
656
|
+
if (!text)
|
|
657
|
+
return;
|
|
658
|
+
const textWidth = text.length * 4 - 1;
|
|
659
|
+
const startX = leftX + Math.max(0, Math.floor((rightX - leftX - textWidth) / 2));
|
|
660
|
+
drawText(buf, text, startX + textWidth, rowY + 1, timeColor);
|
|
661
|
+
}
|
|
662
|
+
function renderWindow(window, leftX, rightX, rowY, brand) {
|
|
663
|
+
const pct = Math.max(0, Math.min(100, window.percent));
|
|
664
|
+
const color = gaugeColor(pct, animFrame, brand);
|
|
665
|
+
const zoneWidth = rightX - leftX;
|
|
666
|
+
const fillWidth = Math.round(zoneWidth * pct / 100);
|
|
667
|
+
// TC001-style fill: the used portion is a dim full-height color field, so
|
|
668
|
+
// the gauge reads at a glance while bright text remains crisp above it.
|
|
669
|
+
for (let y = rowY; y < rowY + 7; y++) {
|
|
670
|
+
for (let x = leftX; x < leftX + fillWidth; x++)
|
|
671
|
+
blendPixel(buf, x, y, color, 0.24);
|
|
672
|
+
}
|
|
673
|
+
const pctText = `${Math.round(pct)}%`;
|
|
674
|
+
const resetText = fittedReset(window.resetsAt, pctText, zoneWidth);
|
|
675
|
+
if (resetText) {
|
|
676
|
+
drawText(buf, resetText, rightX, rowY + 1, timeColor);
|
|
677
|
+
drawText(buf, pctText, rightX - resetText.length * 4, rowY + 1, color);
|
|
678
|
+
}
|
|
679
|
+
else {
|
|
680
|
+
drawText(buf, pctText, rightX, rowY + 1, color);
|
|
681
|
+
}
|
|
682
|
+
}
|
|
683
|
+
providers.forEach((provider, index) => {
|
|
684
|
+
const rowY = firstY + index * 7;
|
|
685
|
+
for (let y = rowY; y < rowY + 7; y++) {
|
|
686
|
+
for (let x = 0; x < 64; x++) {
|
|
687
|
+
blendPixel(buf, x, y, COLORS.black, 0.70);
|
|
688
|
+
blendPixel(buf, x, y, provider.brand, 0.08);
|
|
508
689
|
}
|
|
509
690
|
}
|
|
510
|
-
//
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
691
|
+
// A slightly denser identity dock lets the official mark join the same
|
|
692
|
+
// color band without placing quota fill behind the silhouette itself.
|
|
693
|
+
for (let y = rowY; y < rowY + 7; y++) {
|
|
694
|
+
for (let x = 0; x < 9; x++)
|
|
695
|
+
blendPixel(buf, x, y, provider.brand, 0.12);
|
|
696
|
+
}
|
|
697
|
+
drawCreatureMarker(provider, rowY);
|
|
698
|
+
if (provider.primary && provider.secondary) {
|
|
699
|
+
for (let y = rowY + 1; y < rowY + 6; y++)
|
|
700
|
+
blendPixel(buf, 36, y, provider.brand, 0.28);
|
|
701
|
+
renderWindow(provider.primary, 9, 36, rowY, provider.brand);
|
|
702
|
+
renderWindow(provider.secondary, 37, 64, rowY, provider.brand);
|
|
703
|
+
}
|
|
704
|
+
else if (!provider.primary && provider.secondary && subscriptionDate(provider.subscriptionUntil)) {
|
|
705
|
+
for (let y = rowY + 1; y < rowY + 6; y++)
|
|
706
|
+
blendPixel(buf, 36, y, provider.brand, 0.28);
|
|
707
|
+
drawCenteredLabel(subscriptionDate(provider.subscriptionUntil), 9, 36, rowY);
|
|
708
|
+
renderWindow(provider.secondary, 37, 64, rowY, provider.brand);
|
|
515
709
|
}
|
|
516
710
|
else {
|
|
517
|
-
|
|
711
|
+
const only = provider.primary ?? provider.secondary;
|
|
712
|
+
if (only)
|
|
713
|
+
renderWindow(only, 9, 64, rowY, provider.brand);
|
|
518
714
|
}
|
|
715
|
+
});
|
|
716
|
+
}
|
|
717
|
+
/**
|
|
718
|
+
* Render the Timebox Mini's native 11×11 Agent Beacon, then nearest-scale into
|
|
719
|
+
* the requested output. A stable 9×9 official mark carries identity while the
|
|
720
|
+
* one-pixel perimeter rail alone carries state and motion.
|
|
721
|
+
*/
|
|
722
|
+
function renderMicroFrame(outputBuf, size, animFrame, stateEvent, sessions, usagePct) {
|
|
723
|
+
// Presence-driven SSOT: the crayfish renders iff the daemon emitted an
|
|
724
|
+
// OpenClaw session — never from raw gateway flags. The daemon emits it iff
|
|
725
|
+
// the Gateway is authenticated, so reachability/error alone won't draw it.
|
|
726
|
+
const hasGateway = hasOpenClawSession(sessions ?? []);
|
|
727
|
+
const gatewayHasError = stateEvent?.gatewayHasError ?? false;
|
|
728
|
+
// Pick the dominant creature: awaiting (most urgent) → processing → idle.
|
|
729
|
+
const byPriority = (c) => c.state === 'awaiting' ? 0 : c.state === 'processing' ? 1 : 2;
|
|
730
|
+
const dominant = [...creatureInstances.values()].sort((a, b) => byPriority(a) - byPriority(b))[0];
|
|
731
|
+
// When the only creature is the gateway crayfish, its routing state still
|
|
732
|
+
// drives the background (no dominant creature instance exists for OpenClaw).
|
|
733
|
+
const routing = sessions?.some((s) => s.agentType === 'openclaw' && s.state === 'processing') ?? false;
|
|
734
|
+
const aggregate = gatewayHasError || usagePct >= 90 ? 'error'
|
|
735
|
+
: dominant?.state === 'awaiting' ? 'awaiting'
|
|
736
|
+
: (dominant?.state === 'processing' || (!dominant && routing)) ? 'processing'
|
|
737
|
+
: 'idle';
|
|
738
|
+
const base = new Uint8Array(MICRO_SIZE * MICRO_SIZE * 3);
|
|
739
|
+
let creature = null;
|
|
740
|
+
if (dominant) {
|
|
741
|
+
creature =
|
|
742
|
+
dominant.agentType === 'antigravity' ? 'antigravity'
|
|
743
|
+
: dominant.creatureType === 'jellyfish' ? 'jellyfish'
|
|
744
|
+
: dominant.creatureType === 'opencode' ? 'opencode'
|
|
745
|
+
: 'octopus';
|
|
519
746
|
}
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
747
|
+
else if (hasGateway) {
|
|
748
|
+
creature = 'crayfish';
|
|
749
|
+
}
|
|
750
|
+
paintTimeboxBeacon(base, creature, aggregate, animFrame);
|
|
751
|
+
// Scale the 11×11 base into the size×size output (1:1 when size === 11).
|
|
752
|
+
for (let y = 0; y < size; y++) {
|
|
753
|
+
const sy = Math.min(MICRO_SIZE - 1, Math.floor((y * MICRO_SIZE) / size));
|
|
754
|
+
for (let x = 0; x < size; x++) {
|
|
755
|
+
const sx = Math.min(MICRO_SIZE - 1, Math.floor((x * MICRO_SIZE) / size));
|
|
756
|
+
const s = (sy * MICRO_SIZE + sx) * 3;
|
|
757
|
+
const d = (y * size + x) * 3;
|
|
758
|
+
outputBuf[d] = base[s];
|
|
759
|
+
outputBuf[d + 1] = base[s + 1];
|
|
760
|
+
outputBuf[d + 2] = base[s + 2];
|
|
761
|
+
}
|
|
526
762
|
}
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
763
|
+
}
|
|
764
|
+
/** Native 32×32 iDotMatrix identity stage. The panel gets saturated official
|
|
765
|
+
* marks on a quiet field plus four fixed telemetry rails; no 64px scene is
|
|
766
|
+
* downsampled, so cutouts remain physical pixels. */
|
|
767
|
+
function renderCompact32Frame(outputBuf, animFrame, stateEvent, sessions, usageEvent) {
|
|
768
|
+
const set = (x, y, color) => setPixel(outputBuf, x, y, color);
|
|
769
|
+
const state = String(stateEvent?.state ?? State.IDLE);
|
|
770
|
+
for (let y = 0; y < 28; y++) {
|
|
771
|
+
const t = y / 27;
|
|
772
|
+
const color = [2 + Math.round(5 * t), 7 + Math.round(10 * t), 18 + Math.round(18 * t)];
|
|
773
|
+
for (let x = 0; x < 32; x++)
|
|
774
|
+
set(x, y, color);
|
|
775
|
+
}
|
|
776
|
+
const surface = state.startsWith('awaiting') ? [255, 190, 45]
|
|
777
|
+
: state === 'processing' ? [50, 225, 255] : [38, 210, 145];
|
|
778
|
+
for (let x = 0; x < 32; x++) {
|
|
779
|
+
if ((x + Math.floor(animFrame / 4)) % (state === 'processing' ? 3 : 6) === 0)
|
|
780
|
+
set(x, 2, surface);
|
|
781
|
+
}
|
|
782
|
+
const glyphFor = (kind) => kind === 'jellyfish' ? 'codex' : kind === 'opencode' ? 'openCode'
|
|
783
|
+
: kind === 'antigravity' ? 'antigravity' : 'claudeCode';
|
|
784
|
+
const priority = (s) => s === 'awaiting' ? 0 : s === 'processing' ? 1 : 2;
|
|
785
|
+
const marks = [...creatureInstances.values()].map((c) => ({ glyph: glyphFor(c.creatureType), state: c.state }));
|
|
786
|
+
if (hasOpenClawSession(sessions ?? [])) {
|
|
787
|
+
const routing = sessions?.some((s) => s.agentType === 'openclaw' && s.state === 'processing') ?? false;
|
|
788
|
+
marks.push({ glyph: 'openClaw', state: routing ? 'processing' : 'idle' });
|
|
789
|
+
}
|
|
790
|
+
marks.sort((a, b) => priority(a.state) - priority(b.state));
|
|
791
|
+
marks.splice(3);
|
|
792
|
+
const slots = marks.length === 1 ? [{ x: 16, y: 14, size: 18 }]
|
|
793
|
+
: marks.length === 2 ? [{ x: 9, y: 14, size: 13 }, { x: 23, y: 14, size: 13 }]
|
|
794
|
+
: [{ x: 6, y: 14, size: 10 }, { x: 16, y: 14, size: 10 }, { x: 26, y: 14, size: 10 }];
|
|
795
|
+
const antigravityBands = [
|
|
796
|
+
[92, 214, 77], [245, 203, 36], [255, 132, 16], [255, 82, 65],
|
|
797
|
+
[183, 92, 182], [102, 111, 225], [36, 126, 255],
|
|
798
|
+
];
|
|
799
|
+
const baseColor = (glyph, sx) => {
|
|
800
|
+
if (glyph === 'claudeCode')
|
|
801
|
+
return [255, 112, 76];
|
|
802
|
+
if (glyph === 'codex')
|
|
803
|
+
return [126, 116, 255];
|
|
804
|
+
if (glyph === 'openCode')
|
|
805
|
+
return [255, 246, 248];
|
|
806
|
+
if (glyph === 'openClaw')
|
|
807
|
+
return [255, 67, 84];
|
|
808
|
+
return antigravityBands[Math.min(antigravityBands.length - 1, Math.floor(sx * antigravityBands.length / OFFICIAL_DOT_GLYPH_SIZE))];
|
|
809
|
+
};
|
|
810
|
+
marks.forEach((mark, index) => {
|
|
811
|
+
const slot = slots[index];
|
|
812
|
+
const mask = OFFICIAL_DOT_GLYPHS[mark.glyph];
|
|
813
|
+
const bob = mark.state === 'processing' ? Math.round(Math.sin((animFrame + index * 5) * 0.28)) : 0;
|
|
814
|
+
const x0 = slot.x - Math.floor(slot.size / 2);
|
|
815
|
+
const y0 = slot.y - Math.floor(slot.size / 2) + bob;
|
|
816
|
+
for (let dy = 0; dy < slot.size; dy++) {
|
|
817
|
+
const sy = Math.min(OFFICIAL_DOT_GLYPH_SIZE - 1, Math.floor(dy * OFFICIAL_DOT_GLYPH_SIZE / slot.size));
|
|
818
|
+
for (let dx = 0; dx < slot.size; dx++) {
|
|
819
|
+
const sx = Math.min(OFFICIAL_DOT_GLYPH_SIZE - 1, Math.floor(dx * OFFICIAL_DOT_GLYPH_SIZE / slot.size));
|
|
820
|
+
const alpha = mask[sy * OFFICIAL_DOT_GLYPH_SIZE + sx] / 255;
|
|
821
|
+
if (alpha <= 0.04)
|
|
822
|
+
continue;
|
|
823
|
+
const color = baseColor(mark.glyph, sx);
|
|
824
|
+
blendPixel(outputBuf, x0 + dx + 1, y0 + dy + 1, [0, 0, 0], alpha * 0.55);
|
|
825
|
+
if (alpha > 0.42) {
|
|
826
|
+
blendPixel(outputBuf, x0 + dx - 1, y0 + dy, color, 0.055);
|
|
827
|
+
blendPixel(outputBuf, x0 + dx + 1, y0 + dy, color, 0.055);
|
|
828
|
+
}
|
|
829
|
+
const coverage = Math.min(1, Math.pow(alpha, 0.72) * 1.12);
|
|
830
|
+
const light = 1.08 - dy / Math.max(1, slot.size - 1) * 0.12;
|
|
831
|
+
const lit = [
|
|
832
|
+
Math.min(255, Math.round(color[0] * light)),
|
|
833
|
+
Math.min(255, Math.round(color[1] * light)),
|
|
834
|
+
Math.min(255, Math.round(color[2] * light)),
|
|
835
|
+
];
|
|
836
|
+
blendPixel(outputBuf, x0 + dx, y0 + dy, lit, coverage);
|
|
837
|
+
}
|
|
838
|
+
}
|
|
839
|
+
if (mark.glyph === 'openClaw') {
|
|
840
|
+
set(x0 + Math.round(9.05 / 24 * slot.size), y0 + Math.round(7.63 / 24 * slot.size), [0, 229, 204]);
|
|
841
|
+
set(x0 + Math.round(15.38 / 24 * slot.size), y0 + Math.round(7.63 / 24 * slot.size), [0, 229, 204]);
|
|
842
|
+
}
|
|
843
|
+
if (mark.state === 'processing') {
|
|
844
|
+
for (let spark = 0; spark < 3; spark++) {
|
|
845
|
+
const angle = animFrame * 0.24 + spark * Math.PI * 2 / 3;
|
|
846
|
+
const radius = slot.size / 2 + 1;
|
|
847
|
+
set(Math.round(slot.x + Math.cos(angle) * radius), Math.round(slot.y + bob + Math.sin(angle) * radius), [110, 235, 255]);
|
|
848
|
+
}
|
|
849
|
+
}
|
|
850
|
+
else if (mark.state === 'awaiting') {
|
|
851
|
+
set(Math.min(31, x0 + slot.size), Math.max(2, y0), [255, 190, 45]);
|
|
852
|
+
set(Math.min(31, x0 + slot.size), Math.max(2, y0 + 1), [255, 190, 45]);
|
|
853
|
+
}
|
|
854
|
+
});
|
|
855
|
+
if (marks.length === 0) {
|
|
856
|
+
for (const [x, y] of [[14, 12], [15, 11], [16, 12], [17, 11], [18, 12], [15, 14], [16, 15], [17, 14]]) {
|
|
857
|
+
set(x, y, [76, 206, 220]);
|
|
858
|
+
}
|
|
859
|
+
}
|
|
860
|
+
const primary = usageEvent?.codexRateLimits?.primary?.stale === true
|
|
861
|
+
? undefined : usageEvent?.codexRateLimits?.primary?.usedPercent;
|
|
862
|
+
const secondary = usageEvent?.codexRateLimits?.secondary?.stale === true
|
|
863
|
+
? undefined : usageEvent?.codexRateLimits?.secondary?.usedPercent;
|
|
864
|
+
const telemetry = [
|
|
865
|
+
[usageEvent?.fiveHourPercent, [42, 220, 154]],
|
|
866
|
+
[usageEvent?.sevenDayPercent, [54, 154, 255]],
|
|
867
|
+
[primary, [185, 86, 255]],
|
|
868
|
+
[secondary, [104, 116, 255]],
|
|
869
|
+
];
|
|
870
|
+
telemetry.forEach(([raw, brand], row) => {
|
|
871
|
+
const y = 28 + row;
|
|
872
|
+
for (let x = 0; x < 32; x++)
|
|
873
|
+
set(x, y, [5, 8, 14]);
|
|
874
|
+
if (raw == null)
|
|
875
|
+
return;
|
|
876
|
+
const pct = Math.max(0, Math.min(100, raw));
|
|
877
|
+
const color = pct >= 90 ? [255, 58, 72] : pct >= 70 ? [255, 183, 38] : brand;
|
|
878
|
+
set(0, y, brand);
|
|
879
|
+
set(1, y, brand);
|
|
880
|
+
const width = Math.round(pct / 100 * 29);
|
|
881
|
+
for (let x = 3; x < 3 + width; x++)
|
|
882
|
+
set(x, y, color);
|
|
883
|
+
});
|
|
533
884
|
}
|
|
534
885
|
/**
|
|
535
|
-
* Render a complete
|
|
536
|
-
* Returns
|
|
886
|
+
* Render a complete frame with camera system.
|
|
887
|
+
* Returns RGB buffer.
|
|
888
|
+
*
|
|
889
|
+
* `layout='micro'` renders the Timebox Mini Agent Beacon;
|
|
890
|
+
* `'standard'` is the full terrarium.
|
|
537
891
|
*/
|
|
538
|
-
export function renderFrame(stateEvent, usageEvent, sessions, timeOverrideMs) {
|
|
892
|
+
export function renderFrame(stateEvent, usageEvent, sessions, timeOverrideMs, size = 64, layout = 'standard') {
|
|
539
893
|
const worldBuf = new Uint8Array(W * W * 3);
|
|
540
|
-
const outputBuf = new Uint8Array(
|
|
894
|
+
const outputBuf = new Uint8Array(size * size * 3);
|
|
541
895
|
const animFrame = getAnimFrame(timeOverrideMs);
|
|
896
|
+
if (layout === 'micro') {
|
|
897
|
+
// Still sync creature instances so dominant-creature selection reflects live state.
|
|
898
|
+
syncCreatures(sessions, stateEvent);
|
|
899
|
+
renderMicroFrame(outputBuf, size, animFrame, stateEvent, sessions, usageEvent?.fiveHourPercent ?? 0);
|
|
900
|
+
return outputBuf;
|
|
901
|
+
}
|
|
902
|
+
if (size === 32) {
|
|
903
|
+
syncCreatures(sessions, stateEvent);
|
|
904
|
+
renderCompact32Frame(outputBuf, animFrame, stateEvent, sessions, usageEvent);
|
|
905
|
+
return outputBuf;
|
|
906
|
+
}
|
|
542
907
|
const state = stateEvent?.state ?? State.IDLE;
|
|
543
908
|
const usagePct = usageEvent?.fiveHourPercent ?? 0;
|
|
544
909
|
const surfaceY = SURFACE_Y;
|
|
545
910
|
const palette = ZONE_BLUE; // Water stays blue — usage shown only via HUD gauge
|
|
546
|
-
//
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|| (sessions?.some(s => s.agentType === 'openclaw') ?? false);
|
|
911
|
+
// Presence-driven SSOT: crayfish renders iff the daemon emitted an OpenClaw
|
|
912
|
+
// session (authenticated), not from raw gateway reachability/error flags.
|
|
913
|
+
const hasGateway = hasOpenClawSession(sessions ?? []);
|
|
550
914
|
// === Sync creature instances ===
|
|
551
915
|
syncCreatures(sessions, stateEvent);
|
|
552
916
|
// === Build active creatures list for camera ===
|
|
@@ -571,7 +935,19 @@ export function renderFrame(stateEvent, usageEvent, sessions, timeOverrideMs) {
|
|
|
571
935
|
const dt = lastRenderTime > 0 ? Math.min(5, (now - lastRenderTime) / 1000) : 1.0;
|
|
572
936
|
lastRenderTime = now;
|
|
573
937
|
const schoolPos = getSchoolCenter();
|
|
574
|
-
|
|
938
|
+
let camera = updateDirector(dt, activeCreatures, crayfishRouting, hasGateway ? { x: cfX, y: cfY } : null, schoolPos);
|
|
939
|
+
// Adaptive zoom out when multiple sessions are active to increase spacing & breathing room
|
|
940
|
+
const activeSessionCount = creatureInstances.size;
|
|
941
|
+
if (activeSessionCount > 1 && camera.zoom === 1.0) {
|
|
942
|
+
camera.zoom = Math.max(0.78, 1.0 - (activeSessionCount - 1) * 0.11);
|
|
943
|
+
}
|
|
944
|
+
camera.width = size; // Set camera target resolution width
|
|
945
|
+
// Snap the camera center to whole device pixels so a fixed sprite cell (a
|
|
946
|
+
// creature eye) doesn't sub-step as the camera lerps. Applied to the single
|
|
947
|
+
// camera shared by the background blit and every creature draw → they stay
|
|
948
|
+
// pixel-aligned. Must run after the adaptive-zoom tweak above so the final
|
|
949
|
+
// zoom is what we quantize against.
|
|
950
|
+
camera = quantizeCameraPixels(camera);
|
|
575
951
|
// ========================================
|
|
576
952
|
// Phase 1: Render environment → world buffer
|
|
577
953
|
// ========================================
|
|
@@ -636,20 +1012,16 @@ export function renderFrame(stateEvent, usageEvent, sessions, timeOverrideMs) {
|
|
|
636
1012
|
const spriteState = c.state === 'processing' ? 'working'
|
|
637
1013
|
: c.state === 'awaiting' ? 'asking'
|
|
638
1014
|
: 'idle'; // IDLE → idle (limbs move, body color preserved)
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
}
|
|
645
|
-
else {
|
|
646
|
-
drawOctopus(outputBuf, c.worldX, c.worldY, spriteState, animFrame + c.phaseOffset, camera, getOctopusPaletteForSession(sessionToneIndex));
|
|
647
|
-
}
|
|
1015
|
+
const glyph = c.creatureType === 'jellyfish' ? 'codex'
|
|
1016
|
+
: c.creatureType === 'opencode' ? 'openCode'
|
|
1017
|
+
: c.creatureType === 'antigravity' ? 'antigravity'
|
|
1018
|
+
: 'claudeCode';
|
|
1019
|
+
drawOfficialDotGlyph(outputBuf, glyph, c.worldX, c.worldY, spriteState, animFrame + c.phaseOffset, camera, sessionToneIndex, c.sizeScale);
|
|
648
1020
|
}
|
|
649
1021
|
// Crayfish — always drawn when gateway available; IDLE = sitting (subtle breathing only)
|
|
650
1022
|
if (hasGateway) {
|
|
651
1023
|
const gatewayHasError = stateEvent?.gatewayHasError ?? false;
|
|
652
|
-
|
|
1024
|
+
drawOfficialDotGlyph(outputBuf, 'openClaw', cfX, cfY, crayfishRouting ? 'working' : 'idle', animFrame, camera, 0, 1.0, gatewayHasError);
|
|
653
1025
|
}
|
|
654
1026
|
// ========================================
|
|
655
1027
|
// Phase 4: Screen-space overlays
|
|
@@ -657,8 +1029,8 @@ export function renderFrame(stateEvent, usageEvent, sessions, timeOverrideMs) {
|
|
|
657
1029
|
// Danger flash (>90% usage)
|
|
658
1030
|
if (usagePct >= 90) {
|
|
659
1031
|
const flashIntensity = (Math.sin(animFrame * 0.2) + 1) * 0.08;
|
|
660
|
-
for (let y = 0; y <
|
|
661
|
-
for (let x = 0; x <
|
|
1032
|
+
for (let y = 0; y < size; y++) {
|
|
1033
|
+
for (let x = 0; x < size; x++) {
|
|
662
1034
|
glowPixel(outputBuf, x, y, COLORS.stateError, flashIntensity);
|
|
663
1035
|
}
|
|
664
1036
|
}
|
|
@@ -670,9 +1042,14 @@ export function renderFrame(stateEvent, usageEvent, sessions, timeOverrideMs) {
|
|
|
670
1042
|
for (let i = 0; i < Math.min(sessionCount, 6); i++) {
|
|
671
1043
|
const dotX = 1 + i * 3; // 2px dot + 1px gap
|
|
672
1044
|
const c = orderedCreatures[i];
|
|
1045
|
+
// Color the dot by agent type so OpenCode is distinguishable, not painted as an octopus.
|
|
673
1046
|
const dotColor = c.creatureType === 'jellyfish'
|
|
674
1047
|
? getJellyfishPaletteForSession(i).body
|
|
675
|
-
:
|
|
1048
|
+
: c.creatureType === 'opencode'
|
|
1049
|
+
? getOpenCodePaletteForSession(i).outer
|
|
1050
|
+
: c.creatureType === 'antigravity'
|
|
1051
|
+
? getAntigravityPaletteForSession(i).yellow
|
|
1052
|
+
: getOctopusPaletteForSession(i).body;
|
|
676
1053
|
setPixel(outputBuf, dotX, 1, dotColor);
|
|
677
1054
|
setPixel(outputBuf, dotX + 1, 1, dotColor);
|
|
678
1055
|
setPixel(outputBuf, dotX, 2, dotColor);
|
|
@@ -686,11 +1063,32 @@ export function renderFrame(stateEvent, usageEvent, sessions, timeOverrideMs) {
|
|
|
686
1063
|
// ===== Disconnected Frame =====
|
|
687
1064
|
/** Render a static black frame with centered grey "OFFLINE" text. */
|
|
688
1065
|
export function renderDisconnectedFrame() {
|
|
689
|
-
const buf = new Uint8Array(
|
|
690
|
-
drawTextCentered(buf, 29,
|
|
1066
|
+
const buf = new Uint8Array(64 * 64 * 3); // black
|
|
1067
|
+
drawTextCentered(buf, 29, PASSIVE_OFFLINE_LABEL, '#555555');
|
|
691
1068
|
return buf;
|
|
692
1069
|
}
|
|
693
1070
|
// ===== Preview API (re-export camera controls) =====
|
|
1071
|
+
/**
|
|
1072
|
+
* Introspection seam: the creature set `renderFrame` would draw for this input,
|
|
1073
|
+
* without rasterizing anything. Counting marks back out of a 64×64 buffer is
|
|
1074
|
+
* far too fragile to assert on, so tests and the preview CLI read the resolved
|
|
1075
|
+
* world placement from here instead.
|
|
1076
|
+
*
|
|
1077
|
+
* This runs the same `syncCreatures` the renderer runs, so it mutates the module's
|
|
1078
|
+
* creature state exactly as a real frame would.
|
|
1079
|
+
*/
|
|
1080
|
+
export function getCreatureLayoutSnapshot(sessions, stateEvent) {
|
|
1081
|
+
syncCreatures(sessions, stateEvent);
|
|
1082
|
+
return [...creatureInstances.values()].map(c => ({
|
|
1083
|
+
sessionId: c.sessionId,
|
|
1084
|
+
agentType: c.agentType,
|
|
1085
|
+
creatureType: c.creatureType,
|
|
1086
|
+
state: c.state,
|
|
1087
|
+
worldX: c.worldX,
|
|
1088
|
+
worldY: c.worldY,
|
|
1089
|
+
sizeScale: c.sizeScale,
|
|
1090
|
+
}));
|
|
1091
|
+
}
|
|
694
1092
|
export { setZone, setOverride, resetDirector } from './pixoo-camera.js';
|
|
695
1093
|
export { ZONES } from './pixoo-camera.js';
|
|
696
1094
|
//# sourceMappingURL=pixoo-renderer.js.map
|