@h-rig/cli 0.0.6-alpha.85 → 0.0.6-alpha.86

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.
@@ -2773,19 +2773,6 @@ function panelContentRect(layout, panel) {
2773
2773
  const contentWidth = Math.max(1, width - paddingX * 2 - 1);
2774
2774
  return { left, top, width, height, paddingX, paddingY, contentWidth };
2775
2775
  }
2776
- function panelPixelPlacement(layout, panel, size, minPixels = 8) {
2777
- const cellW = size.width / layout.width;
2778
- const cellH = size.height / layout.height;
2779
- const leftCells = layout.centerLeft + (panel.left ?? 0);
2780
- const topCells = layout.centerTop + panel.top;
2781
- const widthCells = panel.width ?? layout.centerWidth;
2782
- const heightCells = panel.height;
2783
- const left = Math.round(leftCells * cellW);
2784
- const top = Math.round(topCells * cellH);
2785
- const width = Math.round(widthCells * cellW);
2786
- const height = Math.round(heightCells * cellH);
2787
- return width > minPixels && height > minPixels ? { left, top, width, height, leftCells, topCells, widthCells, heightCells } : null;
2788
- }
2789
2776
 
2790
2777
  // packages/cli/src/app-opentui/render/constants.ts
2791
2778
  var LCG_MULTIPLIER = 1664525;
@@ -2818,7 +2805,6 @@ var RAIL_YS_LOOP = [0.18, 0.5, 0.82];
2818
2805
  var RAIL_YS_CARRIER = [0.24, 0.54, 0.82];
2819
2806
  var RAIL_YS_DISPATCH = [0.18, 0.58, 0.82];
2820
2807
  var GLOW_FALLOFF_EXP_ASCII = 2.2;
2821
- var GLOW_FALLOFF_EXP_IMAGE = 2.4;
2822
2808
 
2823
2809
  // packages/cli/src/app-opentui/render/graphics.ts
2824
2810
  var BRAILLE_SAMPLES_X = 2;
