@agentdeck/bridge 0.1.0 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/dist/adapters/claude-code.d.ts +11 -27
- package/dist/adapters/claude-code.d.ts.map +1 -1
- package/dist/adapters/claude-code.js +29 -123
- package/dist/adapters/claude-code.js.map +1 -1
- package/dist/adapters/codex-cli.d.ts +21 -0
- package/dist/adapters/codex-cli.d.ts.map +1 -0
- package/dist/adapters/codex-cli.js +53 -0
- package/dist/adapters/codex-cli.js.map +1 -0
- package/dist/adapters/index.d.ts +4 -0
- package/dist/adapters/index.d.ts.map +1 -1
- package/dist/adapters/index.js +13 -0
- package/dist/adapters/index.js.map +1 -1
- package/dist/adapters/monitor.d.ts +34 -0
- package/dist/adapters/monitor.d.ts.map +1 -0
- package/dist/adapters/monitor.js +86 -0
- package/dist/adapters/monitor.js.map +1 -0
- package/dist/adapters/openclaw.d.ts +36 -2
- package/dist/adapters/openclaw.d.ts.map +1 -1
- package/dist/adapters/openclaw.js +329 -59
- package/dist/adapters/openclaw.js.map +1 -1
- package/dist/adapters/opencode-adapter.d.ts +48 -0
- package/dist/adapters/opencode-adapter.d.ts.map +1 -0
- package/dist/adapters/opencode-adapter.js +345 -0
- package/dist/adapters/opencode-adapter.js.map +1 -0
- package/dist/adapters/pty-adapter.d.ts +56 -0
- package/dist/adapters/pty-adapter.d.ts.map +1 -0
- package/dist/adapters/pty-adapter.js +143 -0
- package/dist/adapters/pty-adapter.js.map +1 -0
- package/dist/adb-reverse.d.ts +25 -0
- package/dist/adb-reverse.d.ts.map +1 -0
- package/dist/adb-reverse.js +129 -0
- package/dist/adb-reverse.js.map +1 -0
- package/dist/antigravity-local.d.ts +7 -0
- package/dist/antigravity-local.d.ts.map +1 -0
- package/dist/antigravity-local.js +153 -0
- package/dist/antigravity-local.js.map +1 -0
- package/dist/apme/adapters/claude-hook.d.ts +15 -0
- package/dist/apme/adapters/claude-hook.d.ts.map +1 -0
- package/dist/apme/adapters/claude-hook.js +85 -0
- package/dist/apme/adapters/claude-hook.js.map +1 -0
- package/dist/apme/adapters/claude-pty.d.ts +28 -0
- package/dist/apme/adapters/claude-pty.d.ts.map +1 -0
- package/dist/apme/adapters/claude-pty.js +88 -0
- package/dist/apme/adapters/claude-pty.js.map +1 -0
- package/dist/apme/adapters/timeline.d.ts +16 -0
- package/dist/apme/adapters/timeline.d.ts.map +1 -0
- package/dist/apme/adapters/timeline.js +66 -0
- package/dist/apme/adapters/timeline.js.map +1 -0
- package/dist/apme/classifier.d.ts +53 -0
- package/dist/apme/classifier.d.ts.map +1 -0
- package/dist/apme/classifier.js +266 -0
- package/dist/apme/classifier.js.map +1 -0
- package/dist/apme/claude-transcript-reader.d.ts +38 -0
- package/dist/apme/claude-transcript-reader.d.ts.map +1 -0
- package/dist/apme/claude-transcript-reader.js +127 -0
- package/dist/apme/claude-transcript-reader.js.map +1 -0
- package/dist/apme/collector.d.ts +108 -0
- package/dist/apme/collector.d.ts.map +1 -0
- package/dist/apme/collector.js +659 -0
- package/dist/apme/collector.js.map +1 -0
- package/dist/apme/dashboard-html.d.ts +9 -0
- package/dist/apme/dashboard-html.d.ts.map +1 -0
- package/dist/apme/dashboard-html.js +416 -0
- package/dist/apme/dashboard-html.js.map +1 -0
- package/dist/apme/http.d.ts +19 -0
- package/dist/apme/http.d.ts.map +1 -0
- package/dist/apme/http.js +210 -0
- package/dist/apme/http.js.map +1 -0
- package/dist/apme/hw-sampler.d.ts +30 -0
- package/dist/apme/hw-sampler.d.ts.map +1 -0
- package/dist/apme/hw-sampler.js +106 -0
- package/dist/apme/hw-sampler.js.map +1 -0
- package/dist/apme/index.d.ts +43 -0
- package/dist/apme/index.d.ts.map +1 -0
- package/dist/apme/index.js +75 -0
- package/dist/apme/index.js.map +1 -0
- package/dist/apme/outcome.d.ts +48 -0
- package/dist/apme/outcome.d.ts.map +1 -0
- package/dist/apme/outcome.js +232 -0
- package/dist/apme/outcome.js.map +1 -0
- package/dist/apme/recommend.d.ts +32 -0
- package/dist/apme/recommend.d.ts.map +1 -0
- package/dist/apme/recommend.js +41 -0
- package/dist/apme/recommend.js.map +1 -0
- package/dist/apme/runner.d.ts +94 -0
- package/dist/apme/runner.d.ts.map +1 -0
- package/dist/apme/runner.js +812 -0
- package/dist/apme/runner.js.map +1 -0
- package/dist/apme/settings.d.ts +65 -0
- package/dist/apme/settings.d.ts.map +1 -0
- package/dist/apme/settings.js +97 -0
- package/dist/apme/settings.js.map +1 -0
- package/dist/apme/store.d.ts +99 -0
- package/dist/apme/store.d.ts.map +1 -0
- package/dist/apme/store.js +911 -0
- package/dist/apme/store.js.map +1 -0
- package/dist/apme/tuner.d.ts +64 -0
- package/dist/apme/tuner.d.ts.map +1 -0
- package/dist/apme/tuner.js +297 -0
- package/dist/apme/tuner.js.map +1 -0
- package/dist/apme/types.d.ts +11 -0
- package/dist/apme/types.d.ts.map +1 -0
- package/dist/apme/types.js +10 -0
- package/dist/apme/types.js.map +1 -0
- package/dist/auth.d.ts +1 -1
- package/dist/auth.d.ts.map +1 -1
- package/dist/auth.js +15 -5
- package/dist/auth.js.map +1 -1
- package/dist/bridge-core.d.ts +162 -0
- package/dist/bridge-core.d.ts.map +1 -0
- package/dist/bridge-core.js +627 -0
- package/dist/bridge-core.js.map +1 -0
- package/dist/check-deps.d.ts +3 -1
- package/dist/check-deps.d.ts.map +1 -1
- package/dist/check-deps.js +34 -13
- package/dist/check-deps.js.map +1 -1
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +1235 -0
- package/dist/cli.js.map +1 -0
- package/dist/codex-auth.d.ts +10 -0
- package/dist/codex-auth.d.ts.map +1 -0
- package/dist/codex-auth.js +63 -0
- package/dist/codex-auth.js.map +1 -0
- package/dist/codex-output-parser.d.ts +21 -0
- package/dist/codex-output-parser.d.ts.map +1 -0
- package/dist/codex-output-parser.js +253 -0
- package/dist/codex-output-parser.js.map +1 -0
- package/dist/d200h/hid-protocol.d.ts +62 -0
- package/dist/d200h/hid-protocol.d.ts.map +1 -0
- package/dist/d200h/hid-protocol.js +124 -0
- package/dist/d200h/hid-protocol.js.map +1 -0
- package/dist/d200h/image-renderer.d.ts +36 -0
- package/dist/d200h/image-renderer.d.ts.map +1 -0
- package/dist/d200h/image-renderer.js +487 -0
- package/dist/d200h/image-renderer.js.map +1 -0
- package/dist/daemon-server.d.ts +19 -0
- package/dist/daemon-server.d.ts.map +1 -0
- package/dist/daemon-server.js +1168 -0
- package/dist/daemon-server.js.map +1 -0
- package/dist/daemon-ws-client.d.ts +82 -0
- package/dist/daemon-ws-client.d.ts.map +1 -0
- package/dist/daemon-ws-client.js +179 -0
- package/dist/daemon-ws-client.js.map +1 -0
- package/dist/daemon.d.ts +10 -0
- package/dist/daemon.d.ts.map +1 -0
- package/dist/daemon.js +56 -0
- package/dist/daemon.js.map +1 -0
- package/dist/display-monitor.d.ts +25 -0
- package/dist/display-monitor.d.ts.map +1 -0
- package/dist/display-monitor.js +143 -0
- package/dist/display-monitor.js.map +1 -0
- package/dist/esp32-serial.d.ts +112 -0
- package/dist/esp32-serial.d.ts.map +1 -0
- package/dist/esp32-serial.js +502 -0
- package/dist/esp32-serial.js.map +1 -0
- package/dist/gateway-probe.d.ts +12 -0
- package/dist/gateway-probe.d.ts.map +1 -0
- package/dist/gateway-probe.js +39 -0
- package/dist/gateway-probe.js.map +1 -0
- package/dist/hook-migration.d.ts +2 -0
- package/dist/hook-migration.d.ts.map +1 -0
- package/dist/hook-migration.js +104 -0
- package/dist/hook-migration.js.map +1 -0
- package/dist/hook-server.d.ts +29 -0
- package/dist/hook-server.d.ts.map +1 -1
- package/dist/hook-server.js +366 -8
- package/dist/hook-server.js.map +1 -1
- package/dist/index.d.ts +20 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1304 -562
- package/dist/index.js.map +1 -1
- package/dist/log-stream.d.ts +26 -0
- package/dist/log-stream.d.ts.map +1 -0
- package/dist/log-stream.js +111 -0
- package/dist/log-stream.js.map +1 -0
- package/dist/logger.d.ts +9 -2
- package/dist/logger.d.ts.map +1 -1
- package/dist/logger.js +36 -5
- package/dist/logger.js.map +1 -1
- package/dist/mdns.d.ts +15 -1
- package/dist/mdns.d.ts.map +1 -1
- package/dist/mdns.js +143 -28
- package/dist/mdns.js.map +1 -1
- package/dist/mlx-probe.d.ts +14 -0
- package/dist/mlx-probe.d.ts.map +1 -0
- package/dist/mlx-probe.js +44 -0
- package/dist/mlx-probe.js.map +1 -0
- package/dist/model-catalog.d.ts +5 -3
- package/dist/model-catalog.d.ts.map +1 -1
- package/dist/model-catalog.js +45 -29
- package/dist/model-catalog.js.map +1 -1
- package/dist/modules/adb-module.d.ts +14 -0
- package/dist/modules/adb-module.d.ts.map +1 -0
- package/dist/modules/adb-module.js +36 -0
- package/dist/modules/adb-module.js.map +1 -0
- package/dist/modules/d200h-module.d.ts +43 -0
- package/dist/modules/d200h-module.d.ts.map +1 -0
- package/dist/modules/d200h-module.js +360 -0
- package/dist/modules/d200h-module.js.map +1 -0
- package/dist/modules/d200h-renderer.d.ts +7 -0
- package/dist/modules/d200h-renderer.d.ts.map +1 -0
- package/dist/modules/d200h-renderer.js +321 -0
- package/dist/modules/d200h-renderer.js.map +1 -0
- package/dist/modules/index.d.ts +23 -0
- package/dist/modules/index.d.ts.map +1 -0
- package/dist/modules/index.js +65 -0
- package/dist/modules/index.js.map +1 -0
- package/dist/modules/mdns-module.d.ts +12 -0
- package/dist/modules/mdns-module.d.ts.map +1 -0
- package/dist/modules/mdns-module.js +21 -0
- package/dist/modules/mdns-module.js.map +1 -0
- package/dist/modules/pixoo-module.d.ts +8 -0
- package/dist/modules/pixoo-module.d.ts.map +1 -0
- package/dist/modules/pixoo-module.js +24 -0
- package/dist/modules/pixoo-module.js.map +1 -0
- package/dist/modules/serial-module.d.ts +18 -0
- package/dist/modules/serial-module.d.ts.map +1 -0
- package/dist/modules/serial-module.js +47 -0
- package/dist/modules/serial-module.js.map +1 -0
- package/dist/modules/types.d.ts +39 -0
- package/dist/modules/types.d.ts.map +1 -0
- package/dist/modules/types.js +2 -0
- package/dist/modules/types.js.map +1 -0
- package/dist/ollama-probe.d.ts +13 -0
- package/dist/ollama-probe.d.ts.map +1 -0
- package/dist/ollama-probe.js +46 -0
- package/dist/ollama-probe.js.map +1 -0
- package/dist/opencode-client.d.ts +126 -0
- package/dist/opencode-client.d.ts.map +1 -0
- package/dist/opencode-client.js +181 -0
- package/dist/opencode-client.js.map +1 -0
- package/dist/output-parser.d.ts +3 -0
- package/dist/output-parser.d.ts.map +1 -1
- package/dist/output-parser.js +29 -3
- package/dist/output-parser.js.map +1 -1
- package/dist/passive-observer.d.ts +38 -0
- package/dist/passive-observer.d.ts.map +1 -0
- package/dist/passive-observer.js +632 -0
- package/dist/passive-observer.js.map +1 -0
- package/dist/pixoo/pixoo-bridge.d.ts +50 -0
- package/dist/pixoo/pixoo-bridge.d.ts.map +1 -0
- package/dist/pixoo/pixoo-bridge.js +280 -0
- package/dist/pixoo/pixoo-bridge.js.map +1 -0
- package/dist/pixoo/pixoo-camera.d.ts +80 -0
- package/dist/pixoo/pixoo-camera.d.ts.map +1 -0
- package/dist/pixoo/pixoo-camera.js +271 -0
- package/dist/pixoo/pixoo-camera.js.map +1 -0
- package/dist/pixoo/pixoo-client.d.ts +67 -0
- package/dist/pixoo/pixoo-client.d.ts.map +1 -0
- package/dist/pixoo/pixoo-client.js +335 -0
- package/dist/pixoo/pixoo-client.js.map +1 -0
- package/dist/pixoo/pixoo-font.d.ts +24 -0
- package/dist/pixoo/pixoo-font.d.ts.map +1 -0
- package/dist/pixoo/pixoo-font.js +121 -0
- package/dist/pixoo/pixoo-font.js.map +1 -0
- package/dist/pixoo/pixoo-renderer.d.ts +32 -0
- package/dist/pixoo/pixoo-renderer.d.ts.map +1 -0
- package/dist/pixoo/pixoo-renderer.js +696 -0
- package/dist/pixoo/pixoo-renderer.js.map +1 -0
- package/dist/pixoo/pixoo-settings.d.ts +10 -0
- package/dist/pixoo/pixoo-settings.d.ts.map +1 -0
- package/dist/pixoo/pixoo-settings.js +47 -0
- package/dist/pixoo/pixoo-settings.js.map +1 -0
- package/dist/pixoo/pixoo-sprites.d.ts +187 -0
- package/dist/pixoo/pixoo-sprites.d.ts.map +1 -0
- package/dist/pixoo/pixoo-sprites.js +926 -0
- package/dist/pixoo/pixoo-sprites.js.map +1 -0
- package/dist/pty-manager.d.ts +1 -1
- package/dist/pty-manager.d.ts.map +1 -1
- package/dist/pty-manager.js +36 -15
- package/dist/pty-manager.js.map +1 -1
- package/dist/session-aggregator.d.ts +41 -0
- package/dist/session-aggregator.d.ts.map +1 -0
- package/dist/session-aggregator.js +76 -0
- package/dist/session-aggregator.js.map +1 -0
- package/dist/session-focus-relay.d.ts +35 -0
- package/dist/session-focus-relay.d.ts.map +1 -0
- package/dist/session-focus-relay.js +127 -0
- package/dist/session-focus-relay.js.map +1 -0
- package/dist/session-registry.d.ts +88 -1
- package/dist/session-registry.d.ts.map +1 -1
- package/dist/session-registry.js +222 -15
- package/dist/session-registry.js.map +1 -1
- package/dist/session-timeline-relay.d.ts +27 -0
- package/dist/session-timeline-relay.d.ts.map +1 -0
- package/dist/session-timeline-relay.js +124 -0
- package/dist/session-timeline-relay.js.map +1 -0
- package/dist/state-machine.d.ts +1 -0
- package/dist/state-machine.d.ts.map +1 -1
- package/dist/state-machine.js +14 -0
- package/dist/state-machine.js.map +1 -1
- package/dist/terminal-status.d.ts +28 -0
- package/dist/terminal-status.d.ts.map +1 -0
- package/dist/terminal-status.js +152 -0
- package/dist/terminal-status.js.map +1 -0
- package/dist/timeline-store.d.ts +22 -0
- package/dist/timeline-store.d.ts.map +1 -0
- package/dist/timeline-store.js +79 -0
- package/dist/timeline-store.js.map +1 -0
- package/dist/timeline-summarizer.d.ts +14 -0
- package/dist/timeline-summarizer.d.ts.map +1 -0
- package/dist/timeline-summarizer.js +154 -0
- package/dist/timeline-summarizer.js.map +1 -0
- package/dist/tts.d.ts +25 -0
- package/dist/tts.d.ts.map +1 -0
- package/dist/tts.js +99 -0
- package/dist/tts.js.map +1 -0
- package/dist/tui/ansi.d.ts +92 -0
- package/dist/tui/ansi.d.ts.map +1 -0
- package/dist/tui/ansi.js +181 -0
- package/dist/tui/ansi.js.map +1 -0
- package/dist/tui/dashboard.d.ts +35 -0
- package/dist/tui/dashboard.d.ts.map +1 -0
- package/dist/tui/dashboard.js +436 -0
- package/dist/tui/dashboard.js.map +1 -0
- package/dist/tui/gauge.d.ts +15 -0
- package/dist/tui/gauge.d.ts.map +1 -0
- package/dist/tui/gauge.js +82 -0
- package/dist/tui/gauge.js.map +1 -0
- package/dist/tui/renderer.d.ts +15 -0
- package/dist/tui/renderer.d.ts.map +1 -0
- package/dist/tui/renderer.js +758 -0
- package/dist/tui/renderer.js.map +1 -0
- package/dist/tui/screen.d.ts +27 -0
- package/dist/tui/screen.d.ts.map +1 -0
- package/dist/tui/screen.js +109 -0
- package/dist/tui/screen.js.map +1 -0
- package/dist/tui/terrarium.d.ts +96 -0
- package/dist/tui/terrarium.d.ts.map +1 -0
- package/dist/tui/terrarium.js +912 -0
- package/dist/tui/terrarium.js.map +1 -0
- package/dist/types.d.ts +4 -2
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +3 -2
- package/dist/types.js.map +1 -1
- package/dist/usage-api.d.ts +9 -2
- package/dist/usage-api.d.ts.map +1 -1
- package/dist/usage-api.js +224 -42
- package/dist/usage-api.js.map +1 -1
- package/dist/usage-event.d.ts +12 -0
- package/dist/usage-event.d.ts.map +1 -0
- package/dist/usage-event.js +78 -0
- package/dist/usage-event.js.map +1 -0
- package/dist/usage-tracker.d.ts +4 -0
- package/dist/usage-tracker.d.ts.map +1 -1
- package/dist/usage-tracker.js +17 -0
- package/dist/usage-tracker.js.map +1 -1
- package/dist/utility-proxy.d.ts +30 -0
- package/dist/utility-proxy.d.ts.map +1 -0
- package/dist/utility-proxy.js +217 -0
- package/dist/utility-proxy.js.map +1 -0
- package/dist/utils/project-name.d.ts +22 -0
- package/dist/utils/project-name.d.ts.map +1 -0
- package/dist/utils/project-name.js +72 -0
- package/dist/utils/project-name.js.map +1 -0
- package/dist/version-check.d.ts +26 -0
- package/dist/version-check.d.ts.map +1 -0
- package/dist/version-check.js +225 -0
- package/dist/version-check.js.map +1 -0
- package/dist/voice-assistant.d.ts +63 -0
- package/dist/voice-assistant.d.ts.map +1 -0
- package/dist/voice-assistant.js +406 -0
- package/dist/voice-assistant.js.map +1 -0
- package/dist/voice.d.ts +2 -0
- package/dist/voice.d.ts.map +1 -1
- package/dist/voice.js +26 -1
- package/dist/voice.js.map +1 -1
- package/dist/wake-word.d.ts +35 -0
- package/dist/wake-word.d.ts.map +1 -0
- package/dist/wake-word.js +225 -0
- package/dist/wake-word.js.map +1 -0
- package/dist/whisper-server-manager.d.ts +1 -1
- package/dist/whisper-server-manager.js +1 -1
- package/dist/wifi-config.d.ts +24 -0
- package/dist/wifi-config.d.ts.map +1 -0
- package/dist/wifi-config.js +84 -0
- package/dist/wifi-config.js.map +1 -0
- package/dist/ws-server.d.ts +12 -0
- package/dist/ws-server.d.ts.map +1 -1
- package/dist/ws-server.js +81 -3
- package/dist/ws-server.js.map +1 -1
- package/package.json +21 -13
|
@@ -0,0 +1,758 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TUI Dashboard renderer — layout calculation + panel rendering.
|
|
3
|
+
*
|
|
4
|
+
* Width invariant: every output line is exactly `cols` characters wide (visually).
|
|
5
|
+
* Box structure:
|
|
6
|
+
* Wide: ┌─leftW─┬─rightW─┐ where leftW + rightW + 3 = cols
|
|
7
|
+
* Std/Nar: ┌──w────┐ where w + 2 = cols
|
|
8
|
+
* Split: │ lh │ rh │ where lh + rh + 3 = cols
|
|
9
|
+
*/
|
|
10
|
+
import { cursor, screen as screenCodes, RESET, colors, box, hLine, sgr, stateColor, stateIcon, truncText, padRight, visLen, terminalCaps, centerText, fg, } from './ansi.js';
|
|
11
|
+
import { blockGauge, resetTimeStr, formatTokens } from './gauge.js';
|
|
12
|
+
import { sortSessions, assignDisplayNames } from '@agentdeck/shared';
|
|
13
|
+
// ===== Layout Breakpoints =====
|
|
14
|
+
export function getLayout(cols, rows) {
|
|
15
|
+
if (cols >= 120)
|
|
16
|
+
return 'wide';
|
|
17
|
+
if (cols >= 80)
|
|
18
|
+
return 'standard';
|
|
19
|
+
return 'narrow';
|
|
20
|
+
}
|
|
21
|
+
export function shouldShowTerrarium(cols, rows) {
|
|
22
|
+
if (cols < 60)
|
|
23
|
+
return false;
|
|
24
|
+
if (rows < 16)
|
|
25
|
+
return false;
|
|
26
|
+
return true;
|
|
27
|
+
}
|
|
28
|
+
// ===== Border Line Builder =====
|
|
29
|
+
function borderFill(prefix, suffix, targetWidth) {
|
|
30
|
+
const fillLen = Math.max(0, targetWidth - visLen(prefix) - visLen(suffix));
|
|
31
|
+
return prefix + `${colors.border}${hLine(fillLen)}${RESET}` + suffix;
|
|
32
|
+
}
|
|
33
|
+
// ===== Pixel Font (4 wide × 6 tall → 4×3 half-block) =====
|
|
34
|
+
const FONT = {
|
|
35
|
+
A: ['.##.', '#..#', '####', '#..#', '#..#', '....'],
|
|
36
|
+
G: ['.###', '#...', '#.##', '#..#', '.##.', '....'],
|
|
37
|
+
E: ['####', '#...', '###.', '#...', '####', '....'],
|
|
38
|
+
N: ['#..#', '##.#', '#.##', '#..#', '#..#', '....'],
|
|
39
|
+
T: ['####', '.##.', '.##.', '.##.', '.##.', '....'],
|
|
40
|
+
D: ['###.', '#..#', '#..#', '#..#', '###.', '....'],
|
|
41
|
+
C: ['.###', '#...', '#...', '#...', '.###', '....'],
|
|
42
|
+
K: ['#..#', '#.#.', '##..', '#.#.', '#..#', '....'],
|
|
43
|
+
};
|
|
44
|
+
/** Render a word in half-block pixel font. Returns 3 terminal lines. */
|
|
45
|
+
function renderPixelFont(word) {
|
|
46
|
+
const result = ['', '', ''];
|
|
47
|
+
for (let li = 0; li < word.length; li++) {
|
|
48
|
+
if (li > 0) {
|
|
49
|
+
result[0] += ' ';
|
|
50
|
+
result[1] += ' ';
|
|
51
|
+
result[2] += ' ';
|
|
52
|
+
}
|
|
53
|
+
const pixels = FONT[word[li]];
|
|
54
|
+
if (!pixels)
|
|
55
|
+
continue;
|
|
56
|
+
for (let hr = 0; hr < 3; hr++) {
|
|
57
|
+
const topRow = pixels[hr * 2];
|
|
58
|
+
const botRow = pixels[hr * 2 + 1];
|
|
59
|
+
for (let col = 0; col < 4; col++) {
|
|
60
|
+
const top = topRow[col] === '#';
|
|
61
|
+
const bot = botRow[col] === '#';
|
|
62
|
+
if (top && bot)
|
|
63
|
+
result[hr] += '\u2588'; // █
|
|
64
|
+
else if (top)
|
|
65
|
+
result[hr] += '\u2580'; // ▀
|
|
66
|
+
else if (bot)
|
|
67
|
+
result[hr] += '\u2584'; // ▄
|
|
68
|
+
else
|
|
69
|
+
result[hr] += ' ';
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
return result;
|
|
74
|
+
}
|
|
75
|
+
// Pre-render logo lines — "AGENT" + "DECK" stacked, sky blue
|
|
76
|
+
const LOGO_AGENT = renderPixelFont('AGENT'); // 3 lines, 24 chars wide
|
|
77
|
+
const LOGO_DECK = renderPixelFont('DECK'); // 3 lines, 19 chars wide
|
|
78
|
+
// ===== Timeline Icons =====
|
|
79
|
+
function typeIcon(type) {
|
|
80
|
+
if (!terminalCaps.unicode) {
|
|
81
|
+
switch (type) {
|
|
82
|
+
case 'chat_start': return '>';
|
|
83
|
+
case 'chat_end': return '=';
|
|
84
|
+
case 'chat_response': return ':';
|
|
85
|
+
case 'tool_request':
|
|
86
|
+
case 'tool_exec': return '*';
|
|
87
|
+
case 'tool_resolved': return '+';
|
|
88
|
+
case 'error': return 'x';
|
|
89
|
+
case 'model_call':
|
|
90
|
+
case 'model_response': return 'm';
|
|
91
|
+
case 'memory_recall': return 'r';
|
|
92
|
+
case 'scheduled': return 's';
|
|
93
|
+
case 'user_action': return 'u';
|
|
94
|
+
case 'eval_result': return '#';
|
|
95
|
+
default: return '*';
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
switch (type) {
|
|
99
|
+
case 'chat_start':
|
|
100
|
+
case 'user_action': return '\u25B6';
|
|
101
|
+
case 'chat_end': return '\u25A0';
|
|
102
|
+
case 'chat_response': return '\u25A1';
|
|
103
|
+
case 'tool_request':
|
|
104
|
+
case 'tool_exec': return '\u25C6';
|
|
105
|
+
case 'tool_resolved': return '\u2713';
|
|
106
|
+
case 'error': return '\u2717';
|
|
107
|
+
case 'model_call':
|
|
108
|
+
case 'model_response': return '\u25C8';
|
|
109
|
+
case 'memory_recall': return '\u25CC';
|
|
110
|
+
case 'scheduled': return '\u25D1';
|
|
111
|
+
case 'eval_result': return '\u2605'; // ★
|
|
112
|
+
default: return '\u25C6';
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
function typeColor(type) {
|
|
116
|
+
switch (type) {
|
|
117
|
+
case 'chat_start':
|
|
118
|
+
case 'user_action': return colors.chat;
|
|
119
|
+
case 'chat_end':
|
|
120
|
+
case 'chat_response': return colors.end;
|
|
121
|
+
case 'tool_request':
|
|
122
|
+
case 'tool_exec':
|
|
123
|
+
case 'tool_resolved': return colors.tool;
|
|
124
|
+
case 'error': return colors.errorTl;
|
|
125
|
+
case 'model_call':
|
|
126
|
+
case 'model_response': return sgr(35);
|
|
127
|
+
case 'memory_recall': return sgr(33);
|
|
128
|
+
case 'eval_result': return sgr(33); // yellow/amber
|
|
129
|
+
default: return colors.dim;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
const SPINNER_FRAMES = ['\u280B', '\u2819', '\u2839', '\u2838', '\u283C', '\u2834', '\u2826', '\u2827', '\u2807', '\u280F'];
|
|
133
|
+
export function spinner(frame) {
|
|
134
|
+
return SPINNER_FRAMES[Math.floor(frame / 2) % SPINNER_FRAMES.length];
|
|
135
|
+
}
|
|
136
|
+
// ===== Creature Icon Helper =====
|
|
137
|
+
function creatureEmoji(agentType) {
|
|
138
|
+
if (!terminalCaps.emoji) {
|
|
139
|
+
if (agentType === 'daemon')
|
|
140
|
+
return 'D';
|
|
141
|
+
if (agentType === 'openclaw')
|
|
142
|
+
return 'C';
|
|
143
|
+
if (agentType === 'codex-cli')
|
|
144
|
+
return 'X';
|
|
145
|
+
if (agentType === 'opencode')
|
|
146
|
+
return 'O';
|
|
147
|
+
return '*';
|
|
148
|
+
}
|
|
149
|
+
if (agentType === 'daemon')
|
|
150
|
+
return '\u2699\uFE0F'; // ⚙️
|
|
151
|
+
if (agentType === 'openclaw')
|
|
152
|
+
return '\uD83E\uDD9E'; // 🦞
|
|
153
|
+
if (agentType === 'codex-cli')
|
|
154
|
+
return '\u2601'; // ☁ (cloud — matches creature)
|
|
155
|
+
if (agentType === 'opencode')
|
|
156
|
+
return '\u25A3'; // ▣ (nested square — matches creature)
|
|
157
|
+
return '\u273B'; // ✻ (teardrop-spoked asterisk — Claude sparkle)
|
|
158
|
+
}
|
|
159
|
+
function creatureBrandColor(agentType) {
|
|
160
|
+
if (!terminalCaps.trueColor)
|
|
161
|
+
return '';
|
|
162
|
+
switch (agentType) {
|
|
163
|
+
case 'claude-code': return fg(192, 112, 88); // terracotta
|
|
164
|
+
case 'openclaw': return fg(255, 77, 77); // red
|
|
165
|
+
case 'codex-cli': return fg(177, 167, 255); // indigo
|
|
166
|
+
case 'opencode': return fg(241, 236, 236); // warm gray (#F1ECEC)
|
|
167
|
+
default: return '';
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
function compactStateLabel(state) {
|
|
171
|
+
switch (state) {
|
|
172
|
+
case 'processing': return 'PROC';
|
|
173
|
+
case 'awaiting_permission': return 'PERM';
|
|
174
|
+
case 'awaiting_option': return 'OPT';
|
|
175
|
+
case 'awaiting_diff': return 'DIFF';
|
|
176
|
+
case 'disconnected': return 'DISC';
|
|
177
|
+
case 'idle':
|
|
178
|
+
default:
|
|
179
|
+
return 'IDLE';
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
function currentSessionSummary(state, width) {
|
|
183
|
+
const parts = [];
|
|
184
|
+
if (state.projectName)
|
|
185
|
+
parts.push(state.projectName);
|
|
186
|
+
if (state.modelName)
|
|
187
|
+
parts.push(state.modelName);
|
|
188
|
+
if (state.state)
|
|
189
|
+
parts.push(compactStateLabel(state.state));
|
|
190
|
+
if (parts.length === 0)
|
|
191
|
+
return 'No session';
|
|
192
|
+
return truncText(parts.join(' · '), width);
|
|
193
|
+
}
|
|
194
|
+
// stateRank, sortSessions imported from @agentdeck/shared
|
|
195
|
+
function sessionHotkeyLabel(index) {
|
|
196
|
+
if (index === null || index > 8)
|
|
197
|
+
return '·';
|
|
198
|
+
return String(index + 1);
|
|
199
|
+
}
|
|
200
|
+
function sessionHotkeyIndex(session, nextIndex) {
|
|
201
|
+
if (!session.port || session.controlMode === 'observed')
|
|
202
|
+
return null;
|
|
203
|
+
return nextIndex;
|
|
204
|
+
}
|
|
205
|
+
function observedDetailParts(session) {
|
|
206
|
+
if (!session)
|
|
207
|
+
return [];
|
|
208
|
+
const parts = [];
|
|
209
|
+
if (session.controlMode === 'observed')
|
|
210
|
+
parts.push('observed');
|
|
211
|
+
if (typeof session.contextPercent === 'number')
|
|
212
|
+
parts.push(`${Math.round(session.contextPercent)}% ctx`);
|
|
213
|
+
if (session.currentTask)
|
|
214
|
+
parts.push(session.currentTask);
|
|
215
|
+
return parts;
|
|
216
|
+
}
|
|
217
|
+
// ===== Panel Renderers =====
|
|
218
|
+
function renderAgentLines(state, maxWidth, useLogo) {
|
|
219
|
+
const lines = [];
|
|
220
|
+
// Big pixel-font logo (AGENT + DECK stacked, sky blue)
|
|
221
|
+
const logoColor = terminalCaps.trueColor ? fg(100, 180, 255) : colors.header; // sky blue
|
|
222
|
+
if (useLogo && maxWidth >= 24) {
|
|
223
|
+
for (const l of LOGO_AGENT)
|
|
224
|
+
lines.push(`${logoColor}${l}${RESET}`);
|
|
225
|
+
for (const l of LOGO_DECK)
|
|
226
|
+
lines.push(`${logoColor} ${l}${RESET}`);
|
|
227
|
+
}
|
|
228
|
+
else if (useLogo && maxWidth >= 10) {
|
|
229
|
+
lines.push(`${logoColor} AgentDeck${RESET}`);
|
|
230
|
+
}
|
|
231
|
+
lines.push('');
|
|
232
|
+
// Session list
|
|
233
|
+
const renderSession = (proj, model, sessState, agentType, hotkeyIndex, session) => {
|
|
234
|
+
const col = stateColor(sessState);
|
|
235
|
+
const hotkey = `${colors.dim}[${sessionHotkeyLabel(hotkeyIndex)}]${RESET}`;
|
|
236
|
+
const name = truncText(proj, maxWidth - 16);
|
|
237
|
+
const emoji = `${creatureBrandColor(agentType)}${creatureEmoji(agentType)}${RESET}`;
|
|
238
|
+
const secondary = [model, ...observedDetailParts(session), compactStateLabel(sessState)]
|
|
239
|
+
.filter(Boolean)
|
|
240
|
+
.join(' - ');
|
|
241
|
+
lines.push(` ${hotkey} ${emoji} ${col}${name}${RESET}`);
|
|
242
|
+
lines.push(`${colors.dim} ${truncText(secondary, maxWidth - 4)}${RESET}`);
|
|
243
|
+
};
|
|
244
|
+
// #N suffix via shared assignDisplayNames
|
|
245
|
+
// Daemon mode: sessions list already contains all agents (including virtual OpenClaw).
|
|
246
|
+
// Also applies when daemon relays OpenClaw (agentType='openclaw' but sessions has the entry).
|
|
247
|
+
// Session bridge mode: sessions has siblings only — prepend self.
|
|
248
|
+
const isDaemonLike = state.agentType === 'daemon' ||
|
|
249
|
+
(state.agentType && state.sessions.some(s => s.agentType === state.agentType));
|
|
250
|
+
if (isDaemonLike) {
|
|
251
|
+
const sorted = sortSessions(state.sessions);
|
|
252
|
+
const displayed = assignDisplayNames(sorted.map(s => ({
|
|
253
|
+
id: s.id, projectName: s.projectName || 'unknown', agentType: s.agentType, state: s.state,
|
|
254
|
+
modelName: s.modelName,
|
|
255
|
+
port: s.port, controlMode: s.controlMode,
|
|
256
|
+
currentTask: s.currentTask,
|
|
257
|
+
contextPercent: s.contextPercent,
|
|
258
|
+
totalTokens: s.totalTokens,
|
|
259
|
+
})));
|
|
260
|
+
let focusableIndex = 0;
|
|
261
|
+
for (const d of displayed) {
|
|
262
|
+
const hotkeyIndex = sessionHotkeyIndex(d.session, focusableIndex);
|
|
263
|
+
if (hotkeyIndex !== null)
|
|
264
|
+
focusableIndex += 1;
|
|
265
|
+
renderSession(d.displayName, d.session.modelName, d.session.state || 'idle', d.session.agentType, hotkeyIndex, d.session);
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
else if (state.state) {
|
|
269
|
+
// Self (primary session)
|
|
270
|
+
renderSession(state.projectName || 'unknown', state.modelName ?? undefined, state.state, state.agentType ?? undefined, null);
|
|
271
|
+
// Siblings — include self as virtual entry for correct #N numbering
|
|
272
|
+
const sorted = sortSessions(state.sessions);
|
|
273
|
+
const selfEntry = {
|
|
274
|
+
id: '__self__', projectName: state.projectName || 'unknown',
|
|
275
|
+
agentType: state.agentType ?? undefined, state: state.state,
|
|
276
|
+
};
|
|
277
|
+
const allForNumbering = [selfEntry, ...sorted.map(s => ({
|
|
278
|
+
id: s.id, projectName: s.projectName || 'unknown', agentType: s.agentType, state: s.state,
|
|
279
|
+
modelName: s.modelName,
|
|
280
|
+
port: s.port, controlMode: s.controlMode,
|
|
281
|
+
currentTask: s.currentTask,
|
|
282
|
+
contextPercent: s.contextPercent,
|
|
283
|
+
totalTokens: s.totalTokens,
|
|
284
|
+
}))];
|
|
285
|
+
const displayed = assignDisplayNames(allForNumbering);
|
|
286
|
+
// Skip the self entry (index 0), render siblings with hotkey indices
|
|
287
|
+
let focusableIndex = 0;
|
|
288
|
+
for (let i = 1; i < displayed.length; i++) {
|
|
289
|
+
const d = displayed[i];
|
|
290
|
+
const sessionInfo = d.session;
|
|
291
|
+
const hotkeyIndex = sessionHotkeyIndex(sessionInfo, focusableIndex);
|
|
292
|
+
if (hotkeyIndex !== null)
|
|
293
|
+
focusableIndex += 1;
|
|
294
|
+
renderSession(d.displayName, d.session.modelName, d.session.state || 'idle', d.session.agentType, hotkeyIndex, sessionInfo);
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
// Virtual OpenClaw entry when gateway detected but not in sessions list
|
|
298
|
+
const hasOcSession = state.sessions.some(s => s.agentType === 'openclaw' || s.agentType === 'gateway');
|
|
299
|
+
if (state.gatewayAvailable && !hasOcSession) {
|
|
300
|
+
renderSession('OpenClaw', undefined, state.crayfishRouting ? 'processing' : 'idle', 'openclaw', null);
|
|
301
|
+
}
|
|
302
|
+
// Gateway error warning
|
|
303
|
+
if (state.gatewayHasError) {
|
|
304
|
+
lines.push(`${colors.error} \u26A0 Gateway Error${RESET}`);
|
|
305
|
+
}
|
|
306
|
+
// Voice assistant indicator
|
|
307
|
+
if (state.voiceAssistantState && state.voiceAssistantState !== 'disabled' && state.voiceAssistantState !== 'idle') {
|
|
308
|
+
lines.push('');
|
|
309
|
+
if (state.voiceAssistantState === 'listening') {
|
|
310
|
+
lines.push(` ${colors.idle}\uD83C\uDFA4 Listening...${RESET}`);
|
|
311
|
+
}
|
|
312
|
+
else if (state.voiceAssistantState === 'processing') {
|
|
313
|
+
const text = state.voiceAssistantText ? truncText(state.voiceAssistantText, maxWidth - 6) : '...';
|
|
314
|
+
lines.push(` ${colors.processing}\uD83C\uDFA4 ${text}${RESET}`);
|
|
315
|
+
}
|
|
316
|
+
else if (state.voiceAssistantState === 'speaking') {
|
|
317
|
+
lines.push(` ${sgr(32)}\uD83D\uDD0A Speaking...${RESET}`);
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
lines.push('');
|
|
321
|
+
if (state.usage) {
|
|
322
|
+
const u = state.usage;
|
|
323
|
+
if (u.inputTokens || u.outputTokens) {
|
|
324
|
+
lines.push(`${colors.dim} Tokens: ${formatTokens(u.inputTokens)}/${formatTokens(u.outputTokens)}${RESET}`);
|
|
325
|
+
}
|
|
326
|
+
if (u.estimatedCostUsd) {
|
|
327
|
+
lines.push(`${colors.dim} Cost: $${u.estimatedCostUsd.toFixed(2)}${RESET}`);
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
return lines;
|
|
331
|
+
}
|
|
332
|
+
// ===== Status Panel: LIMITS | MODELS =====
|
|
333
|
+
function renderStatusLimitsLines(state, width) {
|
|
334
|
+
const lines = [];
|
|
335
|
+
const u = state.usage;
|
|
336
|
+
if (!u)
|
|
337
|
+
return lines;
|
|
338
|
+
const gaugeW = Math.min(10, Math.floor(width * 0.3));
|
|
339
|
+
if (u.fiveHourPercent !== undefined) {
|
|
340
|
+
const pct = Math.round(u.fiveHourPercent);
|
|
341
|
+
lines.push(` 5h [${blockGauge(pct, gaugeW)}] ${pct}%`);
|
|
342
|
+
const reset = resetTimeStr(u.fiveHourResetsAt);
|
|
343
|
+
if (reset)
|
|
344
|
+
lines.push(`${colors.dim} ${reset}${RESET}`);
|
|
345
|
+
}
|
|
346
|
+
if (u.sevenDayPercent !== undefined) {
|
|
347
|
+
const pct = Math.round(u.sevenDayPercent);
|
|
348
|
+
lines.push(` 7d [${blockGauge(pct, gaugeW)}] ${pct}%`);
|
|
349
|
+
const reset = resetTimeStr(u.sevenDayResetsAt);
|
|
350
|
+
if (reset)
|
|
351
|
+
lines.push(`${colors.dim} ${reset}${RESET}`);
|
|
352
|
+
}
|
|
353
|
+
if (state.currentTool) {
|
|
354
|
+
lines.push(` ${colors.tool}${truncText(state.currentTool, width - 2)}${RESET}`);
|
|
355
|
+
}
|
|
356
|
+
return lines;
|
|
357
|
+
}
|
|
358
|
+
function renderStatusModelsLines(state, width) {
|
|
359
|
+
const lines = [];
|
|
360
|
+
const u = state.usage;
|
|
361
|
+
if (state.modelName) {
|
|
362
|
+
const dot = u?.oauthConnected ? `${colors.idle}\u25CF${RESET}` : `${colors.dim}\u25CB${RESET}`;
|
|
363
|
+
lines.push(` ${dot} ${truncText(state.modelName, width - 4)}`);
|
|
364
|
+
}
|
|
365
|
+
lines.push(...renderOauthCatalogLines(state.modelCatalog, u?.oauthConnected, width));
|
|
366
|
+
lines.push(...renderOllamaSummaryLines(u?.ollamaStatus, width));
|
|
367
|
+
return lines;
|
|
368
|
+
}
|
|
369
|
+
function renderStatusLines(state, width) {
|
|
370
|
+
const lines = [];
|
|
371
|
+
const u = state.usage;
|
|
372
|
+
if (u) {
|
|
373
|
+
const gaugeW = Math.min(12, Math.floor(width * 0.15));
|
|
374
|
+
if (u.fiveHourPercent !== undefined) {
|
|
375
|
+
const pct = Math.round(u.fiveHourPercent);
|
|
376
|
+
lines.push(` 5h [${blockGauge(pct, gaugeW)}] ${pct}% ${colors.dim}${resetTimeStr(u.fiveHourResetsAt)}${RESET}`);
|
|
377
|
+
}
|
|
378
|
+
if (u.sevenDayPercent !== undefined) {
|
|
379
|
+
const pct = Math.round(u.sevenDayPercent);
|
|
380
|
+
lines.push(` 7d [${blockGauge(pct, gaugeW)}] ${pct}% ${colors.dim}${resetTimeStr(u.sevenDayResetsAt)}${RESET}`);
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
if (state.currentTool)
|
|
384
|
+
lines.push(` ${colors.tool}Tool: ${truncText(state.currentTool, width - 8)}${RESET}`);
|
|
385
|
+
if (state.modelName)
|
|
386
|
+
lines.push(`${colors.dim} Model: ${state.modelName}${RESET}`);
|
|
387
|
+
lines.push(...renderOauthCatalogLines(state.modelCatalog, u?.oauthConnected, width));
|
|
388
|
+
const ollamaLines = renderOllamaSummaryLines(u?.ollamaStatus, width);
|
|
389
|
+
lines.push(...ollamaLines);
|
|
390
|
+
return lines;
|
|
391
|
+
}
|
|
392
|
+
function renderOauthCatalogLines(modelCatalog, oauthConnected, width) {
|
|
393
|
+
const lines = [];
|
|
394
|
+
const models = modelCatalog.filter((m) => m.available).map((m) => m.name);
|
|
395
|
+
if (models.length > 0) {
|
|
396
|
+
return wrapCommaList(' OAuth: ', models, width);
|
|
397
|
+
}
|
|
398
|
+
if (oauthConnected === true) {
|
|
399
|
+
lines.push(`${colors.dim}${truncText(' OAuth: connected', width)}${RESET}`);
|
|
400
|
+
}
|
|
401
|
+
else if (oauthConnected === false) {
|
|
402
|
+
lines.push(`${colors.dim}${truncText(' OAuth: disconnected', width)}${RESET}`);
|
|
403
|
+
}
|
|
404
|
+
return lines;
|
|
405
|
+
}
|
|
406
|
+
function wrapCommaList(prefix, items, width) {
|
|
407
|
+
const lines = [];
|
|
408
|
+
const indent = ' '.repeat(prefix.length);
|
|
409
|
+
let current = prefix;
|
|
410
|
+
for (let i = 0; i < items.length; i++) {
|
|
411
|
+
const chunk = i === 0 ? items[i] : `, ${items[i]}`;
|
|
412
|
+
if (visLen(current) + visLen(chunk) <= width) {
|
|
413
|
+
current += chunk;
|
|
414
|
+
continue;
|
|
415
|
+
}
|
|
416
|
+
if (visLen(current) > visLen(prefix)) {
|
|
417
|
+
lines.push(`${colors.dim}${padRight(current, width)}${RESET}`.trimEnd());
|
|
418
|
+
current = indent + items[i];
|
|
419
|
+
continue;
|
|
420
|
+
}
|
|
421
|
+
lines.push(`${colors.dim}${truncText(current + chunk, width)}${RESET}`);
|
|
422
|
+
current = indent;
|
|
423
|
+
}
|
|
424
|
+
if (visLen(current.trim()) > 0) {
|
|
425
|
+
lines.push(`${colors.dim}${truncText(current, width)}${RESET}`);
|
|
426
|
+
}
|
|
427
|
+
return lines;
|
|
428
|
+
}
|
|
429
|
+
function renderOllamaSummaryLines(ollamaStatus, width) {
|
|
430
|
+
if (!ollamaStatus)
|
|
431
|
+
return [];
|
|
432
|
+
if (!ollamaStatus.available || ollamaStatus.models.length === 0) {
|
|
433
|
+
return [`${colors.dim}${truncText(' Ollama: stopped', width)}${RESET}`];
|
|
434
|
+
}
|
|
435
|
+
return ollamaStatus.models.map((m) => {
|
|
436
|
+
const size = m.sizeVram > 0 ? m.sizeVram : m.size;
|
|
437
|
+
const sizeText = size > 0 ? ` ${(size / 1e9).toFixed(1)}G` : '';
|
|
438
|
+
return `${colors.dim}${truncText(` Ollama: ${m.name}${sizeText}`, width)}${RESET}`;
|
|
439
|
+
});
|
|
440
|
+
}
|
|
441
|
+
function renderTimelineLines(state, width, maxLines, scrollOffset) {
|
|
442
|
+
const lines = [];
|
|
443
|
+
const entries = state.timeline;
|
|
444
|
+
if (entries.length === 0) {
|
|
445
|
+
lines.push(`${colors.dim} No events yet${RESET}`);
|
|
446
|
+
return lines;
|
|
447
|
+
}
|
|
448
|
+
const start = Math.max(0, entries.length - maxLines - scrollOffset);
|
|
449
|
+
const end = Math.min(entries.length, start + maxLines);
|
|
450
|
+
for (let i = start; i < end; i++) {
|
|
451
|
+
const e = entries[i];
|
|
452
|
+
const time = new Date(e.ts);
|
|
453
|
+
const timeStr = `${String(time.getHours()).padStart(2, '0')}:${String(time.getMinutes()).padStart(2, '0')}`;
|
|
454
|
+
const raw = truncText(e.raw, width - 10);
|
|
455
|
+
const pending = e.status === 'pending' ? `${colors.dim} [PENDING]${RESET}` : '';
|
|
456
|
+
lines.push(` ${colors.dim}${timeStr}${RESET} ${typeColor(e.type)}${typeIcon(e.type)}${RESET} ${raw}${pending}`);
|
|
457
|
+
}
|
|
458
|
+
return lines;
|
|
459
|
+
}
|
|
460
|
+
function timelineHeader(state, width, maxLines, scrollOffset) {
|
|
461
|
+
const total = state.timeline.length;
|
|
462
|
+
if (total === 0)
|
|
463
|
+
return `${colors.header} TIMELINE${RESET}`;
|
|
464
|
+
const shown = Math.min(total, maxLines);
|
|
465
|
+
const offset = scrollOffset > 0 ? ` · -${scrollOffset}` : '';
|
|
466
|
+
return truncText(`${colors.header} TIMELINE${RESET}${colors.dim} ${shown}/${total}${offset}${RESET}`, width);
|
|
467
|
+
}
|
|
468
|
+
function renderHelpOverlay(state, cols, rows) {
|
|
469
|
+
const boxW = Math.min(cols - 4, 78);
|
|
470
|
+
const boxH = Math.min(rows - 4, 18);
|
|
471
|
+
const left = Math.max(1, Math.floor((cols - boxW) / 2));
|
|
472
|
+
const top = Math.max(1, Math.floor((rows - boxH) / 2));
|
|
473
|
+
const lines = [
|
|
474
|
+
`${colors.header}AgentDeck TUI Help${RESET}`,
|
|
475
|
+
'',
|
|
476
|
+
`${colors.bold}Navigation${RESET}`,
|
|
477
|
+
' q quit dashboard',
|
|
478
|
+
' ? / h toggle help',
|
|
479
|
+
' ↑ ↓ scroll timeline',
|
|
480
|
+
' j / k vim-style timeline scroll',
|
|
481
|
+
' 1-9 connect to listed session',
|
|
482
|
+
' Esc close help',
|
|
483
|
+
'',
|
|
484
|
+
`${colors.bold}Reading The UI${RESET}`,
|
|
485
|
+
' Header current project, model, and state',
|
|
486
|
+
' Sessions busiest first, numbered for switching',
|
|
487
|
+
' STATUS limits, models, OAuth, Ollama',
|
|
488
|
+
' TIMELINE shown/total and scroll offset',
|
|
489
|
+
'',
|
|
490
|
+
`${colors.bold}Terminal${RESET}`,
|
|
491
|
+
` Unicode: ${terminalCaps.unicode ? 'on' : 'fallback'} Color: ${terminalCaps.trueColor ? 'truecolor' : '16-color'} Emoji: ${terminalCaps.emoji ? 'on' : 'fallback'}`,
|
|
492
|
+
];
|
|
493
|
+
let output = cursor.moveTo(1, 1) + screenCodes.clear;
|
|
494
|
+
output += cursor.moveTo(top, left) + `${colors.border}${box.tl}${hLine(boxW - 2)}${box.tr}${RESET}`;
|
|
495
|
+
for (let i = 0; i < boxH - 2; i++) {
|
|
496
|
+
const content = lines[i] ?? '';
|
|
497
|
+
output += cursor.moveTo(top + 1 + i, left) +
|
|
498
|
+
`${colors.border}${box.v}${RESET}` +
|
|
499
|
+
padRight(i === 0 ? centerText(content, boxW - 2) : content, boxW - 2) +
|
|
500
|
+
`${colors.border}${box.v}${RESET}`;
|
|
501
|
+
}
|
|
502
|
+
output += cursor.moveTo(top + boxH - 1, left) + `${colors.border}${box.bl}${hLine(boxW - 2)}${box.br}${RESET}`;
|
|
503
|
+
output += cursor.moveTo(Math.min(rows, top + boxH), left) +
|
|
504
|
+
`${colors.dim}Press ? or Esc to return${RESET}`;
|
|
505
|
+
return output;
|
|
506
|
+
}
|
|
507
|
+
// ===== Output Helper =====
|
|
508
|
+
/** Write buf lines to screen. Last row reserved for q quit hint. */
|
|
509
|
+
function flushBuf(buf, cols, rows, footerHint) {
|
|
510
|
+
const maxBoxRows = rows - 1;
|
|
511
|
+
let output = cursor.moveTo(1, 1);
|
|
512
|
+
const limit = Math.min(buf.length, maxBoxRows);
|
|
513
|
+
for (let i = 0; i < limit; i++) {
|
|
514
|
+
output += cursor.moveTo(i + 1, 1) + screenCodes.clearLine + buf[i];
|
|
515
|
+
}
|
|
516
|
+
for (let i = limit; i < maxBoxRows; i++) {
|
|
517
|
+
output += cursor.moveTo(i + 1, 1) + screenCodes.clearLine;
|
|
518
|
+
}
|
|
519
|
+
// q quit on last row
|
|
520
|
+
output += cursor.moveTo(rows, 1) + screenCodes.clearLine +
|
|
521
|
+
` ${colors.dimCyan}${truncText(footerHint, cols - 2)}${RESET}`;
|
|
522
|
+
return output;
|
|
523
|
+
}
|
|
524
|
+
// ===== Main Render =====
|
|
525
|
+
export function renderDashboard(state, cols, rows, terrariumLines, frame, scrollOffset) {
|
|
526
|
+
if (state.helpVisible) {
|
|
527
|
+
return renderHelpOverlay(state, cols, rows);
|
|
528
|
+
}
|
|
529
|
+
const layout = getLayout(cols, rows);
|
|
530
|
+
if (cols < 40 || rows < 10) {
|
|
531
|
+
return cursor.moveTo(1, 1) + screenCodes.clear +
|
|
532
|
+
`Resize terminal to at least 60\u00D716 (current: ${cols}\u00D7${rows})`;
|
|
533
|
+
}
|
|
534
|
+
const connIcon = state.connectionStatus === 'connected' ? `${colors.idle}\u25CF` :
|
|
535
|
+
state.connectionStatus === 'reconnecting' ? `${colors.processing}\u25D4` :
|
|
536
|
+
`${colors.disconnected}\u25CB`;
|
|
537
|
+
const staleTag = state.isStale ? ` ${colors.error}[STALE]${RESET}` : '';
|
|
538
|
+
const spinnerStr = state.state === 'processing'
|
|
539
|
+
? ` ${colors.processing}${spinner(frame)}${RESET}` : '';
|
|
540
|
+
const footerHint = state.sessions.length > 0
|
|
541
|
+
? 'q quit ↑↓/j k scroll 1-9 switch session'
|
|
542
|
+
: 'q quit ↑↓/j k scroll';
|
|
543
|
+
if (layout === 'wide')
|
|
544
|
+
return renderWideLayout(state, cols, rows, terrariumLines, frame, scrollOffset, connIcon, staleTag, spinnerStr, footerHint);
|
|
545
|
+
if (layout === 'standard')
|
|
546
|
+
return renderStandardLayout(state, cols, rows, terrariumLines, frame, scrollOffset, connIcon, staleTag, spinnerStr, footerHint);
|
|
547
|
+
return renderNarrowLayout(state, cols, rows, frame, scrollOffset, connIcon, staleTag, spinnerStr, footerHint);
|
|
548
|
+
}
|
|
549
|
+
// ===== Wide Layout =====
|
|
550
|
+
function renderWideLayout(state, cols, rows, terrariumLines, frame, scrollOffset, connIcon, staleTag, spinnerStr, footerHint) {
|
|
551
|
+
const leftW = Math.max(20, Math.floor(cols * 0.22));
|
|
552
|
+
const rightW = cols - leftW - 3;
|
|
553
|
+
const buf = [];
|
|
554
|
+
const summary = currentSessionSummary(state, Math.max(16, rightW - 16));
|
|
555
|
+
// Top border
|
|
556
|
+
const topLeft = `${colors.border}${box.tl}${RESET}`;
|
|
557
|
+
const topMid = `${colors.border}${box.tee}${box.h} ${summary} ${RESET}${connIcon}${RESET}${spinnerStr}${staleTag} `;
|
|
558
|
+
const topRight = `${colors.border}${box.tr}${RESET}`;
|
|
559
|
+
const leftFillLen = Math.max(0, leftW + 1 - visLen(topLeft));
|
|
560
|
+
const rightFillLen = Math.max(0, rightW + 2 - visLen(topMid) - visLen(topRight));
|
|
561
|
+
buf.push(topLeft + `${colors.border}${hLine(leftFillLen)}${RESET}` + topMid + `${colors.border}${hLine(rightFillLen)}${RESET}` + topRight);
|
|
562
|
+
const agentLines = renderAgentLines(state, leftW - 2, true);
|
|
563
|
+
// Status: LIMITS | MODELS
|
|
564
|
+
const tH = terrariumLines.length;
|
|
565
|
+
const statusLimitW = Math.floor(rightW * 0.4);
|
|
566
|
+
const statusModelW = rightW - statusLimitW - 1;
|
|
567
|
+
const limitsLines = renderStatusLimitsLines(state, statusLimitW);
|
|
568
|
+
const modelsLines = renderStatusModelsLines(state, statusModelW);
|
|
569
|
+
const statusH = Math.max(3, Math.max(limitsLines.length, modelsLines.length) + 1);
|
|
570
|
+
const boxContentRows = rows - 3; // top border + bottom border + q quit row
|
|
571
|
+
const timelineH = Math.max(3, boxContentRows - tH - statusH - 2);
|
|
572
|
+
const tlLines = renderTimelineLines(state, rightW - 2, timelineH - 1, scrollOffset);
|
|
573
|
+
for (let r = 0; r < boxContentRows; r++) {
|
|
574
|
+
const leftContent = padRight(r < agentLines.length ? agentLines[r] : '', leftW);
|
|
575
|
+
let rightContent = '';
|
|
576
|
+
if (r < tH) {
|
|
577
|
+
rightContent = terrariumLines[r] || '';
|
|
578
|
+
}
|
|
579
|
+
else if (r === tH) {
|
|
580
|
+
rightContent = `${colors.border}${hLine(statusLimitW)}${RESET}` +
|
|
581
|
+
`${colors.border}\u252C${RESET}` +
|
|
582
|
+
`${colors.border}${hLine(statusModelW)}${RESET}`;
|
|
583
|
+
}
|
|
584
|
+
else if (r < tH + 1 + statusH) {
|
|
585
|
+
const si = r - tH - 1;
|
|
586
|
+
if (si === 0) {
|
|
587
|
+
rightContent = padRight(`${colors.header} LIMITS${RESET}`, statusLimitW) +
|
|
588
|
+
`${colors.border}${box.v}${RESET}` +
|
|
589
|
+
padRight(`${colors.header} MODELS${RESET}`, statusModelW);
|
|
590
|
+
}
|
|
591
|
+
else {
|
|
592
|
+
const li = si - 1;
|
|
593
|
+
rightContent = padRight(li < limitsLines.length ? limitsLines[li] : '', statusLimitW) +
|
|
594
|
+
`${colors.border}${box.v}${RESET}` +
|
|
595
|
+
padRight(li < modelsLines.length ? modelsLines[li] : '', statusModelW);
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
else if (r === tH + 1 + statusH) {
|
|
599
|
+
rightContent = `${colors.border}${hLine(rightW)}${RESET}`;
|
|
600
|
+
}
|
|
601
|
+
else {
|
|
602
|
+
const ti = r - tH - statusH - 2;
|
|
603
|
+
if (ti === 0)
|
|
604
|
+
rightContent = timelineHeader(state, rightW, timelineH - 1, scrollOffset);
|
|
605
|
+
else
|
|
606
|
+
rightContent = ti - 1 < tlLines.length ? tlLines[ti - 1] : '';
|
|
607
|
+
}
|
|
608
|
+
buf.push(`${colors.border}${box.v}${RESET}${padRight(leftContent, leftW)}` +
|
|
609
|
+
`${colors.border}${box.v}${RESET}${padRight(rightContent, rightW)}` +
|
|
610
|
+
`${colors.border}${box.v}${RESET}`);
|
|
611
|
+
}
|
|
612
|
+
buf.push(`${colors.border}${box.bl}${hLine(leftW)}${box.bTee}${hLine(rightW)}${box.br}${RESET}`);
|
|
613
|
+
return flushBuf(buf, cols, rows, footerHint);
|
|
614
|
+
}
|
|
615
|
+
// ===== Standard Layout =====
|
|
616
|
+
function renderStandardLayout(state, cols, rows, terrariumLines, frame, scrollOffset, connIcon, staleTag, spinnerStr, footerHint) {
|
|
617
|
+
const w = cols - 2;
|
|
618
|
+
const buf = [];
|
|
619
|
+
const summary = currentSessionSummary(state, Math.max(16, w - 18));
|
|
620
|
+
buf.push(borderFill(`${colors.border}${box.tl}${box.h} ${summary} ${RESET}${connIcon}${RESET}${spinnerStr}${staleTag} `, `${colors.border}${box.tr}${RESET}`, cols));
|
|
621
|
+
for (const tl of terrariumLines) {
|
|
622
|
+
buf.push(`${colors.border}${box.v}${RESET}${padRight(tl, w)}${colors.border}${box.v}${RESET}`);
|
|
623
|
+
}
|
|
624
|
+
const leftHalf = Math.floor(w / 2);
|
|
625
|
+
const rightHalf = w - leftHalf - 1;
|
|
626
|
+
const splitPrefix = `${colors.border}${box.lTee}${box.h} STATUS ${RESET}`;
|
|
627
|
+
const splitMid = `${colors.border}${box.tee}${RESET}`;
|
|
628
|
+
const splitSuffix = `${colors.border}${box.rTee}${RESET}`;
|
|
629
|
+
buf.push(splitPrefix + `${colors.border}${hLine(Math.max(0, leftHalf + 1 - visLen(splitPrefix)))}${RESET}` +
|
|
630
|
+
splitMid + `${colors.border}${hLine(Math.max(0, rightHalf + 2 - visLen(splitMid) - visLen(splitSuffix)))}${RESET}` + splitSuffix);
|
|
631
|
+
const statusLines = renderStatusLines(state, leftHalf - 1);
|
|
632
|
+
const agentCompact = renderAgentCompactLines(state, rightHalf - 1);
|
|
633
|
+
const pairRows = Math.max(statusLines.length, agentCompact.length, 3);
|
|
634
|
+
for (let r = 0; r < pairRows; r++) {
|
|
635
|
+
buf.push(`${colors.border}${box.v}${RESET}${padRight(r < statusLines.length ? statusLines[r] : '', leftHalf)}` +
|
|
636
|
+
`${colors.border}${box.v}${RESET}${padRight(r < agentCompact.length ? agentCompact[r] : '', rightHalf)}` +
|
|
637
|
+
`${colors.border}${box.v}${RESET}`);
|
|
638
|
+
}
|
|
639
|
+
buf.push(`${colors.border}${box.lTee}${hLine(leftHalf)}${box.bTee}${hLine(rightHalf)}${box.rTee}${RESET}`);
|
|
640
|
+
const tlAvailable = Math.max(2, rows - buf.length - 2);
|
|
641
|
+
buf.push(`${colors.border}${box.v}${RESET}${padRight(timelineHeader(state, w, tlAvailable, scrollOffset), w)}` +
|
|
642
|
+
`${colors.border}${box.v}${RESET}`);
|
|
643
|
+
const tlLines = renderTimelineLines(state, w - 1, tlAvailable, scrollOffset);
|
|
644
|
+
for (const tl of tlLines) {
|
|
645
|
+
buf.push(`${colors.border}${box.v}${RESET}${padRight(tl, w)}${colors.border}${box.v}${RESET}`);
|
|
646
|
+
}
|
|
647
|
+
buf.push(`${colors.border}${box.bl}${hLine(w)}${box.br}${RESET}`);
|
|
648
|
+
return flushBuf(buf, cols, rows, footerHint);
|
|
649
|
+
}
|
|
650
|
+
function renderAgentCompactLines(state, width) {
|
|
651
|
+
const lines = [];
|
|
652
|
+
const isDaemonLikeCompact = state.agentType === 'daemon' ||
|
|
653
|
+
(state.agentType && state.sessions.some(s => s.agentType === state.agentType));
|
|
654
|
+
if (isDaemonLikeCompact) {
|
|
655
|
+
let focusableIndex = 0;
|
|
656
|
+
for (const s of sortSessions(state.sessions)) {
|
|
657
|
+
const renderInfo = s;
|
|
658
|
+
const st = s.state || 'idle';
|
|
659
|
+
const col = stateColor(st);
|
|
660
|
+
const emoji = `${creatureBrandColor(s.agentType)}${creatureEmoji(s.agentType)}${RESET}`;
|
|
661
|
+
const status = `${col}${stateIcon(st)} ${compactStateLabel(st)}${RESET}`;
|
|
662
|
+
const detail = renderInfo.currentTask || s.modelName;
|
|
663
|
+
const model = detail ? `${colors.dim} · ${truncText(detail, Math.max(8, width - 24))}${RESET}` : '';
|
|
664
|
+
const marker = renderInfo.controlMode === 'observed' ? `${colors.dim} · obs${RESET}` : '';
|
|
665
|
+
const project = truncText(s.projectName || '?', Math.max(8, width - 20));
|
|
666
|
+
const hotkeyIndex = sessionHotkeyIndex(renderInfo, focusableIndex);
|
|
667
|
+
if (hotkeyIndex !== null)
|
|
668
|
+
focusableIndex += 1;
|
|
669
|
+
lines.push(` ${colors.dim}[${sessionHotkeyLabel(hotkeyIndex)}]${RESET} ${emoji} ${project} ${status}${marker}${model}`);
|
|
670
|
+
}
|
|
671
|
+
}
|
|
672
|
+
else if (state.state) {
|
|
673
|
+
// Self first
|
|
674
|
+
const col = stateColor(state.state);
|
|
675
|
+
const proj = truncText(state.projectName || '?', Math.max(8, width - 16));
|
|
676
|
+
const status = `${col}${stateIcon(state.state)} ${compactStateLabel(state.state)}${RESET}`;
|
|
677
|
+
const model = state.modelName ? `${colors.dim} · ${truncText(state.modelName, Math.max(8, width - 24))}${RESET}` : '';
|
|
678
|
+
const emoji = `${creatureBrandColor(state.agentType ?? undefined)}${creatureEmoji(state.agentType ?? undefined)}${RESET}`;
|
|
679
|
+
lines.push(` ${emoji} ${proj} ${status}${model}`);
|
|
680
|
+
// Siblings
|
|
681
|
+
let focusableIndex = 0;
|
|
682
|
+
for (const s of sortSessions(state.sessions)) {
|
|
683
|
+
const renderInfo = s;
|
|
684
|
+
const st = s.state || 'idle';
|
|
685
|
+
const sCol = stateColor(st);
|
|
686
|
+
const sEmoji = `${creatureBrandColor(s.agentType)}${creatureEmoji(s.agentType)}${RESET}`;
|
|
687
|
+
const sStatus = `${sCol}${stateIcon(st)} ${compactStateLabel(st)}${RESET}`;
|
|
688
|
+
const detail = renderInfo.currentTask || s.modelName;
|
|
689
|
+
const sModel = detail ? `${colors.dim} · ${truncText(detail, Math.max(8, width - 24))}${RESET}` : '';
|
|
690
|
+
const marker = renderInfo.controlMode === 'observed' ? `${colors.dim} · obs${RESET}` : '';
|
|
691
|
+
const sProject = truncText(s.projectName || '?', Math.max(8, width - 20));
|
|
692
|
+
const hotkeyIndex = sessionHotkeyIndex(renderInfo, focusableIndex);
|
|
693
|
+
if (hotkeyIndex !== null)
|
|
694
|
+
focusableIndex += 1;
|
|
695
|
+
lines.push(` ${colors.dim}[${sessionHotkeyLabel(hotkeyIndex)}]${RESET} ${sEmoji} ${sProject} ${sStatus}${marker}${sModel}`);
|
|
696
|
+
}
|
|
697
|
+
}
|
|
698
|
+
// Virtual OpenClaw entry
|
|
699
|
+
const hasOcCompact = state.sessions.some(s => s.agentType === 'openclaw' || s.agentType === 'gateway');
|
|
700
|
+
if (state.gatewayAvailable && !hasOcCompact) {
|
|
701
|
+
const ocState = state.crayfishRouting ? 'processing' : 'idle';
|
|
702
|
+
const ocCol = stateColor(ocState);
|
|
703
|
+
const ocEmoji = `${creatureBrandColor('openclaw')}${creatureEmoji('openclaw')}${RESET}`;
|
|
704
|
+
lines.push(` ${ocEmoji} OpenClaw ${ocCol}${stateIcon(ocState)}${ocState.toUpperCase()}${RESET}`);
|
|
705
|
+
}
|
|
706
|
+
// Gateway error warning
|
|
707
|
+
if (state.gatewayHasError) {
|
|
708
|
+
lines.push(`${colors.error} \u26A0 Gateway Error${RESET}`);
|
|
709
|
+
}
|
|
710
|
+
// Voice assistant indicator (compact)
|
|
711
|
+
if (state.voiceAssistantState && state.voiceAssistantState !== 'disabled' && state.voiceAssistantState !== 'idle') {
|
|
712
|
+
if (state.voiceAssistantState === 'listening') {
|
|
713
|
+
lines.push(` ${colors.idle}\uD83C\uDFA4 Listening...${RESET}`);
|
|
714
|
+
}
|
|
715
|
+
else if (state.voiceAssistantState === 'processing') {
|
|
716
|
+
const text = state.voiceAssistantText ? truncText(state.voiceAssistantText, width - 6) : '...';
|
|
717
|
+
lines.push(` ${colors.processing}\uD83C\uDFA4 ${text}${RESET}`);
|
|
718
|
+
}
|
|
719
|
+
else if (state.voiceAssistantState === 'speaking') {
|
|
720
|
+
lines.push(` ${sgr(32)}\uD83D\uDD0A Speaking...${RESET}`);
|
|
721
|
+
}
|
|
722
|
+
}
|
|
723
|
+
if (state.usage) {
|
|
724
|
+
const u = state.usage;
|
|
725
|
+
const parts = [];
|
|
726
|
+
if (u.inputTokens || u.outputTokens)
|
|
727
|
+
parts.push(`${formatTokens(u.inputTokens)}/${formatTokens(u.outputTokens)}`);
|
|
728
|
+
if (u.estimatedCostUsd)
|
|
729
|
+
parts.push(`$${u.estimatedCostUsd.toFixed(2)}`);
|
|
730
|
+
if (parts.length > 0)
|
|
731
|
+
lines.push(`${colors.dim} ${parts.join(' ')}${RESET}`);
|
|
732
|
+
}
|
|
733
|
+
return lines;
|
|
734
|
+
}
|
|
735
|
+
// ===== Narrow Layout =====
|
|
736
|
+
function renderNarrowLayout(state, cols, rows, frame, scrollOffset, connIcon, staleTag, spinnerStr, footerHint) {
|
|
737
|
+
const w = cols - 2;
|
|
738
|
+
const buf = [];
|
|
739
|
+
const summary = currentSessionSummary(state, Math.max(12, w - 18));
|
|
740
|
+
buf.push(borderFill(`${colors.border}${box.tl}${box.h} ${summary} ${RESET}${connIcon}${RESET}${spinnerStr}${staleTag} `, `${colors.border}${box.tr}${RESET}`, cols));
|
|
741
|
+
for (const al of renderAgentCompactLines(state, w - 1)) {
|
|
742
|
+
buf.push(`${colors.border}${box.v}${RESET}${padRight(al, w)}${colors.border}${box.v}${RESET}`);
|
|
743
|
+
}
|
|
744
|
+
buf.push(`${colors.border}${box.lTee}${hLine(w)}${box.rTee}${RESET}`);
|
|
745
|
+
for (const sl of renderStatusLines(state, w - 1)) {
|
|
746
|
+
buf.push(`${colors.border}${box.v}${RESET}${padRight(sl, w)}${colors.border}${box.v}${RESET}`);
|
|
747
|
+
}
|
|
748
|
+
const tlAvailable = Math.max(2, rows - buf.length - 2);
|
|
749
|
+
buf.push(`${colors.border}${box.lTee}${hLine(w)}${box.rTee}${RESET}`);
|
|
750
|
+
buf.push(`${colors.border}${box.v}${RESET}${padRight(timelineHeader(state, w, tlAvailable, scrollOffset), w)}${colors.border}${box.v}${RESET}`);
|
|
751
|
+
const tlLines = renderTimelineLines(state, w - 1, tlAvailable, scrollOffset);
|
|
752
|
+
for (const tl of tlLines) {
|
|
753
|
+
buf.push(`${colors.border}${box.v}${RESET}${padRight(tl, w)}${colors.border}${box.v}${RESET}`);
|
|
754
|
+
}
|
|
755
|
+
buf.push(`${colors.border}${box.bl}${hLine(w)}${box.br}${RESET}`);
|
|
756
|
+
return flushBuf(buf, cols, rows, footerHint);
|
|
757
|
+
}
|
|
758
|
+
//# sourceMappingURL=renderer.js.map
|