ink-hud 0.1.2 → 0.1.4

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/dist/index.cjs CHANGED
@@ -4,6 +4,7 @@ var React6 = require('react');
4
4
  var chalk = require('chalk');
5
5
  var tinygradient = require('tinygradient');
6
6
  var ink = require('ink');
7
+ var zlib = require('zlib');
7
8
 
8
9
  function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
9
10
 
@@ -440,186 +441,6 @@ var BlockRenderer = class _BlockRenderer extends Renderer {
440
441
  }
441
442
  };
442
443
 
443
- // src/core/ascii.ts
444
- var AsciiRenderer = class extends Renderer {
445
- getMetadata() {
446
- return {
447
- name: "ascii",
448
- displayName: "ASCII",
449
- description: "ASCII character (. _ - ' / \\ |) 1x3 resolution, maximum compatibility",
450
- resolution: {
451
- horizontal: 1,
452
- vertical: 3
453
- },
454
- requiresUtf8: false,
455
- requiresUnicode: false,
456
- minScore: 0
457
- // No requirements, usable in any terminal
458
- };
459
- }
460
- // ============================================================
461
- // ASCII-specific private methods
462
- // ============================================================
463
- /**
464
- * Check if the pixel at the specified position is set
465
- */
466
- isPixelSet(pixels, x, y) {
467
- const row = pixels[y];
468
- if (!row) return false;
469
- return row[x]?.active ?? false;
470
- }
471
- getCellPositions(pixels, x, baseY) {
472
- const positions = [];
473
- for (let offset = 0; offset < 3; offset++) {
474
- if (this.isPixelSet(pixels, x, baseY + offset)) {
475
- positions.push(offset);
476
- }
477
- }
478
- return positions;
479
- }
480
- getNeighborPosition(pixels, x, baseY) {
481
- const positions = this.getCellPositions(pixels, x, baseY);
482
- if (positions.length === 0) {
483
- return null;
484
- }
485
- const sum = positions.reduce((total, value) => total + value, 0);
486
- return sum / positions.length;
487
- }
488
- selectMultiPixelChar(hasAnyLeft, hasAnyRight) {
489
- return hasAnyLeft || hasAnyRight ? "+" : "|";
490
- }
491
- selectHorizontalChar(pos) {
492
- if (pos === 0) {
493
- return "'";
494
- }
495
- if (pos === 1) {
496
- return "-";
497
- }
498
- return "_";
499
- }
500
- selectDiagonalChar(params) {
501
- const { pos, hasAnyLeft, hasAnyRight, leftPos, rightPos } = params;
502
- if (hasAnyLeft && leftPos !== null && leftPos !== pos) {
503
- return pos < leftPos ? "/" : "\\";
504
- }
505
- if (hasAnyRight && rightPos !== null && rightPos !== pos) {
506
- return pos > rightPos ? "/" : "\\";
507
- }
508
- return null;
509
- }
510
- selectIsolatedChar(pos) {
511
- if (pos === 0) {
512
- return '"';
513
- }
514
- if (pos === 1) {
515
- return "+";
516
- }
517
- return ".";
518
- }
519
- selectSinglePixelChar(params) {
520
- const { pos, hasSameHorizontal, hasAnyLeft, hasAnyRight, leftPos, rightPos } = params;
521
- if (hasSameHorizontal) {
522
- return this.selectHorizontalChar(pos);
523
- }
524
- const diagonal = this.selectDiagonalChar({
525
- pos,
526
- hasAnyLeft,
527
- hasAnyRight,
528
- leftPos,
529
- rightPos
530
- });
531
- if (diagonal) {
532
- return diagonal;
533
- }
534
- return this.selectIsolatedChar(pos);
535
- }
536
- /**
537
- * Intelligently select ASCII character
538
- * Select appropriate character based on 1x3 vertical pixels and adjacent column connections
539
- */
540
- selectAsciiChar(pixels, x, baseY) {
541
- const positions = this.getCellPositions(pixels, x, baseY);
542
- if (positions.length === 0) {
543
- return " ";
544
- }
545
- const leftPos = this.getNeighborPosition(pixels, x - 1, baseY);
546
- const rightPos = this.getNeighborPosition(pixels, x + 1, baseY);
547
- const hasAnyLeft = leftPos !== null;
548
- const hasAnyRight = rightPos !== null;
549
- if (positions.length >= 2) {
550
- return this.selectMultiPixelChar(hasAnyLeft, hasAnyRight);
551
- }
552
- const pos = positions[0] ?? 1;
553
- const hasLeftSame = this.isPixelSet(pixels, x - 1, baseY + pos);
554
- const hasRightSame = this.isPixelSet(pixels, x + 1, baseY + pos);
555
- const hasSameHorizontal = hasLeftSame || hasRightSame;
556
- return this.selectSinglePixelChar({
557
- pos,
558
- hasSameHorizontal,
559
- hasAnyLeft,
560
- hasAnyRight,
561
- leftPos,
562
- rightPos
563
- });
564
- }
565
- /**
566
- * Determine the primary color for this character cell
567
- */
568
- resolveColor(pixels, x, baseY) {
569
- const counts = /* @__PURE__ */ new Map();
570
- for (let offset = 0; offset < 3; offset++) {
571
- const y = baseY + offset;
572
- if (y >= pixels.length) continue;
573
- const pixel = pixels[y]?.[x];
574
- if (pixel?.active && pixel.color) {
575
- counts.set(pixel.color, (counts.get(pixel.color) ?? 0) + 1);
576
- }
577
- }
578
- if (counts.size === 0) return void 0;
579
- let maxCount = 0;
580
- let dominantColor;
581
- for (const [color, count] of counts) {
582
- if (count > maxCount) {
583
- maxCount = count;
584
- dominantColor = color;
585
- }
586
- }
587
- return dominantColor;
588
- }
589
- // ============================================================
590
- // Methods that subclasses must implement
591
- // ============================================================
592
- renderCanvas(pixels, width, height) {
593
- const lines = [];
594
- const charHeight = Math.ceil(height / 3);
595
- for (let cy = 0; cy < charHeight; cy++) {
596
- const lineSegments = [];
597
- const baseY = cy * 3;
598
- let buffer = "";
599
- let bufferColor;
600
- for (let x = 0; x < width; x++) {
601
- const char = this.selectAsciiChar(pixels, x, baseY);
602
- const color = char === " " ? void 0 : this.resolveColor(pixels, x, baseY);
603
- if (buffer && bufferColor !== color) {
604
- lineSegments.push(
605
- bufferColor ? { text: buffer, color: bufferColor } : { text: buffer }
606
- );
607
- buffer = "";
608
- }
609
- bufferColor = color;
610
- buffer += char;
611
- }
612
- if (buffer) {
613
- lineSegments.push(
614
- bufferColor ? { text: buffer, color: bufferColor } : { text: buffer }
615
- );
616
- }
617
- lines.push(lineSegments);
618
- }
619
- return lines;
620
- }
621
- };
622
-
623
444
  // src/detect/terminal.ts
624
445
  var TerminalDetector = class _TerminalDetector {
625
446
  /** Terminal whitelist supporting Braille characters (lowercase) */
@@ -796,8 +617,6 @@ var RendererSelector = class {
796
617
  return new BrailleRenderer();
797
618
  case "block":
798
619
  return new BlockRenderer();
799
- case "ascii":
800
- return new AsciiRenderer();
801
620
  }
802
621
  }
803
622
  /**
@@ -808,6 +627,17 @@ var RendererSelector = class {
808
627
  getRenderer(type) {
809
628
  return this.createRenderer(type);
810
629
  }
630
+ /**
631
+ * Check if a renderer type is supported by the current terminal
632
+ * Used internally by InkHudProvider to determine the per-chart renderer
633
+ * @param type - Renderer type
634
+ * @returns Whether the renderer type is supported
635
+ */
636
+ isRendererTypeSupported(type) {
637
+ const capabilities = this.detector.detect();
638
+ const renderer = this.createRenderer(type);
639
+ return this.isRendererSupported(renderer, capabilities);
640
+ }
811
641
  /**
812
642
  * Check if the renderer meets terminal capability requirements
813
643
  * @param renderer - Renderer instance
@@ -835,15 +665,16 @@ var RendererSelector = class {
835
665
  return true;
836
666
  }
837
667
  /**
838
- * Automatically select the best renderer
668
+ * Automatically select the best renderer from a priority chain.
839
669
  *
840
- * Try in the order of the priority chain, returning the first renderer that meets terminal capability requirements
841
- * If none are satisfied, fallback to ASCII
670
+ * @deprecated Prefer `<InkHudProvider renderers={{ line: 'block' }}>` combined
671
+ * with `useChartRenderer(kind)`. This method remains as a low-level escape
672
+ * hatch for custom renderer selection outside the React component tree.
842
673
  *
843
- * @param preferredChain - Priority chain (default: ['braille', 'block', 'ascii'])
674
+ * @param preferredChain - Priority chain (default: ['braille', 'block'])
844
675
  * @returns Selected renderer instance
845
676
  */