@@ -3250,27 +3236,20 @@ function writeCell(layer, x, y, char, fg, bg) {
3250
3236
  function drawFullCell(layer, x, y, bg) {
3251
3237
  writeCell(layer, x, y, " ", TRANSPARENT, bg);
3252
3238
  }
3253
- function drawCornerCut(layer, x, y, char, fg) {
3254
- writeCell(layer, x, y, char, fg, BACKDROP);
3255
- }
3256
- function drawRoundedFill(layer, left, top, right, bottom, dividerY) {
3239
+ function drawPanelFill(layer, left, top, right, bottom, dividerY) {
3257
3240
  const bgForY = (y) => y < dividerY ? PANEL_HEADER_BG : PANEL_BG;
3258
3241
  for (let y = top;y <= bottom; y += 1) {
3259
3242
  const bg = bgForY(y);
3260
3243
  for (let x = left;x <= right; x += 1)
3261
3244
  drawFullCell(layer, x, y, bg);
3262
3245
  }
3263
- drawCornerCut(layer, left, top, "\u2597", PANEL_HEADER_BG);
3264
- drawCornerCut(layer, right, top, "\u2596", PANEL_HEADER_BG);
3265
- drawCornerCut(layer, left, bottom, "\u259D", PANEL_BG);
3266
- drawCornerCut(layer, right, bottom, "\u2598", PANEL_BG);
3267
3246
  }
3268
3247
  function drawAdTerminalPanel(layer, layout, panel) {
3269
3248
  const rect = panelCellRect(layout, panel);
3270
3249
  if (!rect)
3271
3250
  return;
3272
3251
  const { left, top, width, right, bottom, headerHeight, dividerY } = rect;
3273
- drawRoundedFill(layer, left, top, right, bottom, dividerY);
3252
+ drawPanelFill(layer, left, top, right, bottom, dividerY);
3274
3253
  if (dividerY > top && dividerY < bottom) {
3275
3254
  for (let x = left + 2;x < right - 1; x += 1) {
3276
3255
  writeCell(layer, x, dividerY, "\u2500", PANEL_LINE_DIM, PANEL_BG);
@@ -3417,971 +3396,6 @@ function sparklineString(values, width) {
3417
3396
  }).join("");
3418
3397
  }
3419
3398
 
3420
- // packages/cli/src/app-opentui/render/image-visual-layer.ts
3421
- import { Worker } from "worker_threads";
3422
- import { deflateSync } from "zlib";
3423
- import { allocateImageId, deleteKittyImage } from "@earendil-works/pi-tui";
3424
-
3425
- // packages/cli/src/app-opentui/terminal-capabilities.ts
3426
- import { getCapabilities } from "@earendil-works/pi-tui";
3427
- function getRigTerminalCapabilities() {
3428
- const imageProtocol = getCapabilities().images;
3429
- const supportsKittyImages = imageProtocol === "kitty";
3430
- return {
3431
- imageProtocol,
3432
- supportsKittyImages,
3433
- visualMode: supportsKittyImages ? "kitty-images" : "framebuffer-fallback"
3434
- };
3435
- }
3436
-
3437
- // packages/cli/src/app-opentui/render/image-visual-layer.ts
3438
- var PANEL_MASK_RADIUS_MIN = 14;
3439
- var PANEL_MASK_RADIUS_MAX = 34;
3440
- var PANEL_MASK_RADIUS_FACTOR = 0.035;
3441
- var PANEL_BLUR_RADIUS_MIN = 8;
3442
- var PANEL_BLUR_RADIUS_MAX = 28;
3443
- var PANEL_BLUR_RADIUS_FACTOR = 0.018;
3444
- var BG = [7, 8, 9, 255];
3445
- var PANEL = [16, 17, 21, 184];
3446
- var PANEL_HEADER = [16, 17, 21, 190];
3447
- var PANEL_LINE2 = [255, 255, 255, 24];
3448
- var LIME = [204, 255, 77, 255];
3449
- var LIME_DIM = [169, 214, 63, 255];
3450
- var CYAN = [86, 216, 255, 255];
3451
- var MAGENTA = [255, 121, 176, 255];
3452
- var INK_DIM = [108, 110, 121, 255];
3453
- var PNG_SIGNATURE = new Uint8Array([137, 80, 78, 71, 13, 10, 26, 10]);
3454
- var CRC_TABLE = makeCrcTable();
3455
- var MAX_IMAGE_BYTES = 48 * 1024 * 1024;
3456
- var KITTY_CHUNK_SIZE = 4096;
3457
- var PANEL_BLUR_SCALE = 1;
3458
- var PANEL_MASK_CACHE_LIMIT = 16;
3459
- var ZLIB_LEVEL = 1;
3460
- var DEFAULT_IMAGE_RESOLUTION_SCALE = 1;
3461
- var LOAD_BUCKETS = 5;
3462
- function loadBucket(load) {
3463
- if (!Number.isFinite(load ?? NaN))
3464
- return 0;
3465
- return Math.max(0, Math.min(LOAD_BUCKETS, Math.round((load ?? 0) * LOAD_BUCKETS)));
3466
- }
3467
- var panelMaskCache = new Map;
3468
- var BG_WORD = rgbaWord(BG);
3469
- function imageTransport() {
3470
- const forced = process.env.RIG_OPENTUI_IMAGE_TRANSPORT;
3471
- if (forced === "rgba-zlib")
3472
- return "rgba-zlib";
3473
- return "png";
3474
- }
3475
- function imageResolutionScale() {
3476
- const raw = Number.parseFloat(process.env.RIG_OPENTUI_IMAGE_SCALE ?? "");
3477
- if (Number.isFinite(raw) && raw > 0)
3478
- return Math.max(0.25, Math.min(1, raw));
3479
- return DEFAULT_IMAGE_RESOLUTION_SCALE;
3480
- }
3481
- function rgbaWord(color) {
3482
- return (color[3] << 24 | color[2] << 16 | color[1] << 8 | color[0]) >>> 0;
3483
- }
3484
- function fillRgba(data, color, word = rgbaWord(color)) {
3485
- if (data.byteOffset % 4 === 0 && data.byteLength % 4 === 0) {
3486
- new Uint32Array(data.buffer, data.byteOffset, data.byteLength / 4).fill(word);
3487
- return;
3488
- }
3489
- for (let i = 0;i < data.length; i += 4) {
3490
- data[i] = color[0];
3491
- data[i + 1] = color[1];
3492
- data[i + 2] = color[2];
3493
- data[i + 3] = color[3];
3494
- }
3495
- }
3496
- var ASCII_GLYPHS = {
3497
- ".": ["00000", "00000", "00000", "00000", "00000", "00100", "00100"],
3498
- "'": ["00100", "00100", "00000", "00000", "00000", "00000", "00000"],
3499
- "-": ["00000", "00000", "00000", "11111", "00000", "00000", "00000"],
3500
- _: ["00000", "00000", "00000", "00000", "00000", "00000", "11111"],
3501
- "=": ["00000", "11111", "00000", "00000", "11111", "00000", "00000"],
3502
- "|": ["00100", "00100", "00100", "00100", "00100", "00100", "00100"],
3503
- "/": ["00001", "00010", "00010", "00100", "01000", "01000", "10000"],
3504
- "\\": ["10000", "01000", "01000", "00100", "00010", "00010", "00001"],
3505
- "(": ["00010", "00100", "01000", "01000", "01000", "00100", "00010"],
3506
- ")": ["01000", "00100", "00010", "00010", "00010", "00100", "01000"],
3507
- "!": ["00100", "00100", "00100", "00100", "00100", "00000", "00100"],
3508
- "%": ["11001", "11010", "00100", "01000", "10110", "00110", "00000"],
3509
- $: ["00100", "11110", "10100", "11110", "00101", "11110", "00100"],
3510
- "@": ["01110", "10001", "10111", "10101", "10111", "10000", "01111"],
3511
- o: ["00000", "01110", "10001", "10001", "10001", "01110", "00000"],
3512
- "*": ["00100", "10101", "01110", "11111", "01110", "10101", "00100"]
3513
- };
3514
- function sceneStaticTick(scene) {
3515
- let hash = 0;
3516
- for (let index = 0;index < scene.length; index += 1)
3517
- hash = hash * 33 + scene.charCodeAt(index) >>> 0;
3518
- return 11 + hash % 97;
3519
- }
3520
- function charColor(char, row, totalRows) {
3521
- if (char === "@" || char === "$" || char === "o")
3522
- return LIME;
3523
- if (char === "%" || char === "!" || char === "/" || char === "\\" || char === "|")
3524
- return CYAN;
3525
- if (char === "." || char === "'" || char === "_" || row < 3 || row > totalRows - 4)
3526
- return INK_DIM;
3527
- if (char === "-" || char === "=" || char === "(" || char === ")")
3528
- return LIME_DIM;
3529
- return MAGENTA;
3530
- }
3531
- function fillRectAlpha(data, width, height, left, top, rectWidth, rectHeight, color, alpha) {
3532
- const x0 = Math.max(0, Math.floor(left));
3533
- const y0 = Math.max(0, Math.floor(top));
3534
- const x1 = Math.min(width, Math.ceil(left + rectWidth));
3535
- const y1 = Math.min(height, Math.ceil(top + rectHeight));
3536
- for (let y = y0;y < y1; y += 1) {
3537
- for (let x = x0;x < x1; x += 1)
3538
- blendOver(data, (y * width + x) * 4, color, alpha);
3539
- }
3540
- }
3541
- function drawAsciiGlyph(data, width, height, x, y, cellWidth, cellHeight, char, color, alpha) {
3542
- const pattern = ASCII_GLYPHS[char] ?? ASCII_GLYPHS["*"];
3543
- const glyphWidth = Math.max(1, cellWidth * 0.56);
3544
- const glyphHeight = Math.max(1, cellHeight * 0.7);
3545
- const originX = x + (cellWidth - glyphWidth) * 0.5;
3546
- const originY = y + (cellHeight - glyphHeight) * 0.5;
3547
- const pixelWidth = Math.max(1, glyphWidth / pattern[0].length);
3548
- const pixelHeight = Math.max(1, glyphHeight / pattern.length);
3549
- for (let row = 0;row < pattern.length; row += 1) {
3550
- const bits = pattern[row];
3551
- for (let col = 0;col < bits.length; col += 1) {
3552
- if (bits[col] !== "1")
3553
- continue;
3554
- fillRectAlpha(data, width, height, originX + col * pixelWidth, originY + row * pixelHeight, pixelWidth * 0.72, pixelHeight * 0.72, color, alpha);
3555
- }
3556
- }
3557
- }
3558
- function drawStaticAsciiFleetBackground(data, width, height, scene, layout, load, activeRuns) {
3559
- const rows = fleetRows(sceneStaticTick(scene), { animate: true });
3560
- const cellWidth = width / Math.max(1, layout.width);
3561
- const cellHeight = height / Math.max(1, layout.height);
3562
- const sideBySide = layout.width >= FLEET_GRID_WIDTH * 1.9;
3563
- const leftCells = sideBySide ? Math.max(1, Math.floor(layout.width * 0.3 - FLEET_GRID_WIDTH / 2)) : Math.floor((layout.width - FLEET_GRID_WIDTH) / 2);
3564
- const topCells = Math.floor((layout.height - FLEET_GRID_HEIGHT) / 2);
3565
- const fleetLeft = leftCells * cellWidth;
3566
- const fleetTop = topCells * cellHeight;
3567
- drawGlow2(data, width, height, width * 0.5, height * 0.5, Math.min(width, height) * 0.4, LIME, 0.022 + load * 0.03);
3568
- drawGlow2(data, width, height, width * 0.18, height * 0.22, Math.min(width, height) * 0.28, CYAN, 0.018 + load * 0.012);
3569
- drawGlow2(data, width, height, width * 0.82, height * 0.78, Math.min(width, height) * 0.22, MAGENTA, 0.012 + load * 0.008);
3570
- let droneTotal = 0;
3571
- for (const line2 of rows)
3572
- for (const char of line2)
3573
- if (char === "@" || char === "$" || char === "%")
3574
- droneTotal += 1;
3575
- const litTarget = Math.max(0, Math.min(droneTotal, activeRuns));
3576
- let droneIndex = 0;
3577
- for (let row = 0;row < rows.length; row += 1) {
3578
- const line2 = rows[row];
3579
- for (let col = 0;col < line2.length; col += 1) {
3580
- const char = line2[col];
3581
- if (char === " ")
3582
- continue;
3583
- const x = fleetLeft + col * cellWidth;
3584
- const y = fleetTop + row * cellHeight;
3585
- const color = charColor(char, row, rows.length);
3586
- const isDrone = char === "@" || char === "$" || char === "%";
3587
- const lit = isDrone && droneIndex < litTarget;
3588
- drawAsciiGlyph(data, width, height, x + cellWidth * 0.07, y + cellHeight * 0.05, cellWidth, cellHeight, char, color, lit ? 0.72 : 0.46);
3589
- if (isDrone) {
3590
- droneIndex += 1;
3591
- drawGlow2(data, width, height, x + cellWidth * 0.5, y + cellHeight * 0.5, Math.max(cellWidth, cellHeight) * 0.95, color, lit ? 0.044 : 0.014);
3592
- }
3593
- }
3594
- }
3595
- }
3596
- function applyStaticAtmosphere(data, width, height, load) {
3597
- const cx = width * 0.5;
3598
- const cy = height * 0.42;
3599
- const radius = Math.max(width, height) * 0.78;
3600
- const minVignette = 0.1 - load * 0.02;
3601
- for (let y = 0;y < height; y += 1) {
3602
- for (let x = 0;x < width; x += 1) {
3603
- const distance = Math.hypot(x - cx, y - cy) / radius;
3604
- const alpha = Math.max(minVignette, Math.min(0.82, 0.12 + Math.pow(distance, 1.65) * 0.62));
3605
- blendOver(data, (y * width + x) * 4, BG, alpha);
3606
- }
3607
- }
3608
- drawGlow2(data, width, height, width * 0.82, height * 0.04, Math.min(width, height) * 0.2, LIME, 0.024 + load * 0.012);
3609
- drawGlow2(data, width, height, width * 0.12, height * 0.18, Math.min(width, height) * 0.16, CYAN, 0.018 + load * 0.008);
3610
- }
3611
- function makeCrcTable() {
3612
- const table = new Uint32Array(256);
3613
- for (let n = 0;n < 256; n += 1) {
3614
- let c = n;
3615
- for (let k = 0;k < 8; k += 1)
3616
- c = c & 1 ? 3988292384 ^ c >>> 1 : c >>> 1;
3617
- table[n] = c >>> 0;
3618
- }
3619
- return table;
3620
- }
3621
- function crc32(bytes) {
3622
- let c = 4294967295;
3623
- for (let i = 0;i < bytes.length; i += 1)
3624
- c = CRC_TABLE[(c ^ bytes[i]) & 255] ^ c >>> 8;
3625
- return (c ^ 4294967295) >>> 0;
3626
- }
3627
- function chunk2(type, data) {
3628
- const out = new Uint8Array(12 + data.length);
3629
- const view = new DataView(out.buffer);
3630
- view.setUint32(0, data.length);
3631
- out[4] = type.charCodeAt(0);
3632
- out[5] = type.charCodeAt(1);
3633
- out[6] = type.charCodeAt(2);
3634
- out[7] = type.charCodeAt(3);
3635
- out.set(data, 8);
3636
- view.setUint32(8 + data.length, crc32(out.subarray(4, 8 + data.length)));
3637
- return out;
3638
- }
3639
- function encodePng(width, height, rgba) {
3640
- const ihdr = new Uint8Array(13);
3641
- const ihdrView = new DataView(ihdr.buffer);
3642
- ihdrView.setUint32(0, width);
3643
- ihdrView.setUint32(4, height);
3644
- ihdr[8] = 8;
3645
- ihdr[9] = 6;
3646
- ihdr[10] = 0;
3647
- ihdr[11] = 0;
3648
- ihdr[12] = 0;
3649
- const stride = width * 4;
3650
- const raw = new Uint8Array((stride + 1) * height);
3651
- for (let y = 0;y < height; y += 1) {
3652
- const rawOffset = y * (stride + 1);
3653
- raw[rawOffset] = 0;
3654
- raw.set(rgba.subarray(y * stride, y * stride + stride), rawOffset + 1);
3655
- }
3656
- const compressed = deflateSync(raw, { level: ZLIB_LEVEL });
3657
- return Buffer.concat([
3658
- Buffer.from(PNG_SIGNATURE),
3659
- Buffer.from(chunk2("IHDR", ihdr)),
3660
- Buffer.from(chunk2("IDAT", compressed)),
3661
- Buffer.from(chunk2("IEND", new Uint8Array(0)))
3662
- ]);
3663
- }
3664
- function kittyImageSequence(base64, options) {
3665
- const params = [`a=T`, `f=${options.format}`, `q=2`, `C=1`, `c=${options.columns}`, `r=${options.rows}`, `i=${options.imageId}`, `z=${options.zIndex}`];
3666
- if (options.format === 32) {
3667
- params.push(`s=${options.pixelWidth ?? 1}`, `v=${options.pixelHeight ?? 1}`);
3668
- if (options.compressed)
3669
- params.push("o=z");
3670
- }
3671
- if (base64.length <= KITTY_CHUNK_SIZE)
3672
- return `\x1B_G${params.join(",")};${base64}\x1B\\`;
3673
- const chunks = [];
3674
- for (let offset = 0;offset < base64.length; offset += KITTY_CHUNK_SIZE) {
3675
- const part = base64.slice(offset, offset + KITTY_CHUNK_SIZE);
3676
- const last = offset + KITTY_CHUNK_SIZE >= base64.length;
3677
- chunks.push(`\x1B_G${offset === 0 ? `${params.join(",")},` : ""}m=${last ? 0 : 1};${part}\x1B\\`);
3678
- }
3679
- return chunks.join("");
3680
- }
3681
- function clampByte(value) {
3682
- return Math.max(0, Math.min(255, Math.round(value)));
3683
- }
3684
- function blendOver(data, index, color, alphaScale = 1) {
3685
- const alpha = color[3] / 255 * alphaScale;
3686
- const inv = 1 - alpha;
3687
- data[index] = clampByte(color[0] * alpha + data[index] * inv);
3688
- data[index + 1] = clampByte(color[1] * alpha + data[index + 1] * inv);
3689
- data[index + 2] = clampByte(color[2] * alpha + data[index + 2] * inv);
3690
- data[index + 3] = 255;
3691
- }
3692
- function drawDisc(data, width, height, cx, cy, radius, color, alpha = 1) {
3693
- const minX = Math.max(0, Math.floor(cx - radius));
3694
- const maxX = Math.min(width - 1, Math.ceil(cx + radius));
3695
- const minY = Math.max(0, Math.floor(cy - radius));
3696
- const maxY = Math.min(height - 1, Math.ceil(cy + radius));
3697
- for (let y = minY;y <= maxY; y += 1) {
3698
- for (let x = minX;x <= maxX; x += 1) {
3699
- const dx = x + 0.5 - cx;
3700
- const dy = y + 0.5 - cy;
3701
- const d = Math.sqrt(dx * dx + dy * dy);
3702
- if (d > radius)
3703
- continue;
3704
- const edge = Math.max(0, Math.min(1, radius - d));
3705
- blendOver(data, (y * width + x) * 4, color, alpha * Math.min(1, 0.35 + edge));
3706
- }
3707
- }
3708
- }
3709
- function drawGlow2(data, width, height, cx, cy, radius, color, alpha = 0.18) {
3710
- const minX = Math.max(0, Math.floor(cx - radius));
3711
- const maxX = Math.min(width - 1, Math.ceil(cx + radius));
3712
- const minY = Math.max(0, Math.floor(cy - radius));
3713
- const maxY = Math.min(height - 1, Math.ceil(cy + radius));
3714
- for (let y = minY;y <= maxY; y += 1) {
3715
- for (let x = minX;x <= maxX; x += 1) {
3716
- const dx = x + 0.5 - cx;
3717
- const dy = y + 0.5 - cy;
3718
- const d = Math.sqrt(dx * dx + dy * dy);
3719
- if (d > radius)
3720
- continue;
3721
- const falloff = Math.pow(1 - d / Math.max(1, radius), GLOW_FALLOFF_EXP_IMAGE);
3722
- blendOver(data, (y * width + x) * 4, color, alpha * falloff);
3723
- }
3724
- }
3725
- }
3726
- function drawLine2(data, width, height, x0, y0, x1, y1, color, alpha = 0.5, thickness = 1.4) {
3727
- const dx = x1 - x0;
3728
- const dy = y1 - y0;
3729
- const steps = Math.max(1, Math.ceil(Math.hypot(dx, dy) * 1.2));
3730
- for (let i = 0;i <= steps; i += 1) {
3731
- const t2 = i / steps;
3732
- drawDisc(data, width, height, x0 + dx * t2, y0 + dy * t2, thickness, color, alpha);
3733
- }
3734
- }
3735
- function roundRectAlpha(px, py, x, y, w, h, r) {
3736
- const rx = Math.max(x + r, Math.min(px, x + w - r));
3737
- const ry = Math.max(y + r, Math.min(py, y + h - r));
3738
- const dx = px - rx;
3739
- const dy = py - ry;
3740
- const dist = Math.sqrt(dx * dx + dy * dy);
3741
- return Math.max(0, Math.min(1, r + 0.75 - dist));
3742
- }
3743
- function panelRect(layout, panel, size) {
3744
- return panelPixelPlacement(layout, panel, size);
3745
- }
3746
- function downsampleRegion(data, canvasW, canvasH, rect, scale) {
3747
- const w = Math.max(1, Math.ceil(rect.width / scale));
3748
- const h = Math.max(1, Math.ceil(rect.height / scale));
3749
- const out = new Uint8ClampedArray(w * h * 3);
3750
- for (let y = 0;y < h; y += 1) {
3751
- for (let x = 0;x < w; x += 1) {
3752
- let r = 0, g = 0, b = 0, count = 0;
3753
- const sx0 = rect.left + x * scale;
3754
- const sy0 = rect.top + y * scale;
3755
- for (let yy = 0;yy < scale; yy += 1) {
3756
- const sy = sy0 + yy;
3757
- if (sy < 0 || sy >= canvasH)
3758
- continue;
3759
- for (let xx = 0;xx < scale; xx += 1) {
3760
- const sx = sx0 + xx;
3761
- if (sx < 0 || sx >= canvasW)
3762
- continue;
3763
- const i = (sy * canvasW + sx) * 4;
3764
- r += data[i];
3765
- g += data[i + 1];
3766
- b += data[i + 2];
3767
- count += 1;
3768
- }
3769
- }
3770
- const oi = (y * w + x) * 3;
3771
- out[oi] = count ? r / count : BG[0];
3772
- out[oi + 1] = count ? g / count : BG[1];
3773
- out[oi + 2] = count ? b / count : BG[2];
3774
- }
3775
- }
3776
- return { pixels: out, width: w, height: h };
3777
- }
3778
- function blurRgb(buffer, width, height, radius) {
3779
- const tmp = new Uint8ClampedArray(buffer.length);
3780
- const out = new Uint8ClampedArray(buffer.length);
3781
- const window = radius * 2 + 1;
3782
- for (let y = 0;y < height; y += 1) {
3783
- let r = 0, g = 0, b = 0;
3784
- for (let x = -radius;x <= radius; x += 1) {
3785
- const sx = Math.max(0, Math.min(width - 1, x));
3786
- const i = (y * width + sx) * 3;
3787
- r += buffer[i];
3788
- g += buffer[i + 1];
3789
- b += buffer[i + 2];
3790
- }
3791
- for (let x = 0;x < width; x += 1) {
3792
- const oi = (y * width + x) * 3;
3793
- tmp[oi] = r / window;
3794
- tmp[oi + 1] = g / window;
3795
- tmp[oi + 2] = b / window;
3796
- const removeX = Math.max(0, x - radius);
3797
- const addX = Math.min(width - 1, x + radius + 1);
3798
- const ri = (y * width + removeX) * 3;
3799
- const ai = (y * width + addX) * 3;
3800
- r += tmp.length ? buffer[ai] - buffer[ri] : 0;
3801
- g += tmp.length ? buffer[ai + 1] - buffer[ri + 1] : 0;
3802
- b += tmp.length ? buffer[ai + 2] - buffer[ri + 2] : 0;
3803
- }
3804
- }
3805
- for (let x = 0;x < width; x += 1) {
3806
- let r = 0, g = 0, b = 0;
3807
- for (let y = -radius;y <= radius; y += 1) {
3808
- const sy = Math.max(0, Math.min(height - 1, y));
3809
- const i = (sy * width + x) * 3;
3810
- r += tmp[i];
3811
- g += tmp[i + 1];
3812
- b += tmp[i + 2];
3813
- }
3814
- for (let y = 0;y < height; y += 1) {
3815
- const oi = (y * width + x) * 3;
3816
- out[oi] = r / window;
3817
- out[oi + 1] = g / window;
3818
- out[oi + 2] = b / window;
3819
- const removeY = Math.max(0, y - radius);
3820
- const addY = Math.min(height - 1, y + radius + 1);
3821
- const ri = (removeY * width + x) * 3;
3822
- const ai = (addY * width + x) * 3;
3823
- r += tmp[ai] - tmp[ri];
3824
- g += tmp[ai + 1] - tmp[ri + 1];
3825
- b += tmp[ai + 2] - tmp[ri + 2];
3826
- }
3827
- }
3828
- return out;
3829
- }
3830
- function panelMaskKey(width, height, rect, headerPx, blurScale) {
3831
- return `${width}x${height}:${rect.left},${rect.top},${rect.width},${rect.height}:${Math.round(headerPx * 100) / 100}:${blurScale}`;
3832
- }
3833
- function getPanelMask(width, height, rect, headerPx, blurScale) {
3834
- const key = panelMaskKey(width, height, rect, headerPx, blurScale);
3835
- const cached = panelMaskCache.get(key);
3836
- if (cached)
3837
- return cached;
3838
- const radius = Math.max(PANEL_MASK_RADIUS_MIN, Math.min(PANEL_MASK_RADIUS_MAX, Math.round(Math.min(rect.width, rect.height) * PANEL_MASK_RADIUS_FACTOR)));
3839
- const sampledWidth = Math.max(1, Math.ceil(rect.width / blurScale));
3840
- const sampledHeight = Math.max(1, Math.ceil(rect.height / blurScale));
3841
- const x0 = Math.max(0, rect.left);
3842
- const y0 = Math.max(0, rect.top);
3843
- const x1 = Math.min(width - 1, rect.left + rect.width - 1);
3844
- const y1 = Math.min(height - 1, rect.top + rect.height - 1);
3845
- const indices = [];
3846
- const sampleIndices = [];
3847
- const panelAlpha = [];
3848
- const edgeAlpha = [];
3849
- const headerFlags = [];
3850
- const dividerIndices = [];
3851
- for (let y = y0;y <= y1; y += 1) {
3852
- for (let x = x0;x <= x1; x += 1) {
3853
- const a = roundRectAlpha(x + 0.5, y + 0.5, rect.left, rect.top, rect.width, rect.height, radius);
3854
- if (a <= 0)
3855
- continue;
3856
- const edge = roundRectAlpha(x + 0.5, y + 0.5, rect.left + 1.2, rect.top + 1.2, rect.width - 2.4, rect.height - 2.4, Math.max(1, radius - 1.2));
3857
- const sx = Math.max(0, Math.min(sampledWidth - 1, Math.floor((x - rect.left) / blurScale)));
3858
- const sy = Math.max(0, Math.min(sampledHeight - 1, Math.floor((y - rect.top) / blurScale)));
3859
- indices.push((y * width + x) * 4);
3860
- sampleIndices.push((sy * sampledWidth + sx) * 3);
3861
- panelAlpha.push(a);
3862
- edgeAlpha.push(edge < 0.96 ? Math.min(1, (1 - edge) * 0.7) : 0);
3863
- headerFlags.push(y < rect.top + headerPx ? 1 : 0);
3864
- }
3865
- }
3866
- const dividerY = Math.round(rect.top + headerPx);
3867
- if (dividerY > y0 && dividerY < y1) {
3868
- for (let x = x0 + radius;x <= x1 - radius; x += 1) {
3869
- dividerIndices.push((dividerY * width + x) * 4);
3870
- }
3871
- }
3872
- const mask = {
3873
- key,
3874
- indices: Uint32Array.from(indices),
3875
- sampleIndices: Uint32Array.from(sampleIndices),
3876
- panelAlpha: Float32Array.from(panelAlpha),
3877
- edgeAlpha: Float32Array.from(edgeAlpha),
3878
- headerFlags: Uint8Array.from(headerFlags),
3879
- dividerIndices: Uint32Array.from(dividerIndices)
3880
- };
3881
- panelMaskCache.set(key, mask);
3882
- while (panelMaskCache.size > PANEL_MASK_CACHE_LIMIT) {
3883
- const oldest = panelMaskCache.keys().next().value;
3884
- if (!oldest)
3885
- break;
3886
- panelMaskCache.delete(oldest);
3887
- }
3888
- return mask;
3889
- }
3890
- function compositePanel(data, width, height, rect, headerPx) {
3891
- const blurScale = PANEL_BLUR_SCALE;
3892
- const sampled = downsampleRegion(data, width, height, rect, blurScale);
3893
- const blurRadius = Math.max(PANEL_BLUR_RADIUS_MIN, Math.min(PANEL_BLUR_RADIUS_MAX, Math.round(Math.min(rect.width, rect.height) * PANEL_BLUR_RADIUS_FACTOR)));
3894
- const blurred = blurRgb(sampled.pixels, sampled.width, sampled.height, blurRadius);
3895
- const mask = getPanelMask(width, height, rect, headerPx, blurScale);
3896
- for (let i = 0;i < mask.indices.length; i += 1) {
3897
- const idx = mask.indices[i];
3898
- const sampleIdx = mask.sampleIndices[i];
3899
- data[idx] = blurred[sampleIdx];
3900
- data[idx + 1] = blurred[sampleIdx + 1];
3901
- data[idx + 2] = blurred[sampleIdx + 2];
3902
- data[idx + 3] = 255;
3903
- blendOver(data, idx, mask.headerFlags[i] ? PANEL_HEADER : PANEL, mask.panelAlpha[i]);
3904
- const edge = mask.edgeAlpha[i];
3905
- if (edge > 0)
3906
- blendOver(data, idx, PANEL_LINE2, edge);
3907
- }
3908
- for (let i = 0;i < mask.dividerIndices.length; i += 1) {
3909
- blendOver(data, mask.dividerIndices[i], PANEL_LINE2, 0.28);
3910
- }
3911
- }
3912
- var RIG_GLYPH_STROKES = {
3913
- R: [[0, 0, 0, 1], [0, 0, 0.82, 0], [0.82, 0, 0.82, 0.5], [0, 0.5, 0.82, 0.5], [0.36, 0.5, 0.92, 1]],
3914
- I: [[0.5, 0, 0.5, 1], [0.18, 0, 0.82, 0], [0.18, 1, 0.82, 1]],
3915
- G: [[0.92, 0.08, 0.12, 0], [0.12, 0, 0.12, 1], [0.12, 1, 0.92, 1], [0.92, 0.5, 0.92, 1], [0.5, 0.5, 0.92, 0.5]]
3916
- };
3917
- function drawBigRigWordmark(data, width, height, layout, load) {
3918
- const sideBySide = layout.width >= FLEET_GRID_WIDTH * 1.9;
3919
- if (!sideBySide)
3920
- return;
3921
- const letters = "RIG";
3922
- const min = Math.min(width, height);
3923
- const glyphH = height * 0.42;
3924
- const glyphW = width * 0.13;
3925
- const gap = glyphW * 0.3;
3926
- const totalW = letters.length * glyphW + (letters.length - 1) * gap;
3927
- const centerX = width * 0.71;
3928
- const startX = centerX - totalW / 2;
3929
- const top = (height - glyphH) / 2;
3930
- const stroke = Math.max(2, min * 0.013);
3931
- drawGlow2(data, width, height, centerX, top + glyphH / 2, Math.max(glyphW, glyphH) * 0.9, LIME, 0.022 + load * 0.02);
3932
- const strokeAlpha = 0.1 + load * 0.06;
3933
- letters.split("").forEach((letter, index) => {
3934
- const segments = RIG_GLYPH_STROKES[letter];
3935
- if (!segments)
3936
- return;
3937
- const ox = startX + index * (glyphW + gap);
3938
- for (const [x0, y0, x1, y1] of segments) {
3939
- drawLine2(data, width, height, ox + x0 * glyphW, top + y0 * glyphH, ox + x1 * glyphW, top + y1 * glyphH, LIME_DIM, strokeAlpha, stroke);
3940
- }
3941
- });
3942
- }
3943
- function drawBackground(data, width, height, _tick, scene, layout, load, activeRuns) {
3944
- fillRgba(data, BG, BG_WORD);
3945
- drawStaticAsciiFleetBackground(data, width, height, scene, layout, load, activeRuns);
3946
- drawBigRigWordmark(data, width, height, layout, load);
3947
- applyStaticAtmosphere(data, width, height, load);
3948
- }
3949
- function imageVisualFrameKey(input) {
3950
- const width = Math.max(1, Math.floor(input.pixelSize.width));
3951
- const height = Math.max(1, Math.floor(input.pixelSize.height));
3952
- const layer = "layer" in input ? input.layer ?? "full" : "full";
3953
- const load = loadBucket(input.load);
3954
- return `${layer}:${width}x${height}:${input.layout.width}x${input.layout.height}:${input.scene}:${input.tick}:l${load}:${input.panels.map((panel) => `${panel.id}:${panel.top}:${panel.height}:${panel.width ?? ""}:${panel.left ?? ""}:${panel.headerHeight ?? ""}`).join("|")}`;
3955
- }
3956
- function encodeKittyFrame(width, height, data, input) {
3957
- if (input.transport === "png") {
3958
- const png = encodePng(width, height, data);
3959
- return kittyImageSequence(png.toString("base64"), { columns: input.layout.width, rows: input.layout.height, imageId: input.imageId, zIndex: input.zIndex, format: 100 });
3960
- }
3961
- const compressed = deflateSync(data, { level: ZLIB_LEVEL });
3962
- return kittyImageSequence(compressed.toString("base64"), {
3963
- columns: input.layout.width,
3964
- rows: input.layout.height,
3965
- imageId: input.imageId,
3966
- zIndex: input.zIndex,
3967
- format: 32,
3968
- pixelWidth: width,
3969
- pixelHeight: height,
3970
- compressed: true
3971
- });
3972
- }
3973
- function cropRgba(data, sourceWidth, rect) {
3974
- const out = new Uint8ClampedArray(rect.width * rect.height * 4);
3975
- for (let y = 0;y < rect.height; y += 1) {
3976
- const sourceStart = ((rect.top + y) * sourceWidth + rect.left) * 4;
3977
- out.set(data.subarray(sourceStart, sourceStart + rect.width * 4), y * rect.width * 4);
3978
- }
3979
- return out;
3980
- }
3981
- function encodePanelPatch(data, sourceWidth, placement, imageId, zIndex) {
3982
- const patch = cropRgba(data, sourceWidth, placement);
3983
- const png = encodePng(placement.width, placement.height, patch);
3984
- const sequence = kittyImageSequence(png.toString("base64"), {
3985
- columns: Math.max(1, Math.round(placement.widthCells)),
3986
- rows: Math.max(1, Math.round(placement.heightCells)),
3987
- imageId,
3988
- zIndex,
3989
- format: 100
3990
- });
3991
- return `\x1B[${Math.max(1, Math.round(placement.topCells) + 1)};${Math.max(1, Math.round(placement.leftCells) + 1)}H${sequence}`;
3992
- }
3993
- function renderImageVisualFrame(input) {
3994
- const width = Math.max(1, Math.floor(input.pixelSize.width));
3995
- const height = Math.max(1, Math.floor(input.pixelSize.height));
3996
- const byteCount = width * height * 4;
3997
- if (byteCount > MAX_IMAGE_BYTES)
3998
- return null;
3999
- const layer = input.layer ?? "full";
4000
- const data = new Uint8ClampedArray(byteCount);
4001
- const load = loadBucket(input.load) / LOAD_BUCKETS;
4002
- const activeRuns = Math.max(0, Math.floor(input.activeRuns ?? 0));
4003
- drawBackground(data, width, height, 0, input.scene, input.layout, load, activeRuns);
4004
- if (layer === "split") {
4005
- const sequences = [encodeKittyFrame(width, height, data, input)];
4006
- let panelIndex = 0;
4007
- for (const panel of input.panels) {
4008
- if (panel.chrome !== "ad-terminal")
4009
- continue;
4010
- const placement = panelPixelPlacement(input.layout, panel, { width, height });
4011
- if (!placement)
4012
- continue;
4013
- const cellH = height / input.layout.height;
4014
- const headerPx = Math.max(cellH * 3, (panel.headerHeight ?? 3) * cellH);
4015
- compositePanel(data, width, height, placement, headerPx);
4016
- const imageId = input.imageIds?.[panelIndex] ?? (input.imageId + 1000 + panelIndex >>> 0 || panelIndex + 1);
4017
- sequences.push(encodePanelPatch(data, width, placement, imageId, -12));
4018
- panelIndex += 1;
4019
- }
4020
- return { key: imageVisualFrameKey(input), sequence: sequences.join("") };
4021
- }
4022
- if (layer === "panel-chrome")
4023
- return { key: imageVisualFrameKey(input), sequence: "" };
4024
- if (layer === "background") {
4025
- return { key: imageVisualFrameKey(input), sequence: encodeKittyFrame(width, height, data, input) };
4026
- }
4027
- if (layer === "panel-patches") {
4028
- const sequences = [];
4029
- let panelIndex = 0;
4030
- for (const panel of input.panels) {
4031
- if (panel.chrome !== "ad-terminal")
4032
- continue;
4033
- const placement = panelPixelPlacement(input.layout, panel, { width, height });
4034
- if (!placement)
4035
- continue;
4036
- const cellH = height / input.layout.height;
4037
- const headerPx = Math.max(cellH * 3, (panel.headerHeight ?? 3) * cellH);
4038
- compositePanel(data, width, height, placement, headerPx);
4039
- const imageId = input.imageIds?.[panelIndex] ?? (input.imageId + 1000 + panelIndex >>> 0 || panelIndex + 1);
4040
- sequences.push(encodePanelPatch(data, width, placement, imageId, input.zIndex));
4041
- panelIndex += 1;
4042
- }
4043
- return { key: imageVisualFrameKey(input), sequence: sequences.join("") };
4044
- }
4045
- for (const panel of input.panels) {
4046
- if (panel.chrome !== "ad-terminal")
4047
- continue;
4048
- const rect = panelRect(input.layout, panel, { width, height });
4049
- if (!rect)
4050
- continue;
4051
- const cellH = height / input.layout.height;
4052
- const headerPx = Math.max(cellH * 3, (panel.headerHeight ?? 3) * cellH);
4053
- compositePanel(data, width, height, rect, headerPx);
4054
- }
4055
- return { key: imageVisualFrameKey(input), sequence: encodeKittyFrame(width, height, data, input) };
4056
- }
4057
- function shouldRenderImagesInline() {
4058
- return process.env.RIG_OPENTUI_IMAGE_WORKER === "0" || import.meta.url.includes("$bunfs");
4059
- }
4060
- function createImageWorker() {
4061
- if (shouldRenderImagesInline())
4062
- return null;
4063
- const isTypeScriptRuntime = import.meta.url.endsWith(".ts");
4064
- const workerUrl = new URL(isTypeScriptRuntime ? "./image-visual-layer-worker.ts" : "./image-visual-layer-worker.js", import.meta.url);
4065
- try {
4066
- return new Worker(workerUrl);
4067
- } catch {
4068
- if (typeof process.versions.bun === "string") {
4069
- try {
4070
- return new Worker("./src/app-opentui/render/image-visual-layer-worker.ts");
4071
- } catch {}
4072
- }
4073
- return null;
4074
- }
4075
- }
4076
- var RECENT_WRITTEN_LIMIT = 24;
4077
- var DEFAULT_IMAGE_OUTPUT_BACKLOG_BYTES = 256 * 1024;
4078
- var DEFAULT_IMAGE_OUTPUT_BYTES_PER_SECOND = 850 * 1024;
4079
- function imageOutputBacklogBytes() {
4080
- const raw = Number.parseInt(process.env.RIG_OPENTUI_IMAGE_BACKLOG_BYTES ?? "", 10);
4081
- if (Number.isFinite(raw) && raw > 0)
4082
- return Math.max(64 * 1024, Math.min(2 * 1024 * 1024, raw));
4083
- return DEFAULT_IMAGE_OUTPUT_BACKLOG_BYTES;
4084
- }
4085
- function imageOutputBytesPerSecond() {
4086
- const raw = Number.parseInt(process.env.RIG_OPENTUI_IMAGE_BYTES_PER_SECOND ?? "", 10);
4087
- if (Number.isFinite(raw) && raw > 0)
4088
- return Math.max(128 * 1024, Math.min(2 * 1024 * 1024, raw));
4089
- return DEFAULT_IMAGE_OUTPUT_BYTES_PER_SECOND;
4090
- }
4091
- function pendingLayerKey(key) {
4092
- return key.split(":", 1)[0] || "full";
4093
- }
4094
-
4095
- class ImageVisualLayer {
4096
- imageId = allocateImageId();
4097
- panelImageIds = Array.from({ length: 8 }, () => allocateImageId());
4098
- panelChromeImageIds = Array.from({ length: 8 }, () => allocateImageId());
4099
- stdout;
4100
- onFrameReady;
4101
- protocol;
4102
- transport = imageTransport();
4103
- maxOutputBacklogBytes = imageOutputBacklogBytes();
4104
- outputBytesPerSecond = imageOutputBytesPerSecond();
4105
- outputBudgetBytes = imageOutputBytesPerSecond();
4106
- lastBudgetAtMs = Date.now();
4107
- motionSlot = this.createSlot();
4108
- workersStarted = false;
4109
- pendingFrames = new Map;
4110
- recentWrittenKeys = [];
4111
- imagesVisible = false;
4112
- flushPaused = false;
4113
- requestId = 0;
4114
- lastBackgroundKey = "";
4115
- fatalWorkerError = null;
4116
- destroyed = false;
4117
- constructor(stdout = process.stdout, onFrameReady) {
4118
- this.stdout = stdout;
4119
- this.onFrameReady = onFrameReady;
4120
- this.protocol = getRigTerminalCapabilities().imageProtocol;
4121
- }
4122
- get enabled() {
4123
- return this.protocol === "kitty";
4124
- }
4125
- clear() {
4126
- if (this.enabled) {
4127
- if (this.imagesVisible)
4128
- this.stdout.write([this.imageId, ...this.panelImageIds, ...this.panelChromeImageIds].map((id) => deleteKittyImage(id)).join("") + "\x1B[?25h");
4129
- this.imagesVisible = false;
4130
- this.pendingFrames.clear();
4131
- this.recentWrittenKeys.length = 0;
4132
- this.resetSlot(this.motionSlot);
4133
- this.lastBackgroundKey = "";
4134
- this.outputBudgetBytes = this.outputBytesPerSecond;
4135
- this.lastBudgetAtMs = Date.now();
4136
- this.motionSlot.clearedThroughRequestId = this.requestId;
4137
- }
4138
- }
4139
- destroy() {
4140
- if (this.destroyed)
4141
- return;
4142
- this.clear();
4143
- this.destroyed = true;
4144
- const workers = [this.motionSlot.worker];
4145
- this.motionSlot.worker = null;
4146
- for (const worker of workers)
4147
- if (worker)
4148
- worker.terminate().catch(() => {
4149
- return;
4150
- });
4151
- }
4152
- setFlushPaused(paused) {
4153
- this.flushPaused = paused;
4154
- }
4155
- isReady(input) {
4156
- if (!this.enabled || this.destroyed)
4157
- return true;
4158
- this.throwIfFatal();
4159
- const splitKey = imageVisualFrameKey({ ...input, tick: 0, layer: "split" });
4160
- return this.recentWrittenKeys.includes(splitKey) || Array.from(this.pendingFrames.values()).some((frame) => frame.key === splitKey);
4161
- }
4162
- render(input) {
4163
- if (!this.enabled || this.destroyed)
4164
- return;
4165
- this.throwIfFatal();
4166
- if (this.transport !== "png")
4167
- this.failWorker("renderer", new Error("OpenTUI image visuals require PNG transport for static-background rendering."));
4168
- this.ensureWorkersStarted();
4169
- const width = Math.max(1, Math.floor(input.pixelSize.width));
4170
- const height = Math.max(1, Math.floor(input.pixelSize.height));
4171
- if (width * height * 4 > MAX_IMAGE_BYTES)
4172
- return;
4173
- if (this.isOutputBackedUp())
4174
- return;
4175
- this.refillOutputBudget();
4176
- const splitInput = { ...input, tick: 0, layer: "split" };
4177
- const splitKey = imageVisualFrameKey(splitInput);
4178
- if (input.force || splitKey !== this.lastBackgroundKey) {
4179
- this.scheduleRender(this.motionSlot, splitInput, this.imageId, -30, input.force, this.panelImageIds);
4180
- this.lastBackgroundKey = splitKey;
4181
- }
4182
- }
4183
- flush() {
4184
- this.throwIfFatal();
4185
- if (this.flushPaused)
4186
- return false;
4187
- if (this.pendingFrames.size === 0)
4188
- return false;
4189
- this.refillOutputBudget();
4190
- if (this.isOutputBackedUp()) {
4191
- const retainedSplit = this.pendingFrames.get("split");
4192
- this.pendingFrames.clear();
4193
- if (retainedSplit)
4194
- this.pendingFrames.set("split", retainedSplit);
4195
- return false;
4196
- }
4197
- const framePriority = (frame) => {
4198
- const layer = pendingLayerKey(frame.key);
4199
- if (layer === "split")
4200
- return 1;
4201
- return 4;
4202
- };
4203
- const frames = Array.from(this.pendingFrames.values()).sort((a, b) => framePriority(a) - framePriority(b));
4204
- const prefix = "\x1B[?25l\x1B7\x1B[s\x1B[H";
4205
- const suffix = "\x1B[u\x1B8";
4206
- const selected = [];
4207
- const retained = new Map;
4208
- let sequence = "";
4209
- let byteLength = Buffer.byteLength(prefix + suffix);
4210
- for (const frame of frames) {
4211
- const candidate = sequence + frame.sequence;
4212
- const candidateBytes = Buffer.byteLength(prefix + candidate + suffix);
4213
- const layer = pendingLayerKey(frame.key);
4214
- const allowOneStaticFrame = selected.length === 0 && layer === "split";
4215
- if (candidateBytes <= this.outputBudgetBytes || allowOneStaticFrame) {
4216
- selected.push(frame);
4217
- sequence = candidate;
4218
- byteLength = candidateBytes;
4219
- } else if (layer === "split") {
4220
- retained.set(layer, frame);
4221
- }
4222
- }
4223
- this.pendingFrames.clear();
4224
- for (const [layer, frame] of retained)
4225
- this.pendingFrames.set(layer, frame);
4226
- if (!sequence || selected.length === 0)
4227
- return false;
4228
- const payload = `${prefix}${sequence}${suffix}`;
4229
- for (const frame of selected)
4230
- this.markWritten(frame.key);
4231
- this.outputBudgetBytes -= byteLength;
4232
- this.stdout.cork?.();
4233
- const accepted = this.stdout.write(payload);
4234
- this.stdout.uncork?.();
4235
- this.imagesVisible = true;
4236
- return accepted;
4237
- }
4238
- createSlot() {
4239
- return { worker: null, busy: false, queuedRequest: null, inFlightKey: "", queuedKey: "", clearedThroughRequestId: 0, latestRequestId: 0 };
4240
- }
4241
- resetSlot(slot) {
4242
- slot.busy = false;
4243
- slot.queuedRequest = null;
4244
- slot.inFlightKey = "";
4245
- slot.queuedKey = "";
4246
- }
4247
- markWritten(key) {
4248
- this.recentWrittenKeys.push(key);
4249
- while (this.recentWrittenKeys.length > RECENT_WRITTEN_LIMIT)
4250
- this.recentWrittenKeys.shift();
4251
- }
4252
- refillOutputBudget() {
4253
- const now = Date.now();
4254
- const elapsedMs = Math.max(0, now - this.lastBudgetAtMs);
4255
- this.lastBudgetAtMs = now;
4256
- this.outputBudgetBytes = Math.min(this.outputBytesPerSecond, this.outputBudgetBytes + elapsedMs / 1000 * this.outputBytesPerSecond);
4257
- }
4258
- isOutputBackedUp() {
4259
- return Boolean(this.stdout.writableNeedDrain) || this.stdout.writableLength > this.maxOutputBacklogBytes;
4260
- }
4261
- stageFrame(frame) {
4262
- this.pendingFrames.set(pendingLayerKey(frame.key), frame);
4263
- }
4264
- isKnownKey(slot, key) {
4265
- return this.recentWrittenKeys.includes(key) || Array.from(this.pendingFrames.values()).some((frame) => frame.key === key) || key === slot.inFlightKey || key === slot.queuedKey;
4266
- }
4267
- ensureWorkersStarted() {
4268
- if (this.workersStarted)
4269
- return;
4270
- this.workersStarted = true;
4271
- this.startWorker(this.motionSlot, "static-visuals");
4272
- }
4273
- failWorker(name, error) {
4274
- const detail = error instanceof Error ? error.message : String(error);
4275
- this.fatalWorkerError ??= new Error(`OpenTUI image worker ${name} failed: ${detail}`);
4276
- throw this.fatalWorkerError;
4277
- }
4278
- throwIfFatal() {
4279
- if (this.fatalWorkerError)
4280
- throw this.fatalWorkerError;
4281
- }
4282
- scheduleRender(slot, input, imageId, zIndex, force, imageIds) {
4283
- this.throwIfFatal();
4284
- const key = imageVisualFrameKey(input);
4285
- if (!force && this.isKnownKey(slot, key))
4286
- return;
4287
- const request = {
4288
- ...input,
4289
- imageId,
4290
- ...imageIds ? { imageIds } : {},
4291
- zIndex,
4292
- transport: this.transport,
4293
- requestId: ++this.requestId,
4294
- key
4295
- };
4296
- slot.latestRequestId = request.requestId;
4297
- if (!slot.worker) {
4298
- this.renderInline(slot, request);
4299
- return;
4300
- }
4301
- this.enqueueWorkerRender(slot, request);
4302
- }
4303
- startWorker(slot, name) {
4304
- const worker = createImageWorker();
4305
- if (!worker)
4306
- return;
4307
- slot.worker = worker;
4308
- worker.on("message", (message) => this.handleWorkerMessage(slot, message));
4309
- worker.once("error", (error) => {
4310
- if (!this.destroyed)
4311
- this.fatalWorkerError ??= new Error(`OpenTUI image worker ${name} failed: ${error instanceof Error ? error.message : String(error)}`);
4312
- });
4313
- worker.once("exit", (code) => {
4314
- if (!this.destroyed)
4315
- this.fatalWorkerError ??= new Error(`OpenTUI image worker ${name} exited unexpectedly with code ${code}.`);
4316
- });
4317
- }
4318
- renderInline(slot, request) {
4319
- try {
4320
- const result = renderImageVisualFrame(request);
4321
- if (result?.sequence && !this.isKnownKey(slot, result.key) && (!this.isOutputBackedUp() || pendingLayerKey(result.key) === "split")) {
4322
- this.stageFrame({ key: result.key, sequence: result.sequence });
4323
- this.onFrameReady?.();
4324
- }
4325
- } catch (error) {
4326
- this.failWorker(String(request.layer ?? "visual"), error);
4327
- }
4328
- }
4329
- enqueueWorkerRender(slot, request) {
4330
- this.throwIfFatal();
4331
- if (slot.busy) {
4332
- slot.queuedRequest = request;
4333
- slot.queuedKey = request.key;
4334
- return;
4335
- }
4336
- this.dispatchWorkerRender(slot, request);
4337
- }
4338
- dispatchWorkerRender(slot, request) {
4339
- this.throwIfFatal();
4340
- if (!slot.worker)
4341
- this.failWorker(String(request.layer ?? "visual"), new Error("worker is unavailable"));
4342
- slot.busy = true;
4343
- slot.inFlightKey = request.key;
4344
- try {
4345
- slot.worker.postMessage(request);
4346
- } catch (error) {
4347
- this.failWorker(String(request.layer ?? "visual"), error);
4348
- }
4349
- }
4350
- handleWorkerMessage(slot, message) {
4351
- if (this.destroyed || !message || typeof message !== "object")
4352
- return;
4353
- const response = message;
4354
- slot.busy = false;
4355
- slot.inFlightKey = "";
4356
- const isStale = response.requestId <= slot.clearedThroughRequestId || response.requestId < slot.latestRequestId;
4357
- if (!isStale) {
4358
- if (response.error)
4359
- this.failWorker(pendingLayerKey(response.key), new Error(response.error));
4360
- if (response.sequence && !this.isKnownKey(slot, response.key) && (!this.isOutputBackedUp() || pendingLayerKey(response.key) === "split")) {
4361
- this.stageFrame({ key: response.key, sequence: response.sequence });
4362
- this.onFrameReady?.();
4363
- }
4364
- }
4365
- const next = slot.queuedRequest;
4366
- slot.queuedRequest = null;
4367
- slot.queuedKey = "";
4368
- if (next && !this.isKnownKey(slot, next.key))
4369
- this.dispatchWorkerRender(slot, next);
4370
- }
4371
- }
4372
- function pixelSizeForRenderer(renderer) {
4373
- const resolution = renderer.resolution;
4374
- const base = resolution?.width && resolution?.height ? resolution : { width: renderer.width * 12, height: renderer.height * 24 };
4375
- const scale = imageResolutionScale();
4376
- return {
4377
- width: Math.max(renderer.width, Math.floor(base.width * scale)),
4378
- height: Math.max(renderer.height, Math.floor(base.height * scale))
4379
- };
4380
- }
4381
- function forceModernImageVisuals() {
4382
- return getRigTerminalCapabilities().supportsKittyImages;
4383
- }
4384
-
4385
3399
  // packages/cli/src/app-opentui/render/text.ts
4386
3400
  import { RGBA as RGBA4, TextAttributes as TextAttributes4, TextRenderable } from "@opentui/core";
4387
3401
  var TRANSPARENT2 = RGBA4.fromInts(0, 0, 0, 0);
@@ -5557,7 +4571,6 @@ async function launchRigOpenTuiApp(options) {
5557
4571
  const events = createAppEventBus();
5558
4572
  let renderer = null;
5559
4573
  let graphics = null;
5560
- let imageVisuals = null;
5561
4574
  let preloaderBackdrop = null;
5562
4575
  const scrollPanels = [];
5563
4576
  let typeBar = null;
@@ -5585,7 +4598,7 @@ async function launchRigOpenTuiApp(options) {
5585
4598
  if (!renderer || destroyed)
5586
4599
  return;
5587
4600
  const currentState = store.getState();
5588
- renderedSelectableItems = renderNow(renderer, graphics, imageVisuals, preloaderBackdrop, scrollPanels, typeBar, brandText, navText, textLines, nativeHost, currentState, sceneRendererRegistry, preloaderTransition, scheduleAppRender);
4601
+ renderedSelectableItems = renderNow(renderer, graphics, preloaderBackdrop, scrollPanels, typeBar, brandText, navText, textLines, nativeHost, currentState, sceneRendererRegistry, preloaderTransition, scheduleAppRender);
5589
4602
  focusManager.restoreTypeBarFocus(typeBar, { hidden: currentState.scene === "handoff" });
5590
4603
  maybeRedirectFromUnresolvedScene(currentState);
5591
4604
  };
@@ -5745,18 +4758,8 @@ async function launchRigOpenTuiApp(options) {
5745
4758
  });
5746
4759
  renderer.setTerminalTitle?.("Operator cockpit");
5747
4760
  const layout = computeStageLayout(renderer.width, renderer.height);
5748
- imageVisuals = forceModernImageVisuals() ? new ImageVisualLayer(process.stdout, () => scheduleAppRender(0)) : null;
5749
- if (imageVisuals?.enabled) {
5750
- const flushImageVisuals = async () => {
5751
- imageVisuals?.flush();
5752
- };
5753
- renderer.setFrameCallback(flushImageVisuals);
5754
- resources.add(() => renderer?.removeFrameCallback(flushImageVisuals));
5755
- }
5756
- if (!imageVisuals?.enabled) {
5757
- graphics = createGraphicsLayer(renderer, layout);
5758
- renderer.root.add(graphics);
5759
- }
4761
+ graphics = createGraphicsLayer(renderer, layout);
4762
+ renderer.root.add(graphics);
5760
4763
  preloaderBackdrop = new BoxRenderable3(renderer, {
5761
4764
  id: "rig-preloader-backdrop",
5762
4765
  position: "absolute",
@@ -5882,7 +4885,6 @@ async function launchRigOpenTuiApp(options) {
5882
4885
  stopActivePiHost("renderer destroyed");
5883
4886
  stopActiveCommandHost("renderer destroyed");
5884
4887
  destroyNativeHost(nativeHost);
5885
- imageVisuals?.destroy();
5886
4888
  if (renderTimer)
5887
4889
  clearTimeout(renderTimer);
5888
4890
  renderTimer = null;
@@ -5931,14 +4933,13 @@ async function launchRigOpenTuiApp(options) {
5931
4933
  clearTimeout(renderTimer);
5932
4934
  renderTimer = null;
5933
4935
  resources.disposeAll();
5934
- imageVisuals?.destroy();
5935
4936
  stopActivePiHost("app shutdown");
5936
4937
  stopActiveCommandHost("app shutdown");
5937
4938
  if (renderer && !renderer.isDestroyed)
5938
4939
  renderer.destroy();
5939
4940
  }
5940
4941
  }
5941
- function renderNow(renderer, graphics, imageVisuals, preloaderBackdrop, scrollPanels, typeBar, brandText, navText, textLines, nativeHost, state, sceneRendererRegistry, preloaderTransition, scheduleRenderAfter) {
4942
+ function renderNow(renderer, graphics, preloaderBackdrop, scrollPanels, typeBar, brandText, navText, textLines, nativeHost, state, sceneRendererRegistry, preloaderTransition, scheduleRenderAfter) {
5942
4943
  const layout = computeStageLayout(renderer.width, renderer.height);
5943
4944
  const hideNav = () => {
5944
4945
  if (navText)
@@ -5947,7 +4948,6 @@ function renderNow(renderer, graphics, imageVisuals, preloaderBackdrop, scrollPa
5947
4948
  if (layout.tooSmall) {
5948
4949
  if (graphics)
5949
4950
  clearGraphicsLayer(graphics);
5950
- imageVisuals?.clear();
5951
4951
  if (preloaderBackdrop)
5952
4952
  preloaderBackdrop.visible = false;
5953
4953
  if (brandText)
@@ -5981,8 +4981,7 @@ function renderNow(renderer, graphics, imageVisuals, preloaderBackdrop, scrollPa
5981
4981
  preloaderTransition.minUntilMs = now;
5982
4982
  const targetFrame = renderState.typeBar.mode === "command" ? commandPaletteFrame(renderState, layout) : rendererForScene(renderState.scene, sceneRendererRegistry)(renderState, layout);
5983
4983
  const targetContentLayout = targetFrame.fullScreen ? { ...layout, centerWidth: layout.width, centerLeft: 0, centerTop: 0, centerHeight: Math.min(layout.height, MAX_SCENE_LINES) } : layout;
5984
- const targetPanelsForImages = (targetFrame.panels ?? []).map((panel) => ({ ...panel }));
5985
- let targetVisualReady = true;
4984
+ const targetVisualReady = true;
5986
4985
  const fleetStats = fleetStatsFromState(state);
5987
4986
  const fleetLoad = fleetStats.load;
5988
4987
  const fleetActive = fleetStats.active;
@@ -5992,23 +4991,7 @@ function renderNow(renderer, graphics, imageVisuals, preloaderBackdrop, scrollPa
5992
4991
  if (renderState.scene === "command") {
5993
4992
  getActiveCommandHost()?.resize(Math.max(40, targetContentLayout.centerWidth - 12), Math.max(10, targetContentLayout.centerHeight - 6));
5994
4993
  }
5995
- if (imageVisuals && targetFrame.terminalActive) {
5996
- imageVisuals.clear();
5997
- } else if (imageVisuals && !targetFrame.fullScreen && !sceneJustChanged) {
5998
- const visualInput = {
5999
- layout: targetContentLayout,
6000
- pixelSize: pixelSizeForRenderer(renderer),
6001
- panels: targetPanelsForImages,
6002
- scene: state.scene,
6003
- tick: state.tick,
6004
- load: fleetLoad,
6005
- activeRuns: fleetActive
6006
- };
6007
- imageVisuals.render(visualInput);
6008
- targetVisualReady = imageVisuals.isReady(visualInput);
6009
- }
6010
4994
  const preloaderActive = sceneJustChanged && !cachedFastPath || isPreloaderActive(preloaderTransition, targetVisualReady, now);
6011
- imageVisuals?.setFlushPaused(preloaderActive);
6012
4995
  const frame = preloaderActive ? renderAsciiPreloaderFrame(renderState.scene, layout, state.tick) : targetFrame;
6013
4996
  if (graphics) {
6014
4997
  resizeGraphicsLayer(graphics, layout);