| 40 | } |
| 41 | |
| 42 | export function createBridgeLogger(options: { |
| 43 | verbose: boolean |
| 44 | write?: (s: string) => void |
| 45 | }): BridgeLogger { |
| 46 | const write = options.write ?? ((s: string) => process.stdout.write(s)) |
| 47 | const verbose = options.verbose |
| 48 | |
| 49 | // Track how many status lines are currently displayed at the bottom |
| 50 | let statusLineCount = 0 |
| 51 | |
| 52 | // Status state machine |
| 53 | let currentState: StatusState = 'idle' |
| 54 | let currentStateText = 'Ready' |
| 55 | let repoName = '' |
| 56 | let branch = '' |
| 57 | let debugLogPath = '' |
| 58 | |
| 59 | // Connect URL (built in printBanner with correct base for staging/prod) |
| 60 | let connectUrl = '' |
| 61 | let cachedIngressUrl = '' |
| 62 | let cachedEnvironmentId = '' |
| 63 | let activeSessionUrl: string | null = null |
| 64 | |
| 65 | // QR code lines for the current URL |
| 66 | let qrLines: string[] = [] |
| 67 | let qrVisible = false |
| 68 | |
| 69 | // Tool activity for the second status line |
| 70 | let lastToolSummary: string | null = null |
| 71 | let lastToolTime = 0 |
| 72 | |
| 73 | // Session count indicator (shown when multi-session mode is enabled) |
| 74 | let sessionActive = 0 |
| 75 | let sessionMax = 1 |
| 76 | // Spawn mode shown in the session-count line + gates the `w` hint |
| 77 | let spawnModeDisplay: 'same-dir' | 'worktree' | null = null |
| 78 | let spawnMode: SpawnMode = 'single-session' |
| 79 | |
| 80 | // Per-session display info for the multi-session bullet list (keyed by compat sessionId) |
| 81 | const sessionDisplayInfo = new Map< |
| 82 | string, |
| 83 | { title?: string; url: string; activity?: SessionActivity } |
| 84 | >() |
| 85 | |
| 86 | // Connecting spinner state |
| 87 | let connectingTimer: ReturnType<typeof setInterval> | null = null |
| 88 | let connectingTick = 0 |
| 89 | |
| 90 | /** |
| 91 | * Count how many visual terminal rows a string occupies, accounting for |
| 92 | * line wrapping. Each `\n` is one row, and content wider than the terminal |
| 93 | * wraps to additional rows. |
| 94 | */ |
| 95 | function countVisualLines(text: string): number { |
| 96 | // eslint-disable-next-line custom-rules/prefer-use-terminal-size |
| 97 | const cols = process.stdout.columns || 80 // non-React CLI context |
| 98 | let count = 0 |
| 99 | // Split on newlines to get logical lines |