846
- selectBest(preferredChain = ["braille", "block", "ascii"]) {
677
+ selectBest(preferredChain = ["braille", "block"]) {
847
678
  const capabilities = this.detector.detect();
848
679
  for (const rendererType of preferredChain) {
849
680
  const renderer = this.getRenderer(rendererType);
@@ -851,7 +682,7 @@ var RendererSelector = class {
851
682
  return renderer;
852
683
  }
853
684
  }
854
- return this.getRenderer("ascii");
685
+ return this.getRenderer("block");
855
686
  }
856
687
  /**
857
688
  * Get terminal capability information
@@ -862,33 +693,70 @@ var RendererSelector = class {
862
693
  }
863
694
  };
864
695
  var rendererSelector = new RendererSelector();
696
+
697
+ // src/components/InkHudProvider.tsx
698
+ var DEFAULT_CHART_RENDERERS = {
699
+ line: "braille",
700
+ area: "braille",
701
+ bar: "block",
702
+ pie: "block"
703
+ };
704
+ var selectorFallbackWarnings = /* @__PURE__ */ new WeakMap();
705
+ function warnFallback(selector, kind, target) {
706
+ if (process.env.NODE_ENV === "production") return;
707
+ let warned = selectorFallbackWarnings.get(selector);
708
+ if (!warned) {
709
+ warned = /* @__PURE__ */ new Set();
710
+ selectorFallbackWarnings.set(selector, warned);
711
+ }
712
+ const key = `${kind}:${target}`;
713
+ if (warned.has(key)) return;
714
+ warned.add(key);
715
+ console.warn(
716
+ `[ink-hud] Renderer '${target}' is not supported by the current terminal; falling back to 'block' for chart kind '${kind}'. Override via <InkHudProvider renderers={{ ${kind}: 'block' }}>.`
717
+ );
718
+ }
865
719
  var defaultSelector = new RendererSelector();
866
720
  var defaultContext = {
867
721
  selector: defaultSelector,
868
722
  getCapabilities: () => defaultSelector.getTerminalCapabilities(),
869
723
  getRenderer: (type) => defaultSelector.getRenderer(type),
870
- selectBest: (chain) => defaultSelector.selectBest(chain)
724
+ getRendererFor: (kind) => {
725
+ const target = DEFAULT_CHART_RENDERERS[kind];
726
+ if (defaultSelector.isRendererTypeSupported(target)) {
727
+ return defaultSelector.getRenderer(target);
728
+ }
729
+ warnFallback(defaultSelector, kind, target);
730
+ return defaultSelector.getRenderer("block");
731
+ }
871
732
  };
872
733
  var InkHudContext = React6.createContext(defaultContext);
873
734
  var InkHudProvider = ({
874
735
  detector,
875
- forceRenderer,
736
+ renderers,
876
737
  children
877
738
  }) => {
878
739
  const value = React6.useMemo(() => {
879
740
  const selector = detector ? new RendererSelector(detector) : defaultSelector;
741
+ const mergedRenderers = {
742
+ ...DEFAULT_CHART_RENDERERS,
743
+ ...renderers
744
+ };
745
+ const getRendererFor = (kind) => {
746
+ const target = mergedRenderers[kind];
747
+ if (selector.isRendererTypeSupported(target)) {
748
+ return selector.getRenderer(target);
749
+ }
750
+ warnFallback(selector, kind, target);
751
+ return selector.getRenderer("block");
752
+ };
880
753
  return {
881
754
  selector,
882
755
  getCapabilities: () => selector.getTerminalCapabilities(),
883
756
  getRenderer: (type) => selector.getRenderer(type),
884
- selectBest: (chain) => {
885
- if (forceRenderer) {
886
- return selector.getRenderer(forceRenderer);
887
- }
888
- return selector.selectBest(chain);
889
- }
757
+ getRendererFor
890
758
  };
891
- }, [detector, forceRenderer]);
759
+ }, [detector, renderers]);
892
760
  return /* @__PURE__ */ React6__default.default.createElement(InkHudContext.Provider, { value }, children);
893
761
  };
894
762
  function useInkHud() {
@@ -1364,6 +1232,420 @@ function useThrottle(value, fps = 30) {
1364
1232
  }, [value, fps]);
1365
1233
  return throttledValue;
1366
1234
  }
1235
+
1236
+ // src/render/capabilities.ts
1237
+ function detectImageProtocol(env = process.env) {
1238
+ const override = env.INKHU_IMAGE_PROTOCOL;
1239
+ if (override === "kitty") return "kitty";
1240
+ if (override === "iterm2") return "iterm2";
1241
+ if (override === "none") return null;
1242
+ if (env.KITTY_WINDOW_ID) return "kitty";
1243
+ const tp = (env.TERM_PROGRAM ?? "").toLowerCase();
1244
+ if (tp === "wezterm" || tp === "ghostty") return "kitty";
1245
+ if (tp === "iterm.app") return "iterm2";
1246
+ return null;
1247
+ }
1248
+
1249
+ // src/render/image/iterm2.ts
1250
+ function encodeIterm2(pngBuffer, cols) {
1251
+ const b64 = pngBuffer.toString("base64");
1252
+ const args = ["inline=1", "preserveAspectRatio=1"];
1253
+ if (cols !== void 0) args.push(`width=${cols}`);
1254
+ return `\x1B]1337;File=${args.join(";")}:${b64}\x07`;
1255
+ }
1256
+
1257
+ // src/render/image/kitty.ts
1258
+ var CHUNK_SIZE = 4096;
1259
+ var DIACRITICS = [
1260
+ // 0–99 (original set)
1261
+ 773,
1262
+ 781,
1263
+ 782,
1264
+ 784,
1265
+ 786,
1266
+ 829,
1267
+ 830,
1268
+ 831,
1269
+ 838,
1270
+ 842,
1271
+ 843,
1272
+ 844,
1273
+ 848,
1274
+ 849,
1275
+ 850,
1276
+ 855,
1277
+ 859,
1278
+ 867,
1279
+ 868,
1280
+ 869,
1281
+ 870,
1282
+ 871,
1283
+ 872,
1284
+ 873,
1285
+ 874,
1286
+ 875,
1287
+ 876,
1288
+ 877,
1289
+ 878,
1290
+ 879,
1291
+ 1155,
1292
+ 1156,
1293
+ 1157,
1294
+ 1158,
1295
+ 1159,
1296
+ 1426,
1297
+ 1427,
1298
+ 1428,
1299
+ 1429,
1300
+ 1431,
1301
+ 1432,
1302
+ 1433,
1303
+ 1436,
1304
+ 1437,
1305
+ 1438,
1306
+ 1439,
1307
+ 1440,
1308
+ 1441,
1309
+ 1448,
1310
+ 1449,
1311
+ 1451,
1312
+ 1452,
1313
+ 1455,
1314
+ 1476,
1315
+ 1552,
1316
+ 1553,
1317
+ 1554,
1318
+ 1555,
1319
+ 1556,
1320
+ 1557,
1321
+ 1558,
1322
+ 1559,
1323
+ 1623,
1324
+ 1624,
1325
+ 1625,
1326
+ 1626,
1327
+ 1627,
1328
+ 1629,
1329
+ 1630,
1330
+ 1750,
1331
+ 1751,
1332
+ 1752,
1333
+ 1753,
1334
+ 1754,
1335
+ 1755,
1336
+ 1756,
1337
+ 1759,
1338
+ 1760,
1339
+ 1761,
1340
+ 1762,
1341
+ 1764,
1342
+ 1767,
1343
+ 1768,
1344
+ 1771,
1345
+ 1772,
1346
+ 1840,
1347
+ 1842,
1348
+ 1843,
1349
+ 1845,
1350
+ 1846,
1351
+ 1850,
1352
+ 1853,
1353
+ 1855,
1354
+ 1856,
1355
+ 1857,
1356
+ 1859,
1357
+ 1861,
1358
+ 1863,
1359
+ 1865,
1360
+ 1866,
1361
+ // 100–107
1362
+ 2027,
1363
+ 2028,
1364
+ 2029,
1365
+ 2030,
1366
+ 2031,
1367
+ 2032,
1368
+ 2033,
1369
+ 2035,
1370
+ // 108–128
1371
+ 2070,
1372
+ 2071,
1373
+ 2072,
1374
+ 2073,
1375
+ 2075,
1376
+ 2076,
1377
+ 2077,
1378
+ 2078,
1379
+ 2079,
1380
+ 2080,
1381
+ 2081,
1382
+ 2082,
1383
+ 2083,
1384
+ 2085,
1385
+ 2086,
1386
+ 2087,
1387
+ 2089,
1388
+ 2090,
1389
+ 2091,
1390
+ 2092,
1391
+ 2093,
1392
+ // 129–131
1393
+ 2385,
1394
+ 2387,
1395
+ 2388,
1396
+ // 132–135
1397
+ 3970,
1398
+ 3971,
1399
+ 3974,
1400
+ 3975,
1401
+ // 136–138
1402
+ 4957,
1403
+ 4958,
1404
+ 4959,
1405
+ // 139
1406
+ 6109,
1407
+ // 140
1408
+ 6458,
1409
+ // 141–149
1410
+ 6679,
1411
+ 6773,
1412
+ 6774,
1413
+ 6775,
1414
+ 6776,
1415
+ 6777,
1416
+ 6778,
1417
+ 6779,
1418
+ 6780,
1419
+ // 150–158
1420
+ 7019,
1421
+ 7020,
1422
+ 7021,
1423
+ 7022,
1424
+ 7023,
1425
+ 7024,
1426
+ 7025,
1427
+ 7026,
1428
+ 7027,
1429
+ // 159–173
1430
+ 7376,
1431
+ 7377,
1432
+ 7378,
1433
+ 7386,
1434
+ 7387,
1435
+ 7392,
1436
+ 7393,
1437
+ 7394,
1438
+ 7395,
1439
+ 7396,
1440
+ 7397,
1441
+ 7398,
1442
+ 7399,
1443
+ 7400,
1444
+ 7405,
1445
+ // 174–176
1446
+ 7412,
1447
+ 7416,
1448
+ 7417,
1449
+ // 177–187
1450
+ 7616,
1451
+ 7617,
1452
+ 7619,
1453
+ 7620,
1454
+ 7621,
1455
+ 7622,
1456
+ 7623,
1457
+ 7624,
1458
+ 7625,
1459
+ 7627,
1460
+ 7628,
1461
+ // 188–210
1462
+ 7633,
1463
+ 7634,
1464
+ 7635,
1465
+ 7636,
1466
+ 7637,
1467
+ 7638,
1468
+ 7639,
1469
+ 7640,
1470
+ 7641,
1471
+ 7642,
1472
+ 7643,
1473
+ 7644,
1474
+ 7645,
1475
+ 7646,
1476
+ 7647,
1477
+ 7648,
1478
+ 7649,
1479
+ 7650,
1480
+ 7651,
1481
+ 7652,
1482
+ 7653,
1483
+ 7654,
1484
+ 7678,
1485
+ // 211–222
1486
+ 8400,
1487
+ 8401,
1488
+ 8404,
1489
+ 8405,
1490
+ 8406,
1491
+ 8407,
1492
+ 8411,
1493
+ 8412,
1494
+ 8417,
1495
+ 8423,
1496
+ 8425,
1497
+ 8432,
1498
+ // 223–225
1499
+ 11503,
1500
+ 11504,
1501
+ 11505,
1502
+ // 226–255
1503
+ 11744,
1504
+ 11745,
1505
+ 11746,
1506
+ 11747,
1507
+ 11748,
1508
+ 11749,
1509
+ 11750,
1510
+ 11751,
1511
+ 11752,
1512
+ 11753,
1513
+ 11754,
1514
+ 11755,
1515
+ 11756,
1516
+ 11757,
1517
+ 11758,
1518
+ 11759,
1519
+ 11760,
1520
+ 11761,
1521
+ 11762,
1522
+ 11763,
1523
+ 11764,
1524
+ 11765,
1525
+ 11766,
1526
+ 11767,
1527
+ 11768,
1528
+ 11769,
1529
+ 11770,
1530
+ 11771,
1531
+ 11772,
1532
+ 11773
1533
+ ];
1534
+ function encodeKittyUpload(pngBuffer, cols, rows, imageId) {
1535
+ const b64 = pngBuffer.toString("base64");
1536
+ const parts = [];
1537
+ for (let i = 0; i < b64.length; i += CHUNK_SIZE) {
1538
+ const data = b64.slice(i, i + CHUNK_SIZE);
1539
+ const isFirst = i === 0;
1540
+ const isLast = i + CHUNK_SIZE >= b64.length;
1541
+ let params;
1542
+ if (isFirst) {
1543
+ const more = isLast ? 0 : 1;
1544
+ params = `a=T,U=1,i=${imageId},f=100,q=2,m=${more},c=${cols},r=${rows}`;
1545
+ } else {
1546
+ params = `m=${isLast ? 0 : 1},q=2`;
1547
+ }
1548
+ parts.push(`\x1B_G${params};${data}\x1B\\`);
1549
+ }
1550
+ return parts.join("");
1551
+ }
1552
+ function encodeKittyPlaceholders(cols, rows, imageId, options = {}) {
1553
+ const { trailingSpace = true } = options;
1554
+ const r = imageId >> 16 & 255;
1555
+ const g = imageId >> 8 & 255;
1556
+ const b = imageId & 255;
1557
+ const colorSeq = `\x1B[38;2;${r};${g};${b}m`;
1558
+ const resetSeq = "\x1B[39m";
1559
+ const placeholder = "\u{10EEEE}";
1560
+ const maxDim = DIACRITICS.length;
1561
+ if (rows > maxDim || cols > maxDim) {
1562
+ throw new RangeError(
1563
+ `encodeKittyPlaceholders: rows (${rows}) and cols (${cols}) must each be \u2264 ${maxDim}`
1564
+ );
1565
+ }
1566
+ const lines = [];
1567
+ for (let row = 0; row < rows; row++) {
1568
+ const rowMark = String.fromCodePoint(DIACRITICS[row] ?? 0);
1569
+ let line = colorSeq;
1570
+ for (let col = 0; col < cols; col++) {
1571
+ const colMark = String.fromCodePoint(DIACRITICS[col] ?? 0);
1572
+ line += placeholder + rowMark + colMark + (trailingSpace ? " " : "");
1573
+ }
1574
+ line += resetSeq;
1575
+ lines.push(line);
1576
+ }
1577
+ return lines.join("\n");
1578
+ }
1579
+ function encodeKittyDelete(imageId) {
1580
+ return `\x1B_Ga=d,d=I,i=${imageId}\x1B\\`;
1581
+ }
1582
+ function encodeKitty(pngBuffer, cols, rows) {
1583
+ const b64 = pngBuffer.toString("base64");
1584
+ const parts = [];
1585
+ for (let i = 0; i < b64.length; i += CHUNK_SIZE) {
1586
+ const data = b64.slice(i, i + CHUNK_SIZE);
1587
+ const isFirst = i === 0;
1588
+ const isLast = i + CHUNK_SIZE >= b64.length;
1589
+ let params;
1590
+ if (isFirst) {
1591
+ const more = isLast ? 0 : 1;
1592
+ params = `a=T,f=100,q=2,m=${more},c=${cols},r=${rows}`;
1593
+ } else {
1594
+ params = `m=${isLast ? 0 : 1},q=2`;
1595
+ }
1596
+ parts.push(`\x1B_G${params};${data}\x1B\\`);
1597
+ }
1598
+ return parts.join("");
1599
+ }
1600
+
1601
+ // src/hooks/useImageProtocol.ts
1602
+ var nextImageId = 1;
1603
+ function useImageProtocol({
1604
+ mode,
1605
+ charCols,
1606
+ charRows,
1607
+ pngBuf,
1608
+ trailingSpace = true
1609
+ }) {
1610
+ const { stdout } = ink.useStdout();
1611
+ const imageIdRef = React6.useRef(null);
1612
+ if (imageIdRef.current === null) {
1613
+ imageIdRef.current = nextImageId++;
1614
+ }
1615
+ const imageId = imageIdRef.current;
1616
+ const protocol = React6.useMemo(() => {
1617
+ if (mode === "character") return null;
1618
+ return detectImageProtocol();
1619
+ }, [mode]);
1620
+ const useImage = protocol !== null && mode !== "character" && pngBuf !== null && charCols > 0 && charRows > 0;
1621
+ React6.useEffect(() => {
1622
+ if (protocol !== "kitty" || !pngBuf || charCols <= 0 || charRows <= 0 || mode === "character")
1623
+ return;
1624
+ stdout.write(encodeKittyUpload(pngBuf, charCols, charRows, imageId));
1625
+ return () => {
1626
+ stdout.write(encodeKittyDelete(imageId));
1627
+ };
1628
+ }, [mode, protocol, pngBuf, charCols, charRows, imageId, stdout]);
1629
+ React6.useEffect(() => {
1630
+ if (protocol !== "iterm2" || !pngBuf || charCols <= 0 || charRows <= 0 || mode === "character")
1631
+ return;
1632
+ const terminalCols = charCols * (trailingSpace ? 2 : 1);
1633
+ const seq = encodeIterm2(pngBuf, terminalCols);
1634
+ stdout.write(`\x1B[${charRows}A\x1B[0G${seq}\x1B[${charRows}B`);
1635
+ }, [mode, protocol, pngBuf, charCols, charRows, trailingSpace, stdout]);
1636
+ if (!useImage) {
1637
+ return { useImage: false, kittyLines: null, iterm2Cols: null };
1638
+ }
1639
+ if (protocol === "kitty") {
1640
+ const text = encodeKittyPlaceholders(charCols, charRows, imageId, { trailingSpace });
1641
+ return { useImage: true, kittyLines: text.split("\n"), iterm2Cols: null };
1642
+ }
1643
+ return {
1644
+ useImage: true,
1645
+ kittyLines: null,
1646
+ iterm2Cols: charCols * (trailingSpace ? 2 : 1)
1647
+ };
1648
+ }
1367
1649
  var GridContext = React6.createContext({
1368
1650
  columns: 12,
1369
1651
  gap: 0,
@@ -1492,6 +1774,24 @@ var GridItem = ({
1492
1774
  };
1493
1775
 
1494
1776
  // src/components/common/chartUtils.ts
1777
+ function estimateHorizontalLegendRows(names, availableWidth) {
1778
+ if (!names || names.length === 0) return 1;
1779
+ const SYMBOL_WIDTH = 2;
1780
+ const ITEM_GAP = 2;
1781
+ let rows = 1;
1782
+ let used = 0;
1783
+ for (const n of names) {
1784
+ const itemWidth = SYMBOL_WIDTH + n.length;
1785
+ const needed = used === 0 ? itemWidth : itemWidth + ITEM_GAP;
1786
+ if (used + needed > availableWidth && used > 0) {
1787
+ rows++;
1788
+ used = itemWidth;
1789
+ } else {
1790
+ used += needed;
1791
+ }
1792
+ }
1793
+ return rows;
1794
+ }
1495
1795
  function resolveSeriesColors(series, colors, palette) {
1496
1796
  if (series.length === 0) {
1497
1797
  return [];
@@ -1519,7 +1819,8 @@ function useChartLayout(props, config) {
1519
1819
  defaultWidth = 60,
1520
1820
  defaultHeight = 15,
1521
1821
  min,
1522
- max
1822
+ max,
1823
+ legendNames
1523
1824
  } = config;
1524
1825
  const gridContext = React6.useContext(GridItemContext);
1525
1826
  const totalHeight = props.height ?? (typeof gridContext?.height === "number" ? gridContext.height : defaultHeight);
@@ -1542,9 +1843,11 @@ function useChartLayout(props, config) {
1542
1843
  let legendHeight = 0;
1543
1844
  if (showLegend) {
1544
1845
  if (legendPosition === "right") {
1545
- legendWidth = 20;
1846
+ legendWidth = legendNames && legendNames.length > 0 ? Math.max(...legendNames.map((n) => n.length)) + 2 : 20;
1546
1847
  } else {
1547
- legendHeight = 2;
1848
+ const legendAvailWidth = Math.max(1, totalWidth - yAxisWidth - (showYAxis ? 1 : 0));
1849
+ const contentRows = estimateHorizontalLegendRows(legendNames, legendAvailWidth);
1850
+ legendHeight = contentRows + 1;
1548
1851
  }
1549
1852
  }
1550
1853
  const yAxisSpacing = showYAxis ? 1 : 0;
@@ -1568,7 +1871,7 @@ function useChartLayout(props, config) {
1568
1871
  xAxisHeight: effectiveXAxisHeight
1569
1872
  };
1570
1873
  }
1571
- function useChartLayoutSimple(props, min, max) {
1874
+ function useChartLayoutSimple(props, min, max, legendNames) {
1572
1875
  const {
1573
1876
  width: propsWidth,
1574
1877
  height: propsHeight,
@@ -1603,7 +1906,8 @@ function useChartLayoutSimple(props, min, max) {
1603
1906
  yTickCount,
1604
1907
  ...yTickFormat && { yTickFormat },
1605
1908
  min,
1606
- max
1909
+ max,
1910
+ ...legendNames && { legendNames }
1607
1911
  }
1608
1912
  );
1609
1913
  }
@@ -1803,8 +2107,38 @@ var ChartContainer = ({
1803
2107
  children
1804
2108
  }) => {
1805
2109
  const { totalWidth, totalHeight, plotWidth, plotHeight, yAxisWidth } = layout;
1806
- return /* @__PURE__ */ React6__default.default.createElement(ink.Box, { flexDirection: "column", width: totalWidth, height: totalHeight }, showLegend && legendPosition === "top" && /* @__PURE__ */ React6__default.default.createElement(ink.Box, { marginBottom: 1, marginLeft: showYAxis ? yAxisWidth + 1 : 0 }, /* @__PURE__ */ React6__default.default.createElement(Legend, { items: legendItems, position: "horizontal" })), /* @__PURE__ */ React6__default.default.createElement(ink.Box, { flexDirection: "row" }, showYAxis && yAxisConfig && /* @__PURE__ */ React6__default.default.createElement(ink.Box, { marginRight: 1, width: yAxisWidth }, /* @__PURE__ */ React6__default.default.createElement(Axis, { type: "y", length: plotHeight, ...yAxisConfig })), /* @__PURE__ */ React6__default.default.createElement(ink.Box, { flexDirection: "column" }, children), showLegend && legendPosition === "right" && /* @__PURE__ */ React6__default.default.createElement(ink.Box, { marginLeft: 2 }, /* @__PURE__ */ React6__default.default.createElement(Legend, { items: legendItems, position: "vertical" }))), showXAxis && xAxisConfig && /* @__PURE__ */ React6__default.default.createElement(ink.Box, { marginLeft: showYAxis ? yAxisWidth + 1 : 0 }, /* @__PURE__ */ React6__default.default.createElement(Axis, { type: "x", length: plotWidth, ...xAxisConfig })), showLegend && legendPosition === "bottom" && /* @__PURE__ */ React6__default.default.createElement(ink.Box, { marginTop: 1, marginLeft: showYAxis ? yAxisWidth + 1 : 0 }, /* @__PURE__ */ React6__default.default.createElement(Legend, { items: legendItems, position: "horizontal" })));
2110
+ return /* @__PURE__ */ React6__default.default.createElement(ink.Box, { flexDirection: "column", width: totalWidth, height: totalHeight }, showLegend && legendPosition === "top" && /* @__PURE__ */ React6__default.default.createElement(ink.Box, { marginBottom: 1, marginLeft: showYAxis ? yAxisWidth + 1 : 0 }, /* @__PURE__ */ React6__default.default.createElement(Legend, { items: legendItems, position: "horizontal" })), /* @__PURE__ */ React6__default.default.createElement(ink.Box, { flexDirection: "row" }, showYAxis && yAxisConfig && /* @__PURE__ */ React6__default.default.createElement(ink.Box, { marginRight: 1, width: yAxisWidth }, /* @__PURE__ */ React6__default.default.createElement(Axis, { type: "y", length: plotHeight, ...yAxisConfig })), /* @__PURE__ */ React6__default.default.createElement(ink.Box, { flexDirection: "column" }, children), showLegend && legendPosition === "right" && /* @__PURE__ */ React6__default.default.createElement(ink.Box, { marginLeft: 2 }, /* @__PURE__ */ React6__default.default.createElement(Legend, { items: legendItems, position: "vertical" }))), showXAxis && xAxisConfig && /* @__PURE__ */ React6__default.default.createElement(ink.Box, { marginLeft: showYAxis ? yAxisWidth + 1 : 0 }, /* @__PURE__ */ React6__default.default.createElement(Axis, { type: "x", length: plotWidth, ...xAxisConfig })), showLegend && legendPosition === "bottom" && /* @__PURE__ */ React6__default.default.createElement(
2111
+ ink.Box,
2112
+ {
2113
+ marginTop: 1,
2114
+ marginLeft: showYAxis ? yAxisWidth + 1 : 0,
2115
+ width: plotWidth,
2116
+ justifyContent: "center"
2117
+ },
2118
+ /* @__PURE__ */ React6__default.default.createElement(Legend, { items: legendItems, position: "horizontal" })
2119
+ ));
1807
2120
  };
2121
+
2122
+ // src/symbols.ts
2123
+ var TREND = { up: "\u25B2", down: "\u25BC", neutral: "\u2500" };
2124
+ var LEGEND = { dot: "\u25CF" };
2125
+ var BAR = { fill: "\u2588", empty: "\u2591" };
2126
+ var HEATMAP = { default: "\u25A0" };
2127
+ var BORDER_ROUNDED = {
2128
+ topLeft: "\u256D",
2129
+ topRight: "\u256E",
2130
+ bottomLeft: "\u2570",
2131
+ bottomRight: "\u256F",
2132
+ horizontal: "\u2500",
2133
+ vertical: "\u2502",
2134
+ bar: "\u258C"
2135
+ };
2136
+ var SPARK_LEVELS = {
2137
+ block: [" ", "\u2582", "\u2583", "\u2584", "\u2585", "\u2586", "\u2587", "\u2588"],
2138
+ braille: ["\u2800", "\u2840", "\u28C0", "\u28C4", "\u28E4", "\u28E6", "\u28F6", "\u28F7", "\u28FF"]
2139
+ };
2140
+
2141
+ // src/components/common/useChartCore.ts
1808
2142
  function useChartCore(props) {
1809
2143
  const { series: seriesProp, data, seriesName, colors: colorsProp, colorPalette } = props;
1810
2144
  const series = React6.useMemo(
@@ -1820,7 +2154,7 @@ function useChartCore(props) {
1820
2154
  () => series.map((item, i) => ({
1821
2155
  name: item.name,
1822
2156
  color: item.color ?? colors[i] ?? "cyan",
1823
- symbol: "\u25CF"
2157
+ symbol: LEGEND.dot
1824
2158
  })),
1825
2159
  [series, colors]
1826
2160
  );
@@ -1833,17 +2167,9 @@ function useChartCore(props) {
1833
2167
  legendItems
1834
2168
  };
1835
2169
  }
1836
- var DEFAULT_RENDERER_CHAIN = ["braille", "block", "ascii"];
1837
- var BAR_CHART_RENDERER_CHAIN = ["block", "braille", "ascii"];
1838
- function useChartRenderer(props, defaultChain = DEFAULT_RENDERER_CHAIN) {
1839
- const { getRenderer, selectBest } = useInkHud();
1840
- const { renderer: preferredRenderer, rendererChain = defaultChain } = props;
1841
- return React6.useMemo(() => {
1842
- if (preferredRenderer) {
1843
- return getRenderer(preferredRenderer);
1844
- }
1845
- return selectBest(rendererChain);
1846
- }, [preferredRenderer, rendererChain, getRenderer, selectBest]);
2170
+ function useChartRenderer(kind) {
2171
+ const { getRendererFor } = useInkHud();
2172
+ return React6.useMemo(() => getRendererFor(kind), [kind, getRendererFor]);
1847
2173
  }
1848
2174
 
1849
2175
  // src/components/LineChart.tsx
@@ -1860,15 +2186,19 @@ var LineChart = (props) => {
1860
2186
  yTickCount = 5,
1861
2187
  xTickFormat,
1862
2188
  yTickFormat,
1863
- rendererChain = DEFAULT_RENDERER_CHAIN,
1864
2189
  xIntegerScale = true,
1865
2190
  yIntegerScale = false
1866
2191
  } = props;
1867
2192
  const renderXAxis = showXAxis ?? showAxis;
1868
2193
  const renderYAxis = showYAxis ?? showAxis;
1869
2194
  const { series, min, max, maxLength, colors, legendItems } = useChartCore(props);
1870
- const renderer = useChartRenderer(props, rendererChain);
1871
- const layout = useChartLayoutSimple(props, min, max);
2195
+ const renderer = useChartRenderer("line");
2196
+ const layout = useChartLayoutSimple(
2197
+ props,
2198
+ min,
2199
+ max,
2200
+ legendItems.map((it) => it.name)
2201
+ );
1872
2202
  const { plotWidth: canvasWidth, plotHeight: canvasHeight } = layout;
1873
2203
  const coloredLines = React6.useMemo(
1874
2204
  () => renderLineChartCanvas({
@@ -2040,15 +2370,19 @@ var AreaChart = (props) => {
2040
2370
  yTickCount = 5,
2041
2371
  xTickFormat,
2042
2372
  yTickFormat,
2043
- rendererChain = DEFAULT_RENDERER_CHAIN,
2044
2373
  xIntegerScale = true,
2045
2374
  yIntegerScale = false
2046
2375
  } = props;
2047
2376
  const renderXAxis = showXAxis ?? showAxis;
2048
2377
  const renderYAxis = showYAxis ?? showAxis;
2049
2378
  const { series, min, max, maxLength, colors, legendItems } = useChartCore(props);
2050
- const renderer = useChartRenderer(props, rendererChain);
2051
- const layout = useChartLayoutSimple(props, min, max);
2379
+ const renderer = useChartRenderer("area");
2380
+ const layout = useChartLayoutSimple(
2381
+ props,
2382
+ min,
2383
+ max,
2384
+ legendItems.map((it) => it.name)
2385
+ );
2052
2386
  const { plotWidth: canvasWidth, plotHeight: canvasHeight } = layout;
2053
2387
  const coloredLines = React6.useMemo(
2054
2388
  () => renderAreaChartCanvas({
@@ -2264,15 +2598,19 @@ var BarChart = (props) => {
2264
2598
  yTickCount = 5,
2265
2599
  xTickFormat,
2266
2600
  yTickFormat,
2267
- rendererChain = BAR_CHART_RENDERER_CHAIN,
2268
2601
  xIntegerScale,
2269
2602
  yIntegerScale
2270
2603
  } = props;
2271
2604
  const renderXAxis = showXAxis ?? showAxis;
2272
2605
  const renderYAxis = showYAxis ?? showAxis;
2273
2606
  const { series, min, max, maxLength, colors, legendItems } = useChartCore(props);
2274
- const renderer = useChartRenderer(props, rendererChain);
2275
- const layout = useChartLayoutSimple(props, min, max);
2607
+ const renderer = useChartRenderer("bar");
2608
+ const layout = useChartLayoutSimple(
2609
+ props,
2610
+ min,
2611
+ max,
2612
+ legendItems.map((it) => it.name)
2613
+ );
2276
2614
  const { plotWidth: canvasWidth, plotHeight: canvasHeight } = layout;
2277
2615
  const coloredLines = React6.useMemo(() => {
2278
2616
  if (series.length === 0 || maxLength === 0) return [];
@@ -2353,15 +2691,17 @@ function resolveDataItems(data, labels) {
2353
2691
  }
2354
2692
  var TWO_PI = Math.PI * 2;
2355
2693
  var START_ANGLE = -Math.PI / 2;
2356
- function resolveRadius(pixelWidth, pixelHeight, customRadius) {
2357
- const centerX = Math.floor(pixelWidth / 2);
2358
- const centerY = Math.floor(pixelHeight / 2);
2359
- const maxRadius = Math.max(
2360
- 0,
2361
- Math.min(Math.floor(pixelWidth / 2) - 1, Math.floor(pixelHeight / 2) - 1)
2362
- );
2363
- const radius = customRadius ?? maxRadius;
2364
- return { centerX, centerY, radius };
2694
+ function resolveRadius(canvasWChars, canvasHChars, renderer, customRadius, aspectRatioCorrection) {
2695
+ const { pixelWidth, pixelHeight } = getPixelDimensions(renderer, canvasWChars, canvasHChars);
2696
+ const { vertical } = renderer.getResolution();
2697
+ const centerX = (pixelWidth - 1) / 2;
2698
+ const centerY = (pixelHeight - 1) / 2;
2699
+ const maxRadiusByWidth = Math.min(centerX, pixelWidth - 1 - centerX);
2700
+ const verticalInsetPixels = Math.ceil(vertical / 2);
2701
+ const maxRadiusByHeight = Math.max(0, Math.min(centerY, pixelHeight - 1 - centerY) - verticalInsetPixels) * aspectRatioCorrection;
2702
+ const maxRadius = Math.max(0, Math.min(maxRadiusByWidth, maxRadiusByHeight));
2703
+ const radius = customRadius !== void 0 ? Math.min(Math.max(0, customRadius), maxRadius) : maxRadius;
2704
+ return { pixelWidth, pixelHeight, centerX, centerY, radius };
2365
2705
  }
2366
2706
  function buildAngleStops(data, total) {
2367
2707
  if (total <= 0) {
@@ -2401,34 +2741,35 @@ var PieChart = ({
2401
2741
  showLegend = true,
2402
2742
  legendPosition = "right",
2403
2743
  colors,
2404
- colorPalette,
2405
- renderer: preferredRenderer,
2406
- rendererChain = ["braille", "block", "ascii"],
2407
- heightOffset = 0,
2408
- widthOffset = 0
2744
+ colorPalette
2409
2745
  }) => {
2410
2746
  const data = React6.useMemo(() => resolveDataItems(dataProp, labels), [dataProp, labels]);
2747
+ const percentagesEarly = React6.useMemo(() => {
2748
+ const sum = data.reduce((acc, item) => acc + item.value, 0);
2749
+ return data.map((item) => sum > 0 ? item.value / sum * 100 : 0);
2750
+ }, [data]);
2751
+ const legendNamesForLayout = React6.useMemo(() => {
2752
+ if (!showLegend) return void 0;
2753
+ return data.map(
2754
+ (item, i) => showLabels ? `${item.name} (${percentagesEarly[i]?.toFixed(1)}%)` : item.name
2755
+ );
2756
+ }, [data, showLegend, showLabels, percentagesEarly]);
2411
2757
  const layout = useChartLayoutSimple(
2412
2758
  {
2413
2759
  ...width !== void 0 && { width },
2414
2760
  ...height !== void 0 && { height },
2415
2761
  showAxis: false,
2416
2762
  showLegend,
2417
- legendPosition,
2418
- widthOffset,
2419
- heightOffset
2763
+ legendPosition
2420
2764
  },
2421
2765
  0,
2422
- 0
2766
+ 0,
2767
+ legendNamesForLayout
2423
2768
  );
2424
- const { totalWidth, plotWidth: canvasWidth, plotHeight: canvasHeight } = layout;
2425
- const { getRenderer, selectBest } = useInkHud();
2426
- const renderer = React6.useMemo(() => {
2427
- if (preferredRenderer) {
2428
- return getRenderer(preferredRenderer);
2429
- }
2430
- return selectBest(rendererChain);
2431
- }, [preferredRenderer, rendererChain, getRenderer, selectBest]);
2769
+ const { totalWidth, plotWidth: plotW, plotHeight: plotH } = layout;
2770
+ const canvasHeight = Math.max(1, Math.min(plotH, Math.floor(plotW / 2)));
2771
+ const canvasWidth = canvasHeight * 2;
2772
+ const renderer = useChartRenderer("pie");
2432
2773
  const ratio = React6.useMemo(() => {
2433
2774
  if (aspectRatio !== void 0) return aspectRatio;
2434
2775
  const resolution = renderer.getResolution();
@@ -2443,22 +2784,20 @@ var PieChart = ({
2443
2784
  }
2444
2785
  return assignColors(data.length, colorPalette);
2445
2786
  }, [data.length, colors, colorPalette]);
2446
- const { total, percentages } = React6.useMemo(() => {
2447
- const sum = data.reduce((acc, item) => acc + item.value, 0);
2448
- const pcts = data.map((item) => sum > 0 ? item.value / sum * 100 : 0);
2449
- return { total: sum, percentages: pcts };
2450
- }, [data]);
2787
+ const total = React6.useMemo(() => data.reduce((acc, item) => acc + item.value, 0), [data]);
2788
+ const percentages = percentagesEarly;
2451
2789
  const coloredLines = React6.useMemo(() => {
2452
2790
  if (data.length === 0 || total <= 0) {
2453
2791
  return [];
2454
2792
  }
2455
- const { pixelWidth, pixelHeight } = getPixelDimensions(renderer, canvasWidth, canvasHeight);
2456
- const canvas = renderer.createCanvas(pixelWidth, pixelHeight);
2457
2793
  const {
2794
+ pixelWidth,
2795
+ pixelHeight,
2458
2796
  centerX,
2459
2797
  centerY,
2460
2798
  radius: outerRadius
2461
- } = resolveRadius(pixelWidth, pixelHeight, customRadius);
2799
+ } = resolveRadius(canvasWidth, canvasHeight, renderer, customRadius, ratio);
2800
+ const canvas = renderer.createCanvas(pixelWidth, pixelHeight);
2462
2801
  const innerRadius = outerRadius * donutRatio;
2463
2802
  const angleStops = buildAngleStops(data, total);
2464
2803
  for (let y = 0; y < pixelHeight; y++) {
@@ -2495,7 +2834,7 @@ var PieChart = ({
2495
2834
  return data.map((item, i) => ({
2496
2835
  name: showLabels ? `${item.name} (${percentages[i]?.toFixed(1)}%)` : item.name,
2497
2836
  color: item.color ?? itemColors[i] ?? "cyan",
2498
- symbol: "\u25CF"
2837
+ symbol: LEGEND.dot
2499
2838
  }));
2500
2839
  }, [data, itemColors, showLabels, percentages]);
2501
2840
  if (coloredLines.length === 0) {
@@ -2509,40 +2848,203 @@ var PieChart = ({
2509
2848
  ...segment.backgroundColor ? { backgroundColor: segment.backgroundColor } : {}
2510
2849
  },
2511
2850
  segment.text
2512
- ))))), showLegend && legendPosition === "right" && /* @__PURE__ */ React6__default.default.createElement(ink.Box, { marginLeft: 2 }, /* @__PURE__ */ React6__default.default.createElement(Legend, { items: legendItems, position: "vertical" }))), showLegend && legendPosition === "bottom" && /* @__PURE__ */ React6__default.default.createElement(ink.Box, { marginTop: 1 }, /* @__PURE__ */ React6__default.default.createElement(Legend, { items: legendItems, position: "horizontal" })));
2851
+ ))))), showLegend && legendPosition === "right" && /* @__PURE__ */ React6__default.default.createElement(ink.Box, { marginLeft: 2 }, /* @__PURE__ */ React6__default.default.createElement(Legend, { items: legendItems, position: "vertical" }))), showLegend && legendPosition === "bottom" && /* @__PURE__ */ React6__default.default.createElement(ink.Box, { marginTop: 1, width: totalWidth, justifyContent: "center" }, /* @__PURE__ */ React6__default.default.createElement(Legend, { items: legendItems, position: "horizontal" })));
2513
2852
  };
2514
- var SPARK_LEVELS_BLOCK = [" ", "\u2582", "\u2583", "\u2584", "\u2585", "\u2586", "\u2587", "\u2588"];
2515
- var SPARK_LEVELS_BRAILLE = ["\u2800", "\u2840", "\u28C0", "\u28C4", "\u28E4", "\u28E6", "\u28F6", "\u28F7", "\u28FF"];
2516
- var SPARK_LEVELS_ASCII = [" ", ".", ":", "-", "=", "+", "*", "#", "%", "@"];
2853
+ var CRC_TABLE = (() => {
2854
+ const table = new Uint32Array(256);
2855
+ for (let i = 0; i < 256; i++) {
2856
+ let c = i;
2857
+ for (let k = 0; k < 8; k++) {
2858
+ c = c & 1 ? 3988292384 ^ c >>> 1 : c >>> 1;
2859
+ }
2860
+ table[i] = c;
2861
+ }
2862
+ return table;
2863
+ })();
2864
+ function crc32(buf) {
2865
+ let crc = 4294967295;
2866
+ for (let i = 0; i < buf.length; i++) {
2867
+ crc = (CRC_TABLE[(crc ^ buf[i]) & 255] ?? 0) ^ crc >>> 8;
2868
+ }
2869
+ return (crc ^ 4294967295) >>> 0;
2870
+ }
2871
+ function chunk(type, data) {
2872
+ const len = Buffer.alloc(4);
2873
+ len.writeUInt32BE(data.length, 0);
2874
+ const typeBytes = Buffer.from(type, "ascii");
2875
+ const crcBytes = Buffer.alloc(4);
2876
+ crcBytes.writeUInt32BE(crc32(Buffer.concat([typeBytes, data])), 0);
2877
+ return Buffer.concat([len, typeBytes, data, crcBytes]);
2878
+ }
2879
+ function createRgbPng(pixels) {
2880
+ const height = pixels.length;
2881
+ const width = pixels[0]?.length ?? 0;
2882
+ const ihdrData = Buffer.alloc(13);
2883
+ ihdrData.writeUInt32BE(width, 0);
2884
+ ihdrData.writeUInt32BE(height, 4);
2885
+ ihdrData[8] = 8;
2886
+ ihdrData[9] = 2;
2887
+ const rawRows = [];
2888
+ for (const row of pixels) {
2889
+ const rowBuf = Buffer.alloc(1 + width * 3);
2890
+ rowBuf[0] = 0;
2891
+ let offset = 1;
2892
+ for (const [r, g, b] of row) {
2893
+ rowBuf[offset++] = r;
2894
+ rowBuf[offset++] = g;
2895
+ rowBuf[offset++] = b;
2896
+ }
2897
+ rawRows.push(rowBuf);
2898
+ }
2899
+ const compressed = zlib.deflateSync(Buffer.concat(rawRows), { level: 1 });
2900
+ const PNG_SIGNATURE = Buffer.from([137, 80, 78, 71, 13, 10, 26, 10]);
2901
+ return Buffer.concat([
2902
+ PNG_SIGNATURE,
2903
+ chunk("IHDR", ihdrData),
2904
+ chunk("IDAT", compressed),
2905
+ chunk("IEND", Buffer.alloc(0))
2906
+ ]);
2907
+ }
2908
+ function hexToRgb(hex) {
2909
+ const h = hex.startsWith("#") ? hex.slice(1) : hex;
2910
+ const n = Number.parseInt(h, 16);
2911
+ return [n >> 16 & 255, n >> 8 & 255, n & 255];
2912
+ }
2913
+
2914
+ // src/render/image/drawing.ts
2915
+ function clamp2(v, lo, hi) {
2916
+ return v < lo ? lo : v > hi ? hi : v;
2917
+ }
2918
+ function darken(rgb, factor) {
2919
+ return [Math.round(rgb[0] * factor), Math.round(rgb[1] * factor), Math.round(rgb[2] * factor)];
2920
+ }
2921
+ function gradientColorFn(colors) {
2922
+ if (colors.length === 0) return () => [128, 128, 128];
2923
+ if (colors.length === 1) {
2924
+ const rgb = hexToRgb(colors[0] ?? "");
2925
+ return () => rgb;
2926
+ }
2927
+ const g = tinygradient__default.default(colors);
2928
+ const palette = g.rgb(256).map((c) => hexToRgb(`#${c.toHex()}`));
2929
+ return (normalized) => {
2930
+ const idx = clamp2(Math.floor(normalized * 256), 0, 255);
2931
+ return palette[idx] ?? [128, 128, 128];
2932
+ };
2933
+ }
2934
+ function buildSparklinePixelGrid(data, widthPx, heightPx, min, max, colorFn, bgColor = [10, 10, 14]) {
2935
+ const grid = Array.from(
2936
+ { length: heightPx },
2937
+ () => Array.from({ length: widthPx }, () => [...bgColor])
2938
+ );
2939
+ if (data.length === 0 || widthPx === 0 || heightPx === 0) return grid;
2940
+ const range = max === min ? 1 : max - min;
2941
+ for (let x = 0; x < widthPx; x++) {
2942
+ const t = data.length === 1 ? 0 : x / Math.max(widthPx - 1, 1) * (data.length - 1);
2943
+ const baseIdx = clamp2(Math.floor(t), 0, data.length - 2);
2944
+ const frac = data.length === 1 ? 0 : t - baseIdx;
2945
+ const raw = data.length === 1 ? data[0] ?? 0 : (data[baseIdx] ?? 0) * (1 - frac) + (data[baseIdx + 1] ?? 0) * frac;
2946
+ const normalized = clamp2((raw - min) / range, 0, 1);
2947
+ const yLine = Math.round((1 - normalized) * (heightPx - 1));
2948
+ const lineRgb = colorFn(normalized);
2949
+ const fillRgb = darken(lineRgb, 0.28);
2950
+ for (let y = yLine + 2; y < heightPx; y++) {
2951
+ const row = grid[y];
2952
+ if (row !== void 0) row[x] = fillRgb;
2953
+ }
2954
+ if (yLine + 1 < heightPx) {
2955
+ const row = grid[yLine + 1];
2956
+ if (row !== void 0) row[x] = darken(lineRgb, 0.55);
2957
+ }
2958
+ const lineRow = grid[yLine];
2959
+ if (lineRow !== void 0) lineRow[x] = lineRgb;
2960
+ if (yLine - 1 >= 0) {
2961
+ const capRow = grid[yLine - 1];
2962
+ if (capRow !== void 0) capRow[x] = darken(lineRgb, 0.75);
2963
+ }
2964
+ }
2965
+ return grid;
2966
+ }
2967
+
2968
+ // src/components/Sparkline.tsx
2517
2969
  var Sparkline = ({
2518
2970
  data,
2519
2971
  width: propsWidth,
2520
2972
  min: userMin,
2521
2973
  max: userMax,
2522
2974
  color,
2523
- variant = "block"
2975
+ variant = "block",
2976
+ mode = "auto",
2977
+ height = 1,
2978
+ colors,
2979
+ cellPx = 8
2524
2980
  }) => {
2525
2981
  const gridContext = React6.useContext(GridItemContext);
2982
+ const theme = useTheme();
2526
2983
  const effectiveWidth = propsWidth ?? gridContext?.width;
2527
- const text = React6.useMemo(() => {
2528
- if (!data || data.length === 0) return "";
2529
- let processedData = data;
2984
+ const { processedData, min, max } = React6.useMemo(() => {
2985
+ if (!data || data.length === 0) return { processedData: [], min: 0, max: 1 };
2986
+ let processed = data;
2530
2987
  if (effectiveWidth && data.length > effectiveWidth) {
2531
- processedData = lttb(data, effectiveWidth);
2532
- }
2533
- const min = userMin ?? Math.min(...processedData);
2534
- let max = userMax ?? Math.max(...processedData);
2535
- if (max === min) {
2536
- max = min + 1;
2537
- }
2538
- const levels = variant === "braille" ? SPARK_LEVELS_BRAILLE : variant === "ascii" ? SPARK_LEVELS_ASCII : SPARK_LEVELS_BLOCK;
2539
- return processedData.map((v) => {
2540
- const value = Math.max(min, Math.min(max, v));
2541
- const normalized = (value - min) / (max - min);
2542
- const index = Math.round(normalized * (levels.length - 1));
2543
- return levels[index];
2544
- }).join("");
2545
- }, [data, effectiveWidth, userMin, userMax, variant]);
2988
+ processed = lttb(data, effectiveWidth);
2989
+ }
2990
+ const lo = userMin ?? Math.min(...processed);
2991
+ let hi = userMax ?? Math.max(...processed);
2992
+ if (hi === lo) hi = lo + 1;
2993
+ return { processedData: processed, min: lo, max: hi };
2994
+ }, [data, effectiveWidth, userMin, userMax]);
2995
+ const charCols = effectiveWidth ?? processedData.length;
2996
+ const charRows = height;
2997
+ const pngBuf = React6.useMemo(() => {
2998
+ if (mode === "character" || charCols === 0 || processedData.length === 0) return null;
2999
+ const effectiveColors = colors ?? (color ? [color] : theme.heatmapGradient);
3000
+ const colorFn = gradientColorFn(effectiveColors);
3001
+ const pixelGrid = buildSparklinePixelGrid(
3002
+ processedData,
3003
+ charCols * cellPx,
3004
+ charRows * cellPx,
3005
+ min,
3006
+ max,
3007
+ colorFn
3008
+ );
3009
+ return createRgbPng(pixelGrid);
3010
+ }, [
3011
+ mode,
3012
+ charCols,
3013
+ charRows,
3014
+ cellPx,
3015
+ colors,
3016
+ color,
3017
+ theme.heatmapGradient,
3018
+ processedData,
3019
+ min,
3020
+ max
3021
+ ]);
3022
+ const { kittyLines, iterm2Cols } = useImageProtocol({
3023
+ mode,
3024
+ charCols,
3025
+ charRows,
3026
+ pngBuf,
3027
+ trailingSpace: false
3028
+ });
3029
+ if (kittyLines !== null) {
3030
+ return /* @__PURE__ */ React6__default.default.createElement(ink.Box, { flexDirection: "column" }, kittyLines.map((line, i) => /* @__PURE__ */ React6__default.default.createElement(ink.Text, { key: i }, line)));
3031
+ }
3032
+ if (iterm2Cols !== null) {
3033
+ return /* @__PURE__ */ React6__default.default.createElement(ink.Box, { flexDirection: "column" }, Array.from({ length: charRows }, (_, i) => /* @__PURE__ */ React6__default.default.createElement(ink.Text, { key: i }, " ".repeat(iterm2Cols))));
3034
+ }
3035
+ if (processedData.length === 0) {
3036
+ return /* @__PURE__ */ React6__default.default.createElement(ink.Text, null, "");
3037
+ }
3038
+ const levels = SPARK_LEVELS[variant];
3039
+ const outputCols = charCols > 0 ? charCols : processedData.length;
3040
+ const text = Array.from({ length: outputCols }, (_, i) => {
3041
+ const dataIndex = Math.floor(i * processedData.length / outputCols);
3042
+ const v = processedData[dataIndex] ?? 0;
3043
+ const value = Math.max(min, Math.min(max, v));
3044
+ const normalized = (value - min) / (max - min);
3045
+ const index = Math.round(normalized * (levels.length - 1));
3046
+ return levels[index];
3047
+ }).join("");
2546
3048
  return /* @__PURE__ */ React6__default.default.createElement(ink.Text, { ...color ? { color } : {} }, text);
2547
3049
  };
2548
3050
  var Panel = ({
@@ -2591,10 +3093,6 @@ var Panel = ({
2591
3093
  /* @__PURE__ */ React6__default.default.createElement(ink.Text, { bold: true, color: borderColor || "white" }, " ", title, " ")
2592
3094
  ), /* @__PURE__ */ React6__default.default.createElement(ink.Box, { padding, flexDirection: "column", flexGrow: 1 }, children)));
2593
3095
  };
2594
- var CHAR_SETS = {
2595
- unicode: { fill: "\u2588", empty: "\u2591" },
2596
- ascii: { fill: "#", empty: "-" }
2597
- };
2598
3096
  var Gauge = ({
2599
3097
  value,
2600
3098
  min = 0,
@@ -2603,7 +3101,6 @@ var Gauge = ({
2603
3101
  color,
2604
3102
  emptyColor,
2605
3103
  showPercent = true,
2606
- variant = "unicode",
2607
3104
  fillChar,
2608
3105
  emptyChar,
2609
3106
  label
@@ -2611,9 +3108,8 @@ var Gauge = ({
2611
3108
  const theme = useTheme();
2612
3109
  const effectiveColor = color ?? theme.semantic.success;
2613
3110
  const effectiveEmptyColor = emptyColor ?? theme.semantic.muted;
2614
- const charSet = CHAR_SETS[variant];
2615
- const effectiveFillChar = fillChar ?? charSet.fill;
2616
- const effectiveEmptyChar = emptyChar ?? charSet.empty;
3111
+ const effectiveFillChar = fillChar ?? BAR.fill;
3112
+ const effectiveEmptyChar = emptyChar ?? BAR.empty;
2617
3113
  const clampedValue = Math.min(Math.max(value, min), max);
2618
3114
  const range = max - min;
2619
3115
  const ratio = range === 0 ? 0 : (clampedValue - min) / range;
@@ -2660,35 +3156,31 @@ var BRAILLE_FONT = {
2660
3156
  "+": ["\u2800\u2800", "\u2810\u2812", "\u2800\u2800"],
2661
3157
  "-": ["\u2800\u2800", "\u2810\u2812", "\u2800\u2800"]
2662
3158
  };
2663
- var ASCII_FONT = {
2664
- "0": ["+~+", "| |", "+~+"],
2665
- "1": [" | ", " | ", " | "],
2666
- "2": ["~~+", "+-+", "+~~"],
2667
- "3": ["~~+", " ~+", "~~+"],
2668
- "4": ["+ +", "+-+", " +"],
2669
- "5": ["+~~", "+~+", "~~+"],
2670
- "6": ["+~~", "+~+", "+~+"],
2671
- "7": ["~~+", " +", " +"],
2672
- "8": ["+~+", "+~+", "+~+"],
2673
- "9": ["+~+", "+~+", " +"],
2674
- ".": [" ", " ", " . "],
2675
- ",": [" ", " ", " , "],
2676
- "%": ["* ", " * ", " *"],
2677
- "+": [" ", " + ", " "],
2678
- "-": [" ", " - ", " "]
2679
- };
2680
3159
  var FONTS = {
2681
3160
  block: BLOCK_FONT,
2682
- braille: BRAILLE_FONT,
2683
- ascii: ASCII_FONT
3161
+ braille: BRAILLE_FONT
2684
3162
  };
2685
3163
  var UNKNOWN = {
2686
3164
  block: [" ", " ? ", " "],
2687
- braille: ["\u2800\u2800", "\u2800\u2826", "\u2800\u2800"],
2688
- ascii: [" ", " ? ", " "]
3165
+ braille: ["\u2800\u2800", "\u2800\u2826", "\u2800\u2800"]
2689
3166
  };
3167
+ var warnedChars = /* @__PURE__ */ new Set();
3168
+ function warnUnsupportedChar(char, style) {
3169
+ if (process.env.NODE_ENV === "production") return;
3170
+ const key = `${style}:${char}`;
3171
+ if (warnedChars.has(key)) return;
3172
+ warnedChars.add(key);
3173
+ console.warn(
3174
+ `[ink-hud] BigNumber: unsupported char "${char}" (style="${style}") rendered as "?". Use the prefix/suffix props for units or symbols (e.g. <BigNumber value="49" suffix="ms" />).`
3175
+ );
3176
+ }
2690
3177
  function getBigChar(char, style = "block") {
2691
- return FONTS[style][char] || UNKNOWN[style];
3178
+ const glyphs = FONTS[style][char];
3179
+ if (!glyphs) {
3180
+ warnUnsupportedChar(char, style);
3181
+ return UNKNOWN[style];
3182
+ }
3183
+ return glyphs;
2692
3184
  }
2693
3185
  function renderBigString(text, style = "block") {
2694
3186
  const rows = ["", "", ""];
@@ -2702,42 +3194,34 @@ function renderBigString(text, style = "block") {
2702
3194
  }
2703
3195
 
2704
3196
  // src/components/BigNumber.tsx
2705
- var TREND_ARROWS = {
2706
- unicode: { up: "\u25B2", down: "\u25BC", neutral: "\u2500" },
2707
- ascii: { up: "^", down: "v", neutral: "-" }
2708
- };
2709
3197
  var BigNumber = ({
2710
3198
  value,
3199
+ prefix,
3200
+ suffix,
2711
3201
  label,
2712
3202
  color = "white",
2713
3203
  trendDirection,
2714
3204
  trendLabel,
2715
- variant = "unicode",
2716
3205
  fontStyle = "block",
2717
3206
  align = "center"
2718
3207
  }) => {
2719
3208
  const bigLines = React6.useMemo(() => renderBigString(String(value), fontStyle), [value, fontStyle]);
2720
3209
  const theme = useTheme();
2721
- const arrows = TREND_ARROWS[variant];
2722
3210
  let trendColor = theme.semantic.muted;
2723
3211
  let trendArrow = "";
2724
3212
  if (trendDirection === "up") {
2725
3213
  trendColor = theme.semantic.success;
2726
- trendArrow = arrows.up;
3214
+ trendArrow = TREND.up;
2727
3215
  } else if (trendDirection === "down") {
2728
3216
  trendColor = theme.semantic.error;
2729
- trendArrow = arrows.down;
3217
+ trendArrow = TREND.down;
2730
3218
  } else if (trendDirection === "neutral") {
2731
- trendArrow = arrows.neutral;
3219
+ trendArrow = TREND.neutral;
2732
3220
  }
2733
3221
  const alignItems = align === "center" ? "center" : align === "right" ? "flex-end" : "flex-start";
2734
- return /* @__PURE__ */ React6__default.default.createElement(ink.Box, { flexDirection: "column", alignItems }, /* @__PURE__ */ React6__default.default.createElement(ink.Box, { flexDirection: "column", marginBottom: 1 }, bigLines.map((line, i) => /* @__PURE__ */ React6__default.default.createElement(ink.Text, { key: i, color }, line))), /* @__PURE__ */ React6__default.default.createElement(ink.Box, { flexDirection: "row", gap: 1 }, label && /* @__PURE__ */ React6__default.default.createElement(ink.Text, { color: theme.semantic.textSecondary }, label), (trendLabel || trendArrow) && /* @__PURE__ */ React6__default.default.createElement(ink.Text, { color: trendColor }, trendArrow, " ", trendLabel)));
2735
- };
2736
- var CHAR_SETS2 = {
2737
- unicode: "\u25A0",
2738
- ascii: "#"
3222
+ return /* @__PURE__ */ React6__default.default.createElement(ink.Box, { flexDirection: "column", alignItems }, /* @__PURE__ */ React6__default.default.createElement(ink.Box, { flexDirection: "row", alignItems: "flex-end", marginBottom: 1 }, prefix && /* @__PURE__ */ React6__default.default.createElement(ink.Box, { marginRight: 1 }, /* @__PURE__ */ React6__default.default.createElement(ink.Text, { color }, prefix)), /* @__PURE__ */ React6__default.default.createElement(ink.Box, { flexDirection: "column" }, bigLines.map((line, i) => /* @__PURE__ */ React6__default.default.createElement(ink.Text, { key: i, color }, line))), suffix && /* @__PURE__ */ React6__default.default.createElement(ink.Box, { marginLeft: 1 }, /* @__PURE__ */ React6__default.default.createElement(ink.Text, { color }, suffix))), /* @__PURE__ */ React6__default.default.createElement(ink.Box, { flexDirection: "row", gap: 1 }, label && /* @__PURE__ */ React6__default.default.createElement(ink.Text, { color: theme.semantic.textSecondary }, label), (trendLabel || trendArrow) && /* @__PURE__ */ React6__default.default.createElement(ink.Text, { color: trendColor }, trendArrow, " ", trendLabel)));
2739
3223
  };
2740
- var findMinMax = (data) => {
3224
+ function findMinMax(data) {
2741
3225
  let minVal = Number.POSITIVE_INFINITY;
2742
3226
  let maxVal = Number.NEGATIVE_INFINITY;
2743
3227
  for (const row of data) {
@@ -2747,21 +3231,71 @@ var findMinMax = (data) => {
2747
3231
  }
2748
3232
  }
2749
3233
  return { min: minVal, max: maxVal };
2750
- };
2751
- var Heatmap = ({ data, colors, variant = "unicode", char }) => {
3234
+ }
3235
+ function gradientRgbPalette(colors, steps) {
3236
+ if (steps === 0) return [];
3237
+ if (colors.length === 0) return Array(steps).fill([128, 128, 128]);
3238
+ if (colors.length === 1) {
3239
+ const rgb = hexToRgb(colors[0] ?? "#808080");
3240
+ return Array(steps).fill(rgb);
3241
+ }
3242
+ const g = tinygradient__default.default(colors);
3243
+ return g.rgb(steps).map((c) => hexToRgb(`#${c.toHex()}`));
3244
+ }
3245
+ var Heatmap = ({
3246
+ data,
3247
+ colors,
3248
+ char,
3249
+ mode = "auto",
3250
+ cellPx = 8
3251
+ }) => {
2752
3252
  const theme = useTheme();
2753
3253
  const effectiveColors = colors ?? theme.heatmapGradient;
2754
- const effectiveChar = char ?? CHAR_SETS2[variant];
3254
+ const effectiveChar = char ?? HEATMAP.default;
3255
+ const dataRows = data.length;
3256
+ const dataCols = data[0]?.length ?? 0;
2755
3257
  const { min, max } = React6.useMemo(() => {
2756
3258
  const { min: minVal, max: maxVal } = findMinMax(data);
2757
3259
  if (minVal === Number.POSITIVE_INFINITY) return { min: 0, max: 0 };
2758
3260
  return { min: minVal, max: maxVal > minVal ? maxVal : minVal + 1 };
2759
3261
  }, [data]);
2760
3262
  const steps = effectiveColors.length;
2761
- const gradient = React6.useMemo(
2762
- () => createGradient(effectiveColors, steps),
2763
- [effectiveColors, steps]
2764
- );
3263
+ const pngBuf = React6.useMemo(() => {
3264
+ if (mode === "character" || !dataRows || !dataCols) return null;
3265
+ const palette = gradientRgbPalette(effectiveColors, steps);
3266
+ const pixelGrid = [];
3267
+ const GAP = [16, 16, 16];
3268
+ for (let row = 0; row < dataRows; row++) {
3269
+ for (let py = 0; py < cellPx; py++) {
3270
+ const pixelRow = [];
3271
+ for (let col = 0; col < dataCols; col++) {
3272
+ const val = data[row]?.[col] ?? 0;
3273
+ const normalized = max === min ? 0 : (val - min) / (max - min);
3274
+ let si = Math.floor(normalized * steps);
3275
+ if (si >= steps) si = steps - 1;
3276
+ const rgb = palette[si] ?? GAP;
3277
+ for (let px = 0; px < cellPx; px++) pixelRow.push(rgb);
3278
+ for (let px = 0; px < cellPx; px++) pixelRow.push(GAP);
3279
+ }
3280
+ pixelGrid.push(pixelRow);
3281
+ }
3282
+ }
3283
+ return createRgbPng(pixelGrid);
3284
+ }, [data, min, max, steps, effectiveColors, cellPx, mode, dataRows, dataCols]);
3285
+ const { kittyLines, iterm2Cols } = useImageProtocol({
3286
+ mode,
3287
+ charCols: dataCols,
3288
+ charRows: dataRows,
3289
+ pngBuf,
3290
+ trailingSpace: true
3291
+ });
3292
+ if (kittyLines !== null) {
3293
+ return /* @__PURE__ */ React6__default.default.createElement(ink.Box, { flexDirection: "column" }, kittyLines.map((line, i) => /* @__PURE__ */ React6__default.default.createElement(ink.Text, { key: i }, line)));
3294
+ }
3295
+ if (iterm2Cols !== null) {
3296
+ return /* @__PURE__ */ React6__default.default.createElement(ink.Box, { flexDirection: "column" }, data.map((_, i) => /* @__PURE__ */ React6__default.default.createElement(ink.Text, { key: i }, " ".repeat(iterm2Cols))));
3297
+ }
3298
+ const gradient = createGradient(effectiveColors, steps);
2765
3299
  return /* @__PURE__ */ React6__default.default.createElement(ink.Box, { flexDirection: "column" }, data.map((row, rowIndex) => /* @__PURE__ */ React6__default.default.createElement(ink.Box, { key: rowIndex, flexDirection: "row" }, row.map((val, colIndex) => {
2766
3300
  const normalized = max === min ? 0 : (val - min) / (max - min);
2767
3301
  let stepIndex = Math.floor(normalized * steps);
@@ -2875,7 +3409,7 @@ var SortableHeaderCell = ({
2875
3409
  });
2876
3410
  let indicator = "";
2877
3411
  if (isSorted) {
2878
- indicator = sortDirection === "asc" ? " \u25B2" : " \u25BC";
3412
+ indicator = sortDirection === "asc" ? ` ${TREND.up}` : ` ${TREND.down}`;
2879
3413
  }
2880
3414
  return /* @__PURE__ */ React6__default.default.createElement(
2881
3415
  ink.Box,
@@ -3015,34 +3549,8 @@ var Table = ({
3015
3549
  })
3016
3550
  );
3017
3551
  };
3018
- var CHAR_SETS3 = {
3019
- unicode: {
3020
- bar: "\u258C",
3021
- left: "\u256D",
3022
- right: "\u256E",
3023
- leftBottom: "\u2570",
3024
- rightBottom: "\u256F",
3025
- horizontal: "\u2500",
3026
- vertical: "\u2502"
3027
- },
3028
- ascii: {
3029
- bar: "|",
3030
- left: "/",
3031
- right: "\\",
3032
- leftBottom: "\\",
3033
- rightBottom: "/",
3034
- horizontal: "-",
3035
- vertical: "|"
3036
- }
3037
- };
3038
- var PulseBar = ({
3039
- records,
3040
- maxBars = 30,
3041
- variant = "unicode",
3042
- colors
3043
- }) => {
3552
+ var PulseBar = ({ records = [], maxBars = 30, colors }) => {
3044
3553
  const theme = useTheme();
3045
- const chars = CHAR_SETS3[variant];
3046
3554
  const getColor = (status) => {
3047
3555
  switch (status) {
3048
3556
  case "good":
@@ -3056,23 +3564,27 @@ var PulseBar = ({
3056
3564
  const displayRecords = records.slice(-maxBars);
3057
3565
  const paddingCount = maxBars - displayRecords.length;
3058
3566
  const borderColor = theme.semantic.muted;
3059
- const topBorder = chars.left + chars.horizontal.repeat(maxBars) + chars.right;
3060
- const bottomBorder = chars.leftBottom + chars.horizontal.repeat(maxBars) + chars.rightBottom;
3061
- return /* @__PURE__ */ React6__default.default.createElement(ink.Box, { flexDirection: "column" }, /* @__PURE__ */ React6__default.default.createElement(ink.Text, { color: borderColor }, topBorder), /* @__PURE__ */ React6__default.default.createElement(ink.Box, { flexDirection: "row" }, /* @__PURE__ */ React6__default.default.createElement(ink.Text, { color: borderColor }, chars.vertical), paddingCount > 0 && /* @__PURE__ */ React6__default.default.createElement(ink.Text, { color: borderColor }, chars.bar.repeat(paddingCount)), displayRecords.map((record, index) => /* @__PURE__ */ React6__default.default.createElement(ink.Text, { key: index, color: getColor(record.status) }, chars.bar)), /* @__PURE__ */ React6__default.default.createElement(ink.Text, { color: borderColor }, chars.vertical)), /* @__PURE__ */ React6__default.default.createElement(ink.Text, { color: borderColor }, bottomBorder));
3567
+ const topBorder = BORDER_ROUNDED.topLeft + BORDER_ROUNDED.horizontal.repeat(maxBars) + BORDER_ROUNDED.topRight;
3568
+ const bottomBorder = BORDER_ROUNDED.bottomLeft + BORDER_ROUNDED.horizontal.repeat(maxBars) + BORDER_ROUNDED.bottomRight;
3569
+ return /* @__PURE__ */ React6__default.default.createElement(ink.Box, { flexDirection: "column" }, /* @__PURE__ */ React6__default.default.createElement(ink.Text, { color: borderColor }, topBorder), /* @__PURE__ */ React6__default.default.createElement(ink.Box, { flexDirection: "row" }, /* @__PURE__ */ React6__default.default.createElement(ink.Text, { color: borderColor }, BORDER_ROUNDED.vertical), paddingCount > 0 && /* @__PURE__ */ React6__default.default.createElement(ink.Text, { color: borderColor }, BORDER_ROUNDED.bar.repeat(paddingCount)), displayRecords.map((record, index) => /* @__PURE__ */ React6__default.default.createElement(ink.Text, { key: index, color: getColor(record.status) }, BORDER_ROUNDED.bar)), /* @__PURE__ */ React6__default.default.createElement(ink.Text, { color: borderColor }, BORDER_ROUNDED.vertical)), /* @__PURE__ */ React6__default.default.createElement(ink.Text, { color: borderColor }, bottomBorder));
3062
3570
  };
3063
3571
 
3064
3572
  exports.AreaChart = AreaChart;
3065
- exports.AsciiRenderer = AsciiRenderer;
3066
3573
  exports.Axis = Axis;
3574
+ exports.BAR = BAR;
3575
+ exports.BORDER_ROUNDED = BORDER_ROUNDED;
3067
3576
  exports.BarChart = BarChart;
3068
3577
  exports.BigNumber = BigNumber;
3069
3578
  exports.BlockRenderer = BlockRenderer;
3070
3579
  exports.BrailleRenderer = BrailleRenderer;
3580
+ exports.DEFAULT_CHART_RENDERERS = DEFAULT_CHART_RENDERERS;
3071
3581
  exports.Gauge = Gauge;
3072
3582
  exports.Grid = Grid;
3073
3583
  exports.GridItem = GridItem;
3584
+ exports.HEATMAP = HEATMAP;
3074
3585
  exports.Heatmap = Heatmap;
3075
3586
  exports.InkHudProvider = InkHudProvider;
3587
+ exports.LEGEND = LEGEND;
3076
3588
  exports.Legend = Legend;
3077
3589
  exports.LineChart = LineChart;
3078
3590
  exports.LogStream = LogStream;
@@ -3082,23 +3594,35 @@ exports.PieChart = PieChart;
3082
3594
  exports.PulseBar = PulseBar;
3083
3595
  exports.Renderer = Renderer;
3084
3596
  exports.RendererSelector = RendererSelector;
3597
+ exports.SPARK_LEVELS = SPARK_LEVELS;
3085
3598
  exports.Sparkline = Sparkline;
3599
+ exports.TREND = TREND;
3086
3600
  exports.Table = Table;
3087
3601
  exports.TerminalDetector = TerminalDetector;
3088
3602
  exports.ThemeProvider = ThemeProvider;
3089
3603
  exports.arcPoints = arcPoints;
3090
3604
  exports.assignColors = assignColors;
3091
3605
  exports.averageDownsampling = averageDownsampling;
3606
+ exports.buildSparklinePixelGrid = buildSparklinePixelGrid;
3092
3607
  exports.clamp = clamp;
3093
3608
  exports.colorToChalk = colorToChalk;
3094
3609
  exports.createGradient = createGradient;
3610
+ exports.createRgbPng = createRgbPng;
3095
3611
  exports.degreesToRadians = degreesToRadians;
3612
+ exports.detectImageProtocol = detectImageProtocol;
3096
3613
  exports.distanceBetweenPoints = distanceBetweenPoints;
3097
3614
  exports.easeInCubic = easeInCubic;
3098
3615
  exports.easeInOutQuad = easeInOutQuad;
3099
3616
  exports.easeLinear = easeLinear;
3100
3617
  exports.easeOutCubic = easeOutCubic;
3618
+ exports.encodeIterm2 = encodeIterm2;
3619
+ exports.encodeKitty = encodeKitty;
3620
+ exports.encodeKittyDelete = encodeKittyDelete;
3621
+ exports.encodeKittyPlaceholders = encodeKittyPlaceholders;
3622
+ exports.encodeKittyUpload = encodeKittyUpload;
3101
3623
  exports.fixedIntervalDownsampling = fixedIntervalDownsampling;
3624
+ exports.gradientColorFn = gradientColorFn;
3625
+ exports.hexToRgb = hexToRgb;
3102
3626
  exports.linearScale = linearScale;
3103
3627
  exports.lttb = lttb;
3104
3628
  exports.midpointCircle = midpointCircle;
@@ -3109,6 +3633,7 @@ exports.radiansToDegrees = radiansToDegrees;
3109
3633
  exports.rendererSelector = rendererSelector;
3110
3634
  exports.scaleToRange = scaleToRange;
3111
3635
  exports.terminalDetector = terminalDetector;
3636
+ exports.useImageProtocol = useImageProtocol;
3112
3637
  exports.useInkHud = useInkHud;
3113
3638
  exports.useRendererSelector = useRendererSelector;
3114
3639
  exports.useSemanticColors = useSemanticColors;