@opentui/core 0.1.13 → 0.1.15

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/index.js CHANGED
@@ -11,6 +11,7 @@ import {
11
11
  Gutter,
12
12
  KeyHandler,
13
13
  LayoutEvents,
14
+ LogLevel,
14
15
  MeasureMode,
15
16
  MouseButton,
16
17
  MouseEvent,
@@ -26,7 +27,6 @@ import {
26
27
  TerminalConsole,
27
28
  TextAttributes,
28
29
  TextBuffer,
29
- TextSelectionHelper,
30
30
  TrackedNode,
31
31
  bg,
32
32
  bgBlack,
@@ -51,6 +51,7 @@ import {
51
51
  brightWhite,
52
52
  brightYellow,
53
53
  capture,
54
+ convertGlobalToLocalSelection,
54
55
  coordinateToCharacterIndex,
55
56
  createCliRenderer,
56
57
  createTextAttributes,
@@ -74,9 +75,11 @@ import {
74
75
  isDimensionType,
75
76
  isFlexBasisType,
76
77
  isMarginType,
78
+ isOverflowType,
77
79
  isPaddingType,
78
80
  isPositionType,
79
- isPostionTypeType,
81
+ isPositionTypeType,
82
+ isRenderable,
80
83
  isSizeType,
81
84
  isVNode,
82
85
  isValidPercentage,
@@ -115,7 +118,7 @@ import {
115
118
  white,
116
119
  wrapWithDelegates,
117
120
  yellow
118
- } from "./index-4gez9k7q.js";
121
+ } from "./index-sw194bbj.js";
119
122
  // src/post/filters.ts
120
123
  function applyScanlines(buffer, strength = 0.8, step = 2) {
121
124
  const width = buffer.width;
@@ -958,6 +961,7 @@ class Timeline {
958
961
  autoplay;
959
962
  onComplete;
960
963
  onPause;
964
+ stateChangeListeners = [];
961
965
  constructor(options = {}) {
962
966
  this.duration = options.duration || 1000;
963
967
  this.loop = options.loop === true;
@@ -965,6 +969,17 @@ class Timeline {
965
969
  this.onComplete = options.onComplete;
966
970
  this.onPause = options.onPause;
967
971
  }
972
+ addStateChangeListener(listener) {
973
+ this.stateChangeListeners.push(listener);
974
+ }
975
+ removeStateChangeListener(listener) {
976
+ this.stateChangeListeners = this.stateChangeListeners.filter((l) => l !== listener);
977
+ }
978
+ notifyStateChange() {
979
+ for (const listener of this.stateChangeListeners) {
980
+ listener(this);
981
+ }
982
+ }
968
983
  add(target, properties, startTime = 0) {
969
984
  const resolvedStartTime = typeof startTime === "string" ? 0 : startTime;
970
985
  const animationProperties = {};
@@ -1036,6 +1051,7 @@ class Timeline {
1036
1051
  }
1037
1052
  });
1038
1053
  this.isPlaying = true;
1054
+ this.notifyStateChange();
1039
1055
  return this;
1040
1056
  }
1041
1057
  pause() {
@@ -1046,6 +1062,7 @@ class Timeline {
1046
1062
  if (this.onPause) {
1047
1063
  this.onPause();
1048
1064
  }
1065
+ this.notifyStateChange();
1049
1066
  return this;
1050
1067
  }
1051
1068
  resetItems() {
@@ -1071,6 +1088,7 @@ class Timeline {
1071
1088
  this.currentTime = 0;
1072
1089
  this.isPlaying = true;
1073
1090
  this.resetItems();
1091
+ this.notifyStateChange();
1074
1092
  return this;
1075
1093
  }
1076
1094
  update(deltaTime) {
@@ -1103,23 +1121,75 @@ class Timeline {
1103
1121
  if (this.onComplete) {
1104
1122
  this.onComplete();
1105
1123
  }
1124
+ this.notifyStateChange();
1106
1125
  }
1107
1126
  }
1108
1127
  }
1109
1128
 
1110
1129
  class TimelineEngine {
1111
1130
  timelines = new Set;
1131
+ renderer = null;
1132
+ frameCallback = null;
1133
+ isLive = false;
1112
1134
  defaults = {
1113
1135
  frameRate: 60
1114
1136
  };
1137
+ attach(renderer) {
1138
+ if (this.renderer) {
1139
+ this.detach();
1140
+ }
1141
+ this.renderer = renderer;
1142
+ this.frameCallback = async (deltaTime) => {
1143
+ this.update(deltaTime);
1144
+ };
1145
+ renderer.setFrameCallback(this.frameCallback);
1146
+ }
1147
+ detach() {
1148
+ if (this.renderer && this.frameCallback) {
1149
+ this.renderer.removeFrameCallback(this.frameCallback);
1150
+ if (this.isLive) {
1151
+ this.renderer.dropLive();
1152
+ this.isLive = false;
1153
+ }
1154
+ }
1155
+ this.renderer = null;
1156
+ this.frameCallback = null;
1157
+ }
1158
+ updateLiveState() {
1159
+ if (!this.renderer)
1160
+ return;
1161
+ const hasRunningTimelines = Array.from(this.timelines).some((timeline) => !timeline.synced && timeline.isPlaying && !timeline.isComplete);
1162
+ if (hasRunningTimelines && !this.isLive) {
1163
+ this.renderer.requestLive();
1164
+ this.isLive = true;
1165
+ } else if (!hasRunningTimelines && this.isLive) {
1166
+ this.renderer.dropLive();
1167
+ this.isLive = false;
1168
+ }
1169
+ }
1170
+ onTimelineStateChange = (timeline) => {
1171
+ this.updateLiveState();
1172
+ };
1115
1173
  register(timeline) {
1116
- this.timelines.add(timeline);
1174
+ if (!this.timelines.has(timeline)) {
1175
+ this.timelines.add(timeline);
1176
+ timeline.addStateChangeListener(this.onTimelineStateChange);
1177
+ this.updateLiveState();
1178
+ }
1117
1179
  }
1118
1180
  unregister(timeline) {
1119
- this.timelines.delete(timeline);
1181
+ if (this.timelines.has(timeline)) {
1182
+ this.timelines.delete(timeline);
1183
+ timeline.removeStateChangeListener(this.onTimelineStateChange);
1184
+ this.updateLiveState();
1185
+ }
1120
1186
  }
1121
1187
  clear() {
1188
+ for (const timeline of this.timelines) {
1189
+ timeline.removeStateChangeListener(this.onTimelineStateChange);
1190
+ }
1122
1191
  this.timelines.clear();
1192
+ this.updateLiveState();
1123
1193
  }
1124
1194
  update(deltaTime) {
1125
1195
  for (const timeline of this.timelines) {
@@ -1198,7 +1268,7 @@ class BoxRenderable extends Renderable {
1198
1268
  set customBorderChars(value) {
1199
1269
  this._customBorderCharsObj = value;
1200
1270
  this._customBorderChars = value ? borderCharsToArray(value) : undefined;
1201
- this.needsUpdate();
1271
+ this.requestRender();
1202
1272
  }
1203
1273
  get backgroundColor() {
1204
1274
  return this._backgroundColor;
@@ -1207,7 +1277,7 @@ class BoxRenderable extends Renderable {
1207
1277
  const newColor = parseColor(value ?? this._defaultOptions.backgroundColor);
1208
1278
  if (this._backgroundColor !== newColor) {
1209
1279
  this._backgroundColor = newColor;
1210
- this.needsUpdate();
1280
+ this.requestRender();
1211
1281
  }
1212
1282
  }
1213
1283
  get border() {
@@ -1218,7 +1288,7 @@ class BoxRenderable extends Renderable {
1218
1288
  this._border = value;
1219
1289
  this.borderSides = getBorderSides(value);
1220
1290
  this.applyYogaBorders();
1221
- this.needsUpdate();
1291
+ this.requestRender();
1222
1292
  }
1223
1293
  }
1224
1294
  get borderStyle() {
@@ -1229,7 +1299,7 @@ class BoxRenderable extends Renderable {
1229
1299
  if (this._borderStyle !== _value) {
1230
1300
  this._borderStyle = _value;
1231
1301
  this._customBorderChars = undefined;
1232
- this.needsUpdate();
1302
+ this.requestRender();
1233
1303
  }
1234
1304
  }
1235
1305
  get borderColor() {
@@ -1239,7 +1309,7 @@ class BoxRenderable extends Renderable {
1239
1309
  const newColor = parseColor(value ?? this._defaultOptions.borderColor);
1240
1310
  if (this._borderColor !== newColor) {
1241
1311
  this._borderColor = newColor;
1242
- this.needsUpdate();
1312
+ this.requestRender();
1243
1313
  }
1244
1314
  }
1245
1315
  get focusedBorderColor() {
@@ -1250,7 +1320,7 @@ class BoxRenderable extends Renderable {
1250
1320
  if (this._focusedBorderColor !== newColor) {
1251
1321
  this._focusedBorderColor = newColor;
1252
1322
  if (this._focused) {
1253
- this.needsUpdate();
1323
+ this.requestRender();
1254
1324
  }
1255
1325
  }
1256
1326
  }
@@ -1260,7 +1330,7 @@ class BoxRenderable extends Renderable {
1260
1330
  set title(value) {
1261
1331
  if (this._title !== value) {
1262
1332
  this._title = value;
1263
- this.needsUpdate();
1333
+ this.requestRender();
1264
1334
  }
1265
1335
  }
1266
1336
  get titleAlignment() {
@@ -1269,7 +1339,7 @@ class BoxRenderable extends Renderable {
1269
1339
  set titleAlignment(value) {
1270
1340
  if (this._titleAlignment !== value) {
1271
1341
  this._titleAlignment = value;
1272
- this.needsUpdate();
1342
+ this.requestRender();
1273
1343
  }
1274
1344
  }
1275
1345
  renderSelf(buffer) {
@@ -1289,13 +1359,29 @@ class BoxRenderable extends Renderable {
1289
1359
  titleAlignment: this._titleAlignment
1290
1360
  });
1291
1361
  }
1362
+ getScissorRect() {
1363
+ const baseRect = super.getScissorRect();
1364
+ if (!this.borderSides.top && !this.borderSides.right && !this.borderSides.bottom && !this.borderSides.left) {
1365
+ return baseRect;
1366
+ }
1367
+ const leftInset = this.borderSides.left ? 1 : 0;
1368
+ const rightInset = this.borderSides.right ? 1 : 0;
1369
+ const topInset = this.borderSides.top ? 1 : 0;
1370
+ const bottomInset = this.borderSides.bottom ? 1 : 0;
1371
+ return {
1372
+ x: baseRect.x + leftInset,
1373
+ y: baseRect.y + topInset,
1374
+ width: Math.max(0, baseRect.width - leftInset - rightInset),
1375
+ height: Math.max(0, baseRect.height - topInset - bottomInset)
1376
+ };
1377
+ }
1292
1378
  applyYogaBorders() {
1293
1379
  const node = this.layoutNode.yogaNode;
1294
1380
  node.setBorder(Edge.Left, this.borderSides.left ? 1 : 0);
1295
1381
  node.setBorder(Edge.Right, this.borderSides.right ? 1 : 0);
1296
1382
  node.setBorder(Edge.Top, this.borderSides.top ? 1 : 0);
1297
1383
  node.setBorder(Edge.Bottom, this.borderSides.bottom ? 1 : 0);
1298
- this.needsUpdate();
1384
+ this.requestRender();
1299
1385
  }
1300
1386
  applyYogaGap(options) {
1301
1387
  const node = this.layoutNode.yogaNode;
@@ -1312,19 +1398,19 @@ class BoxRenderable extends Renderable {
1312
1398
  set gap(gap) {
1313
1399
  if (isGapType(gap)) {
1314
1400
  this.layoutNode.yogaNode.setGap(Gutter.All, gap);
1315
- this.needsUpdate();
1401
+ this.requestRender();
1316
1402
  }
1317
1403
  }
1318
1404
  set rowGap(rowGap) {
1319
1405
  if (isGapType(rowGap)) {
1320
1406
  this.layoutNode.yogaNode.setGap(Gutter.Row, rowGap);
1321
- this.needsUpdate();
1407
+ this.requestRender();
1322
1408
  }
1323
1409
  }
1324
1410
  set columnGap(columnGap) {
1325
1411
  if (isGapType(columnGap)) {
1326
1412
  this.layoutNode.yogaNode.setGap(Gutter.Column, columnGap);
1327
- this.needsUpdate();
1413
+ this.requestRender();
1328
1414
  }
1329
1415
  }
1330
1416
  }
@@ -1336,7 +1422,8 @@ class FrameBufferRenderable extends Renderable {
1336
1422
  super(ctx, options);
1337
1423
  this.respectAlpha = options.respectAlpha || false;
1338
1424
  this.frameBuffer = OptimizedBuffer.create(options.width, options.height, this._ctx.widthMethod, {
1339
- respectAlpha: this.respectAlpha
1425
+ respectAlpha: this.respectAlpha,
1426
+ id: options.id || `framebufferrenderable-${this.id}`
1340
1427
  });
1341
1428
  }
1342
1429
  onResize(width, height) {
@@ -1345,7 +1432,7 @@ class FrameBufferRenderable extends Renderable {
1345
1432
  }
1346
1433
  this.frameBuffer.resize(width, height);
1347
1434
  super.onResize(width, height);
1348
- this.needsUpdate();
1435
+ this.requestRender();
1349
1436
  }
1350
1437
  renderSelf(buffer) {
1351
1438
  if (!this.visible)
@@ -1360,96 +1447,136 @@ class FrameBufferRenderable extends Renderable {
1360
1447
  // src/renderables/Text.ts
1361
1448
  class TextRenderable extends Renderable {
1362
1449
  selectable = true;
1363
- _text = stringToStyledText("");
1450
+ _text;
1364
1451
  _defaultFg;
1365
1452
  _defaultBg;
1366
1453
  _defaultAttributes;
1367
1454
  _selectionBg;
1368
1455
  _selectionFg;
1369
- selectionHelper;
1456
+ lastLocalSelection = null;
1370
1457
  textBuffer;
1371
- _plainText = "";
1372
1458
  _lineInfo = { lineStarts: [], lineWidths: [] };
1459
+ _defaultOptions = {
1460
+ content: "",
1461
+ fg: RGBA.fromValues(1, 1, 1, 1),
1462
+ bg: RGBA.fromValues(0, 0, 0, 0),
1463
+ selectionBg: undefined,
1464
+ selectionFg: undefined,
1465
+ selectable: true,
1466
+ attributes: 0
1467
+ };
1373
1468
  constructor(ctx, options) {
1374
1469
  super(ctx, options);
1375
- this.selectionHelper = new TextSelectionHelper(() => this.x, () => this.y, () => this._plainText.length, () => this._lineInfo);
1376
- const content = options.content ?? "";
1377
- this._text = typeof content === "string" ? stringToStyledText(content) : content;
1378
- this._defaultFg = options.fg ? parseColor(options.fg) : RGBA.fromValues(1, 1, 1, 1);
1379
- this._defaultBg = options.bg ? parseColor(options.bg) : RGBA.fromValues(0, 0, 0, 0);
1380
- this._defaultAttributes = options.attributes ?? 0;
1381
- this._selectionBg = options.selectionBg ? parseColor(options.selectionBg) : undefined;
1382
- this._selectionFg = options.selectionFg ? parseColor(options.selectionFg) : undefined;
1383
- this.selectable = options.selectable ?? true;
1470
+ const content = options.content ?? this._defaultOptions.content;
1471
+ const styledText = typeof content === "string" ? stringToStyledText(content) : content;
1472
+ this._text = styledText;
1473
+ this._defaultFg = parseColor(options.fg ?? this._defaultOptions.fg);
1474
+ this._defaultBg = parseColor(options.bg ?? this._defaultOptions.bg);
1475
+ this._defaultAttributes = options.attributes ?? this._defaultOptions.attributes;
1476
+ this._selectionBg = options.selectionBg ? parseColor(options.selectionBg) : this._defaultOptions.selectionBg;
1477
+ this._selectionFg = options.selectionFg ? parseColor(options.selectionFg) : this._defaultOptions.selectionFg;
1478
+ this.selectable = options.selectable ?? this._defaultOptions.selectable;
1384
1479
  this.textBuffer = TextBuffer.create(64, this._ctx.widthMethod);
1385
1480
  this.textBuffer.setDefaultFg(this._defaultFg);
1386
1481
  this.textBuffer.setDefaultBg(this._defaultBg);
1387
1482
  this.textBuffer.setDefaultAttributes(this._defaultAttributes);
1388
1483
  this.setupMeasureFunc();
1389
- this.updateTextInfo();
1484
+ this.updateTextInfo(styledText);
1390
1485
  }
1391
1486
  get content() {
1392
1487
  return this._text;
1393
1488
  }
1394
1489
  set content(value) {
1395
- this._text = typeof value === "string" ? stringToStyledText(value) : value;
1396
- this.updateTextInfo();
1490
+ const styledText = typeof value === "string" ? stringToStyledText(value) : value;
1491
+ this._text = styledText;
1492
+ this.updateTextInfo(styledText);
1397
1493
  }
1398
1494
  get fg() {
1399
1495
  return this._defaultFg;
1400
1496
  }
1401
1497
  set fg(value) {
1402
- if (value) {
1403
- this._defaultFg = parseColor(value);
1498
+ const newColor = parseColor(value ?? this._defaultOptions.fg);
1499
+ if (this._defaultFg !== newColor) {
1500
+ this._defaultFg = newColor;
1404
1501
  this.textBuffer.setDefaultFg(this._defaultFg);
1405
- this.needsUpdate();
1502
+ this.requestRender();
1503
+ }
1504
+ }
1505
+ get selectionBg() {
1506
+ return this._selectionBg;
1507
+ }
1508
+ set selectionBg(value) {
1509
+ const newColor = value ? parseColor(value) : this._defaultOptions.selectionBg;
1510
+ if (this._selectionBg !== newColor) {
1511
+ this._selectionBg = newColor;
1512
+ if (this.lastLocalSelection) {
1513
+ this.updateLocalSelection(this.lastLocalSelection);
1514
+ }
1515
+ this.requestRender();
1516
+ }
1517
+ }
1518
+ get selectionFg() {
1519
+ return this._selectionFg;
1520
+ }
1521
+ set selectionFg(value) {
1522
+ const newColor = value ? parseColor(value) : this._defaultOptions.selectionFg;
1523
+ if (this._selectionFg !== newColor) {
1524
+ this._selectionFg = newColor;
1525
+ if (this.lastLocalSelection) {
1526
+ this.updateLocalSelection(this.lastLocalSelection);
1527
+ }
1528
+ this.requestRender();
1406
1529
  }
1407
1530
  }
1408
1531
  get bg() {
1409
1532
  return this._defaultBg;
1410
1533
  }
1411
1534
  set bg(value) {
1412
- if (value) {
1413
- this._defaultBg = parseColor(value);
1535
+ const newColor = parseColor(value ?? this._defaultOptions.bg);
1536
+ if (this._defaultBg !== newColor) {
1537
+ this._defaultBg = newColor;
1414
1538
  this.textBuffer.setDefaultBg(this._defaultBg);
1415
- this.needsUpdate();
1539
+ this.requestRender();
1416
1540
  }
1417
1541
  }
1418
1542
  get attributes() {
1419
1543
  return this._defaultAttributes;
1420
1544
  }
1421
1545
  set attributes(value) {
1422
- this._defaultAttributes = value;
1423
- this.textBuffer.setDefaultAttributes(this._defaultAttributes);
1424
- this.needsUpdate();
1546
+ if (this._defaultAttributes !== value) {
1547
+ this._defaultAttributes = value;
1548
+ this.textBuffer.setDefaultAttributes(this._defaultAttributes);
1549
+ this.requestRender();
1550
+ }
1425
1551
  }
1426
1552
  onResize(width, height) {
1427
- const changed = this.selectionHelper.reevaluateSelection(width, height);
1428
- if (changed) {
1429
- this.syncSelectionToTextBuffer();
1430
- this.needsUpdate();
1553
+ if (this.lastLocalSelection) {
1554
+ const changed = this.updateLocalSelection(this.lastLocalSelection);
1555
+ if (changed) {
1556
+ this.requestRender();
1557
+ }
1431
1558
  }
1432
1559
  }
1433
- syncSelectionToTextBuffer() {
1434
- const selection = this.selectionHelper.getSelection();
1435
- if (selection) {
1436
- this.textBuffer.setSelection(selection.start, selection.end, this._selectionBg, this._selectionFg);
1437
- } else {
1438
- this.textBuffer.resetSelection();
1560
+ updateLocalSelection(localSelection) {
1561
+ if (!localSelection?.isActive) {
1562
+ this.textBuffer.resetLocalSelection();
1563
+ return true;
1439
1564
  }
1565
+ return this.textBuffer.setLocalSelection(localSelection.anchorX, localSelection.anchorY, localSelection.focusX, localSelection.focusY, this._selectionBg, this._selectionFg);
1440
1566
  }
1441
- updateTextInfo() {
1442
- this._plainText = this._text.toString();
1443
- this.updateTextBuffer();
1567
+ updateTextInfo(styledText) {
1568
+ this.updateTextBuffer(styledText);
1444
1569
  const lineInfo = this.textBuffer.lineInfo;
1445
1570
  this._lineInfo.lineStarts = lineInfo.lineStarts;
1446
1571
  this._lineInfo.lineWidths = lineInfo.lineWidths;
1447
- const changed = this.selectionHelper.reevaluateSelection(this.width, this.height);
1448
- if (changed) {
1449
- this.syncSelectionToTextBuffer();
1572
+ if (this.lastLocalSelection) {
1573
+ const changed = this.updateLocalSelection(this.lastLocalSelection);
1574
+ if (changed) {
1575
+ this.requestRender();
1576
+ }
1450
1577
  }
1451
1578
  this.layoutNode.yogaNode.markDirty();
1452
- this.needsUpdate();
1579
+ this.requestRender();
1453
1580
  }
1454
1581
  setupMeasureFunc() {
1455
1582
  const measureFunc = (width, widthMode, height, heightMode) => {
@@ -1475,27 +1602,32 @@ class TextRenderable extends Renderable {
1475
1602
  this.layoutNode.yogaNode.setMeasureFunc(measureFunc);
1476
1603
  }
1477
1604
  shouldStartSelection(x, y) {
1478
- return this.selectionHelper.shouldStartSelection(x, y, this.width, this.height);
1605
+ if (!this.selectable)
1606
+ return false;
1607
+ const localX = x - this.x;
1608
+ const localY = y - this.y;
1609
+ return localX >= 0 && localX < this.width && localY >= 0 && localY < this.height;
1479
1610
  }
1480
1611
  onSelectionChanged(selection) {
1481
- const changed = this.selectionHelper.onSelectionChanged(selection, this.width, this.height);
1612
+ const localSelection = convertGlobalToLocalSelection(selection, this.x, this.y);
1613
+ this.lastLocalSelection = localSelection;
1614
+ const changed = this.updateLocalSelection(localSelection);
1482
1615
  if (changed) {
1483
- this.syncSelectionToTextBuffer();
1484
- this.needsUpdate();
1616
+ this.requestRender();
1485
1617
  }
1486
- return this.selectionHelper.hasSelection();
1618
+ return this.hasSelection();
1487
1619
  }
1488
1620
  getSelectedText() {
1489
- const selection = this.selectionHelper.getSelection();
1490
- if (!selection)
1491
- return "";
1492
- return this._plainText.slice(selection.start, selection.end);
1621
+ return this.textBuffer.getSelectedText();
1493
1622
  }
1494
1623
  hasSelection() {
1495
- return this.selectionHelper.hasSelection();
1624
+ return this.textBuffer.hasSelection();
1625
+ }
1626
+ getSelection() {
1627
+ return this.textBuffer.getSelection();
1496
1628
  }
1497
- updateTextBuffer() {
1498
- this.textBuffer.setStyledText(this._text);
1629
+ updateTextBuffer(styledText) {
1630
+ this.textBuffer.setStyledText(styledText);
1499
1631
  }
1500
1632
  renderSelf(buffer) {
1501
1633
  if (this.textBuffer.ptr) {
@@ -1522,6 +1654,7 @@ class ASCIIFontRenderable extends FrameBufferRenderable {
1522
1654
  _bg;
1523
1655
  _selectionBg;
1524
1656
  _selectionFg;
1657
+ lastLocalSelection = null;
1525
1658
  selectionHelper;
1526
1659
  constructor(ctx, options) {
1527
1660
  const font = options.font || "tiny";
@@ -1540,7 +1673,7 @@ class ASCIIFontRenderable extends FrameBufferRenderable {
1540
1673
  this._selectionBg = options.selectionBg ? parseColor(options.selectionBg) : undefined;
1541
1674
  this._selectionFg = options.selectionFg ? parseColor(options.selectionFg) : undefined;
1542
1675
  this.selectable = options.selectable ?? true;
1543
- this.selectionHelper = new ASCIIFontSelectionHelper(() => this.x, () => this.y, () => this._text, () => this._font);
1676
+ this.selectionHelper = new ASCIIFontSelectionHelper(() => this._text, () => this._font);
1544
1677
  this.renderFontToBuffer();
1545
1678
  }
1546
1679
  get text() {
@@ -1549,9 +1682,11 @@ class ASCIIFontRenderable extends FrameBufferRenderable {
1549
1682
  set text(value) {
1550
1683
  this._text = value;
1551
1684
  this.updateDimensions();
1552
- this.selectionHelper.reevaluateSelection(this.width, this.height);
1685
+ if (this.lastLocalSelection) {
1686
+ this.selectionHelper.onLocalSelectionChanged(this.lastLocalSelection, this.width, this.height);
1687
+ }
1553
1688
  this.renderFontToBuffer();
1554
- this.needsUpdate();
1689
+ this.requestRender();
1555
1690
  }
1556
1691
  get font() {
1557
1692
  return this._font;
@@ -1559,9 +1694,11 @@ class ASCIIFontRenderable extends FrameBufferRenderable {
1559
1694
  set font(value) {
1560
1695
  this._font = value;
1561
1696
  this.updateDimensions();
1562
- this.selectionHelper.reevaluateSelection(this.width, this.height);
1697
+ if (this.lastLocalSelection) {
1698
+ this.selectionHelper.onLocalSelectionChanged(this.lastLocalSelection, this.width, this.height);
1699
+ }
1563
1700
  this.renderFontToBuffer();
1564
- this.needsUpdate();
1701
+ this.requestRender();
1565
1702
  }
1566
1703
  get fg() {
1567
1704
  return this._fg;
@@ -1573,7 +1710,7 @@ class ASCIIFontRenderable extends FrameBufferRenderable {
1573
1710
  this._fg = [typeof value === "string" ? parseColor(value) : value];
1574
1711
  }
1575
1712
  this.renderFontToBuffer();
1576
- this.needsUpdate();
1713
+ this.requestRender();
1577
1714
  }
1578
1715
  get bg() {
1579
1716
  return this._bg;
@@ -1581,7 +1718,7 @@ class ASCIIFontRenderable extends FrameBufferRenderable {
1581
1718
  set bg(value) {
1582
1719
  this._bg = typeof value === "string" ? parseColor(value) : value;
1583
1720
  this.renderFontToBuffer();
1584
- this.needsUpdate();
1721
+ this.requestRender();
1585
1722
  }
1586
1723
  updateDimensions() {
1587
1724
  const measurements = measureText({ text: this._text, font: this._font });
@@ -1589,15 +1726,19 @@ class ASCIIFontRenderable extends FrameBufferRenderable {
1589
1726
  this.height = measurements.height;
1590
1727
  }
1591
1728
  shouldStartSelection(x, y) {
1592
- return this.selectionHelper.shouldStartSelection(x, y, this.width, this.height);
1729
+ const localX = x - this.x;
1730
+ const localY = y - this.y;
1731
+ return this.selectionHelper.shouldStartSelection(localX, localY, this.width, this.height);
1593
1732
  }
1594
1733
  onSelectionChanged(selection) {
1595
- const changed = this.selectionHelper.onSelectionChanged(selection, this.width, this.height);
1734
+ const localSelection = convertGlobalToLocalSelection(selection, this.x, this.y);
1735
+ this.lastLocalSelection = localSelection;
1736
+ const changed = this.selectionHelper.onLocalSelectionChanged(localSelection, this.width, this.height);
1596
1737
  if (changed) {
1597
1738
  this.renderFontToBuffer();
1598
- this.needsUpdate();
1739
+ this.requestRender();
1599
1740
  }
1600
- return this.selectionHelper.hasSelection();
1741
+ return changed;
1601
1742
  }
1602
1743
  getSelectedText() {
1603
1744
  const selection = this.selectionHelper.getSelection();
@@ -1771,7 +1912,7 @@ class InputRenderable extends Renderable {
1771
1912
  if (this._value !== newValue) {
1772
1913
  this._value = newValue;
1773
1914
  this._cursorPosition = Math.min(this._cursorPosition, this._value.length);
1774
- this.needsUpdate();
1915
+ this.requestRender();
1775
1916
  this.updateCursorPosition();
1776
1917
  this.emit("input" /* INPUT */, this._value);
1777
1918
  }
@@ -1779,14 +1920,14 @@ class InputRenderable extends Renderable {
1779
1920
  set placeholder(placeholder) {
1780
1921
  if (this._placeholder !== placeholder) {
1781
1922
  this._placeholder = placeholder;
1782
- this.needsUpdate();
1923
+ this.requestRender();
1783
1924
  }
1784
1925
  }
1785
1926
  set cursorPosition(position) {
1786
1927
  const newPosition = Math.max(0, Math.min(position, this._value.length));
1787
1928
  if (this._cursorPosition !== newPosition) {
1788
1929
  this._cursorPosition = newPosition;
1789
- this.needsUpdate();
1930
+ this.requestRender();
1790
1931
  this.updateCursorPosition();
1791
1932
  }
1792
1933
  }
@@ -1798,7 +1939,7 @@ class InputRenderable extends Renderable {
1798
1939
  const afterCursor = this._value.substring(this._cursorPosition);
1799
1940
  this._value = beforeCursor + text + afterCursor;
1800
1941
  this._cursorPosition += text.length;
1801
- this.needsUpdate();
1942
+ this.requestRender();
1802
1943
  this.updateCursorPosition();
1803
1944
  this.emit("input" /* INPUT */, this._value);
1804
1945
  }
@@ -1808,14 +1949,14 @@ class InputRenderable extends Renderable {
1808
1949
  const afterCursor = this._value.substring(this._cursorPosition);
1809
1950
  this._value = beforeCursor + afterCursor;
1810
1951
  this._cursorPosition--;
1811
- this.needsUpdate();
1952
+ this.requestRender();
1812
1953
  this.updateCursorPosition();
1813
1954
  this.emit("input" /* INPUT */, this._value);
1814
1955
  } else if (direction === "forward" && this._cursorPosition < this._value.length) {
1815
1956
  const beforeCursor = this._value.substring(0, this._cursorPosition);
1816
1957
  const afterCursor = this._value.substring(this._cursorPosition + 1);
1817
1958
  this._value = beforeCursor + afterCursor;
1818
- this.needsUpdate();
1959
+ this.requestRender();
1819
1960
  this.updateCursorPosition();
1820
1961
  this.emit("input" /* INPUT */, this._value);
1821
1962
  }
@@ -1863,49 +2004,49 @@ class InputRenderable extends Renderable {
1863
2004
  this._maxLength = maxLength;
1864
2005
  if (this._value.length > maxLength) {
1865
2006
  this._value = this._value.substring(0, maxLength);
1866
- this.needsUpdate();
2007
+ this.requestRender();
1867
2008
  }
1868
2009
  }
1869
2010
  set backgroundColor(value) {
1870
2011
  const newColor = parseColor(value ?? this._defaultOptions.backgroundColor);
1871
2012
  if (this._backgroundColor !== newColor) {
1872
2013
  this._backgroundColor = newColor;
1873
- this.needsUpdate();
2014
+ this.requestRender();
1874
2015
  }
1875
2016
  }
1876
2017
  set textColor(value) {
1877
2018
  const newColor = parseColor(value ?? this._defaultOptions.textColor);
1878
2019
  if (this._textColor !== newColor) {
1879
2020
  this._textColor = newColor;
1880
- this.needsUpdate();
2021
+ this.requestRender();
1881
2022
  }
1882
2023
  }
1883
2024
  set focusedBackgroundColor(value) {
1884
2025
  const newColor = parseColor(value ?? this._defaultOptions.focusedBackgroundColor);
1885
2026
  if (this._focusedBackgroundColor !== newColor) {
1886
2027
  this._focusedBackgroundColor = newColor;
1887
- this.needsUpdate();
2028
+ this.requestRender();
1888
2029
  }
1889
2030
  }
1890
2031
  set focusedTextColor(value) {
1891
2032
  const newColor = parseColor(value ?? this._defaultOptions.focusedTextColor);
1892
2033
  if (this._focusedTextColor !== newColor) {
1893
2034
  this._focusedTextColor = newColor;
1894
- this.needsUpdate();
2035
+ this.requestRender();
1895
2036
  }
1896
2037
  }
1897
2038
  set placeholderColor(value) {
1898
2039
  const newColor = parseColor(value ?? this._defaultOptions.placeholderColor);
1899
2040
  if (this._placeholderColor !== newColor) {
1900
2041
  this._placeholderColor = newColor;
1901
- this.needsUpdate();
2042
+ this.requestRender();
1902
2043
  }
1903
2044
  }
1904
2045
  set cursorColor(value) {
1905
2046
  const newColor = parseColor(value ?? this._defaultOptions.cursorColor);
1906
2047
  if (this._cursorColor !== newColor) {
1907
2048
  this._cursorColor = newColor;
1908
- this.needsUpdate();
2049
+ this.requestRender();
1909
2050
  }
1910
2051
  }
1911
2052
  updateFromLayout() {
@@ -1990,7 +2131,7 @@ class SelectRenderable extends Renderable {
1990
2131
  this._descriptionColor = parseColor(options.descriptionColor || this._defaultOptions.descriptionColor);
1991
2132
  this._selectedDescriptionColor = parseColor(options.selectedDescriptionColor || this._defaultOptions.selectedDescriptionColor);
1992
2133
  this._fastScrollStep = options.fastScrollStep || this._defaultOptions.fastScrollStep;
1993
- this.needsUpdate();
2134
+ this.requestRender();
1994
2135
  }
1995
2136
  renderSelf(buffer, deltaTime) {
1996
2137
  if (!this.visible || !this.frameBuffer)
@@ -2066,7 +2207,7 @@ class SelectRenderable extends Renderable {
2066
2207
  this._options = options;
2067
2208
  this.selectedIndex = Math.min(this.selectedIndex, Math.max(0, options.length - 1));
2068
2209
  this.updateScrollOffset();
2069
- this.needsUpdate();
2210
+ this.requestRender();
2070
2211
  }
2071
2212
  getSelectedOption() {
2072
2213
  return this._options[this.selectedIndex] || null;
@@ -2084,7 +2225,7 @@ class SelectRenderable extends Renderable {
2084
2225
  this.selectedIndex = 0;
2085
2226
  }
2086
2227
  this.updateScrollOffset();
2087
- this.needsUpdate();
2228
+ this.requestRender();
2088
2229
  this.emit("selectionChanged" /* SELECTION_CHANGED */, this.selectedIndex, this.getSelectedOption());
2089
2230
  }
2090
2231
  moveDown(steps = 1) {
@@ -2097,7 +2238,7 @@ class SelectRenderable extends Renderable {
2097
2238
  this.selectedIndex = this._options.length - 1;
2098
2239
  }
2099
2240
  this.updateScrollOffset();
2100
- this.needsUpdate();
2241
+ this.requestRender();
2101
2242
  this.emit("selectionChanged" /* SELECTION_CHANGED */, this.selectedIndex, this.getSelectedOption());
2102
2243
  }
2103
2244
  selectCurrent() {
@@ -2110,7 +2251,7 @@ class SelectRenderable extends Renderable {
2110
2251
  if (index >= 0 && index < this._options.length) {
2111
2252
  this.selectedIndex = index;
2112
2253
  this.updateScrollOffset();
2113
- this.needsUpdate();
2254
+ this.requestRender();
2114
2255
  this.emit("selectionChanged" /* SELECTION_CHANGED */, this.selectedIndex, this.getSelectedOption());
2115
2256
  }
2116
2257
  }
@@ -2121,13 +2262,13 @@ class SelectRenderable extends Renderable {
2121
2262
  const newScrollOffset = Math.max(0, Math.min(this.selectedIndex - halfVisible, this._options.length - this.maxVisibleItems));
2122
2263
  if (newScrollOffset !== this.scrollOffset) {
2123
2264
  this.scrollOffset = newScrollOffset;
2124
- this.needsUpdate();
2265
+ this.requestRender();
2125
2266
  }
2126
2267
  }
2127
2268
  onResize(width, height) {
2128
2269
  this.maxVisibleItems = Math.max(1, Math.floor(height / this.linesPerItem));
2129
2270
  this.updateScrollOffset();
2130
- this.needsUpdate();
2271
+ this.requestRender();
2131
2272
  }
2132
2273
  handleKeyPress(key) {
2133
2274
  const keyName = typeof key === "string" ? key : key.name;
@@ -2153,7 +2294,7 @@ class SelectRenderable extends Renderable {
2153
2294
  }
2154
2295
  set showScrollIndicator(show) {
2155
2296
  this._showScrollIndicator = show;
2156
- this.needsUpdate();
2297
+ this.requestRender();
2157
2298
  }
2158
2299
  get showDescription() {
2159
2300
  return this._showDescription;
@@ -2165,7 +2306,7 @@ class SelectRenderable extends Renderable {
2165
2306
  this.linesPerItem += this._itemSpacing;
2166
2307
  this.maxVisibleItems = Math.max(1, Math.floor(this.height / this.linesPerItem));
2167
2308
  this.updateScrollOffset();
2168
- this.needsUpdate();
2309
+ this.requestRender();
2169
2310
  }
2170
2311
  }
2171
2312
  get wrapSelection() {
@@ -2178,56 +2319,56 @@ class SelectRenderable extends Renderable {
2178
2319
  const newColor = parseColor(value ?? this._defaultOptions.backgroundColor);
2179
2320
  if (this._backgroundColor !== newColor) {
2180
2321
  this._backgroundColor = newColor;
2181
- this.needsUpdate();
2322
+ this.requestRender();
2182
2323
  }
2183
2324
  }
2184
2325
  set textColor(value) {
2185
2326
  const newColor = parseColor(value ?? this._defaultOptions.textColor);
2186
2327
  if (this._textColor !== newColor) {
2187
2328
  this._textColor = newColor;
2188
- this.needsUpdate();
2329
+ this.requestRender();
2189
2330
  }
2190
2331
  }
2191
2332
  set focusedBackgroundColor(value) {
2192
2333
  const newColor = parseColor(value ?? this._defaultOptions.focusedBackgroundColor);
2193
2334
  if (this._focusedBackgroundColor !== newColor) {
2194
2335
  this._focusedBackgroundColor = newColor;
2195
- this.needsUpdate();
2336
+ this.requestRender();
2196
2337
  }
2197
2338
  }
2198
2339
  set focusedTextColor(value) {
2199
2340
  const newColor = parseColor(value ?? this._defaultOptions.focusedTextColor);
2200
2341
  if (this._focusedTextColor !== newColor) {
2201
2342
  this._focusedTextColor = newColor;
2202
- this.needsUpdate();
2343
+ this.requestRender();
2203
2344
  }
2204
2345
  }
2205
2346
  set selectedBackgroundColor(value) {
2206
2347
  const newColor = parseColor(value ?? this._defaultOptions.selectedBackgroundColor);
2207
2348
  if (this._selectedBackgroundColor !== newColor) {
2208
2349
  this._selectedBackgroundColor = newColor;
2209
- this.needsUpdate();
2350
+ this.requestRender();
2210
2351
  }
2211
2352
  }
2212
2353
  set selectedTextColor(value) {
2213
2354
  const newColor = parseColor(value ?? this._defaultOptions.selectedTextColor);
2214
2355
  if (this._selectedTextColor !== newColor) {
2215
2356
  this._selectedTextColor = newColor;
2216
- this.needsUpdate();
2357
+ this.requestRender();
2217
2358
  }
2218
2359
  }
2219
2360
  set descriptionColor(value) {
2220
2361
  const newColor = parseColor(value ?? this._defaultOptions.descriptionColor);
2221
2362
  if (this._descriptionColor !== newColor) {
2222
2363
  this._descriptionColor = newColor;
2223
- this.needsUpdate();
2364
+ this.requestRender();
2224
2365
  }
2225
2366
  }
2226
2367
  set selectedDescriptionColor(value) {
2227
2368
  const newColor = parseColor(value ?? this._defaultOptions.selectedDescriptionColor);
2228
2369
  if (this._selectedDescriptionColor !== newColor) {
2229
2370
  this._selectedDescriptionColor = newColor;
2230
- this.needsUpdate();
2371
+ this.requestRender();
2231
2372
  }
2232
2373
  }
2233
2374
  set font(font) {
@@ -2237,7 +2378,7 @@ class SelectRenderable extends Renderable {
2237
2378
  this.linesPerItem += this._itemSpacing;
2238
2379
  this.maxVisibleItems = Math.max(1, Math.floor(this.height / this.linesPerItem));
2239
2380
  this.updateScrollOffset();
2240
- this.needsUpdate();
2381
+ this.requestRender();
2241
2382
  }
2242
2383
  set itemSpacing(spacing) {
2243
2384
  this._itemSpacing = spacing;
@@ -2245,7 +2386,7 @@ class SelectRenderable extends Renderable {
2245
2386
  this.linesPerItem += this._itemSpacing;
2246
2387
  this.maxVisibleItems = Math.max(1, Math.floor(this.height / this.linesPerItem));
2247
2388
  this.updateScrollOffset();
2248
- this.needsUpdate();
2389
+ this.requestRender();
2249
2390
  }
2250
2391
  set fastScrollStep(step) {
2251
2392
  this._fastScrollStep = step;
@@ -2379,7 +2520,7 @@ class TabSelectRenderable extends Renderable {
2379
2520
  this._options = options;
2380
2521
  this.selectedIndex = Math.min(this.selectedIndex, Math.max(0, options.length - 1));
2381
2522
  this.updateScrollOffset();
2382
- this.needsUpdate();
2523
+ this.requestRender();
2383
2524
  }
2384
2525
  getSelectedOption() {
2385
2526
  return this._options[this.selectedIndex] || null;
@@ -2396,7 +2537,7 @@ class TabSelectRenderable extends Renderable {
2396
2537
  return;
2397
2538
  }
2398
2539
  this.updateScrollOffset();
2399
- this.needsUpdate();
2540
+ this.requestRender();
2400
2541
  this.emit("selectionChanged" /* SELECTION_CHANGED */, this.selectedIndex, this.getSelectedOption());
2401
2542
  }
2402
2543
  moveRight() {
@@ -2408,7 +2549,7 @@ class TabSelectRenderable extends Renderable {
2408
2549
  return;
2409
2550
  }
2410
2551
  this.updateScrollOffset();
2411
- this.needsUpdate();
2552
+ this.requestRender();
2412
2553
  this.emit("selectionChanged" /* SELECTION_CHANGED */, this.selectedIndex, this.getSelectedOption());
2413
2554
  }
2414
2555
  selectCurrent() {
@@ -2421,7 +2562,7 @@ class TabSelectRenderable extends Renderable {
2421
2562
  if (index >= 0 && index < this._options.length) {
2422
2563
  this.selectedIndex = index;
2423
2564
  this.updateScrollOffset();
2424
- this.needsUpdate();
2565
+ this.requestRender();
2425
2566
  this.emit("selectionChanged" /* SELECTION_CHANGED */, this.selectedIndex, this.getSelectedOption());
2426
2567
  }
2427
2568
  }
@@ -2430,13 +2571,13 @@ class TabSelectRenderable extends Renderable {
2430
2571
  const newScrollOffset = Math.max(0, Math.min(this.selectedIndex - halfVisible, this._options.length - this.maxVisibleTabs));
2431
2572
  if (newScrollOffset !== this.scrollOffset) {
2432
2573
  this.scrollOffset = newScrollOffset;
2433
- this.needsUpdate();
2574
+ this.requestRender();
2434
2575
  }
2435
2576
  }
2436
2577
  onResize(width, height) {
2437
2578
  this.maxVisibleTabs = Math.max(1, Math.floor(width / this._tabWidth));
2438
2579
  this.updateScrollOffset();
2439
- this.needsUpdate();
2580
+ this.requestRender();
2440
2581
  }
2441
2582
  setTabWidth(tabWidth) {
2442
2583
  if (this._tabWidth === tabWidth)
@@ -2444,7 +2585,7 @@ class TabSelectRenderable extends Renderable {
2444
2585
  this._tabWidth = tabWidth;
2445
2586
  this.maxVisibleTabs = Math.max(1, Math.floor(this.width / this._tabWidth));
2446
2587
  this.updateScrollOffset();
2447
- this.needsUpdate();
2588
+ this.requestRender();
2448
2589
  }
2449
2590
  getTabWidth() {
2450
2591
  return this._tabWidth;
@@ -2474,35 +2615,35 @@ class TabSelectRenderable extends Renderable {
2474
2615
  this._options = options;
2475
2616
  this.selectedIndex = Math.min(this.selectedIndex, Math.max(0, options.length - 1));
2476
2617
  this.updateScrollOffset();
2477
- this.needsUpdate();
2618
+ this.requestRender();
2478
2619
  }
2479
2620
  set backgroundColor(color) {
2480
2621
  this._backgroundColor = parseColor(color);
2481
- this.needsUpdate();
2622
+ this.requestRender();
2482
2623
  }
2483
2624
  set textColor(color) {
2484
2625
  this._textColor = parseColor(color);
2485
- this.needsUpdate();
2626
+ this.requestRender();
2486
2627
  }
2487
2628
  set focusedBackgroundColor(color) {
2488
2629
  this._focusedBackgroundColor = parseColor(color);
2489
- this.needsUpdate();
2630
+ this.requestRender();
2490
2631
  }
2491
2632
  set focusedTextColor(color) {
2492
2633
  this._focusedTextColor = parseColor(color);
2493
- this.needsUpdate();
2634
+ this.requestRender();
2494
2635
  }
2495
2636
  set selectedBackgroundColor(color) {
2496
2637
  this._selectedBackgroundColor = parseColor(color);
2497
- this.needsUpdate();
2638
+ this.requestRender();
2498
2639
  }
2499
2640
  set selectedTextColor(color) {
2500
2641
  this._selectedTextColor = parseColor(color);
2501
- this.needsUpdate();
2642
+ this.requestRender();
2502
2643
  }
2503
2644
  set selectedDescriptionColor(color) {
2504
2645
  this._selectedDescriptionColor = parseColor(color);
2505
- this.needsUpdate();
2646
+ this.requestRender();
2506
2647
  }
2507
2648
  get showDescription() {
2508
2649
  return this._showDescription;
@@ -2512,7 +2653,7 @@ class TabSelectRenderable extends Renderable {
2512
2653
  this._showDescription = show;
2513
2654
  const newHeight = this.calculateDynamicHeight();
2514
2655
  this.height = newHeight;
2515
- this.needsUpdate();
2656
+ this.requestRender();
2516
2657
  }
2517
2658
  }
2518
2659
  get showUnderline() {
@@ -2523,7 +2664,7 @@ class TabSelectRenderable extends Renderable {
2523
2664
  this._showUnderline = show;
2524
2665
  const newHeight = this.calculateDynamicHeight();
2525
2666
  this.height = newHeight;
2526
- this.needsUpdate();
2667
+ this.requestRender();
2527
2668
  }
2528
2669
  }
2529
2670
  get showScrollArrows() {
@@ -2532,7 +2673,7 @@ class TabSelectRenderable extends Renderable {
2532
2673
  set showScrollArrows(show) {
2533
2674
  if (this._showScrollArrows !== show) {
2534
2675
  this._showScrollArrows = show;
2535
- this.needsUpdate();
2676
+ this.requestRender();
2536
2677
  }
2537
2678
  }
2538
2679
  get wrapSelection() {
@@ -2550,7 +2691,792 @@ class TabSelectRenderable extends Renderable {
2550
2691
  this._tabWidth = tabWidth;
2551
2692
  this.maxVisibleTabs = Math.max(1, Math.floor(this.width / this._tabWidth));
2552
2693
  this.updateScrollOffset();
2553
- this.needsUpdate();
2694
+ this.requestRender();
2695
+ }
2696
+ }
2697
+ // src/renderables/Slider.ts
2698
+ var defaultThumbBackgroundColor = RGBA.fromHex("#9a9ea3");
2699
+ var defaultTrackBackgroundColor = RGBA.fromHex("#252527");
2700
+
2701
+ class SliderRenderable extends Renderable {
2702
+ orientation;
2703
+ _thumbSize;
2704
+ _thumbPosition;
2705
+ _backgroundColor;
2706
+ _foregroundColor;
2707
+ _onChange;
2708
+ constructor(ctx, options) {
2709
+ super(ctx, options);
2710
+ this.orientation = options.orientation;
2711
+ this._thumbSize = options.thumbSize ?? 1;
2712
+ this._thumbPosition = options.thumbPosition ?? 0;
2713
+ this._onChange = options.onChange;
2714
+ this._backgroundColor = options.backgroundColor ? parseColor(options.backgroundColor) : defaultTrackBackgroundColor;
2715
+ this._foregroundColor = options.foregroundColor ? parseColor(options.foregroundColor) : defaultThumbBackgroundColor;
2716
+ this.setupMouseHandling();
2717
+ }
2718
+ get thumbSize() {
2719
+ return this._thumbSize;
2720
+ }
2721
+ set thumbSize(value) {
2722
+ const clamped = Math.max(1, Math.min(value, this.orientation === "vertical" ? this.height : this.width));
2723
+ if (clamped !== this._thumbSize) {
2724
+ this._thumbSize = clamped;
2725
+ this.requestRender();
2726
+ }
2727
+ }
2728
+ get thumbPosition() {
2729
+ return this._thumbPosition;
2730
+ }
2731
+ set thumbPosition(value) {
2732
+ const clamped = Math.max(0, Math.min(1, value));
2733
+ if (clamped !== this._thumbPosition) {
2734
+ this._thumbPosition = clamped;
2735
+ this._onChange?.(clamped);
2736
+ this.emit("change", { position: clamped });
2737
+ this.requestRender();
2738
+ }
2739
+ }
2740
+ get backgroundColor() {
2741
+ return this._backgroundColor;
2742
+ }
2743
+ set backgroundColor(value) {
2744
+ this._backgroundColor = parseColor(value);
2745
+ this.requestRender();
2746
+ }
2747
+ get foregroundColor() {
2748
+ return this._foregroundColor;
2749
+ }
2750
+ set foregroundColor(value) {
2751
+ this._foregroundColor = parseColor(value);
2752
+ this.requestRender();
2753
+ }
2754
+ setupMouseHandling() {
2755
+ let isDragging = false;
2756
+ let relativeStartPos = 0;
2757
+ this.onMouseDown = (event) => {
2758
+ event.stopPropagation();
2759
+ isDragging = true;
2760
+ const thumbRect = this.getThumbRect();
2761
+ const isOnThumb = event.x >= thumbRect.x && event.x < thumbRect.x + thumbRect.width && event.y >= thumbRect.y && event.y < thumbRect.y + thumbRect.height;
2762
+ if (isOnThumb) {
2763
+ relativeStartPos = this.orientation === "vertical" ? event.y - thumbRect.y : event.x - thumbRect.x;
2764
+ } else {
2765
+ relativeStartPos = this.orientation === "vertical" ? thumbRect.height / 2 : thumbRect.width / 2;
2766
+ }
2767
+ this.updatePositionFromMouse(event, relativeStartPos);
2768
+ };
2769
+ this.onMouseDrag = (event) => {
2770
+ if (!isDragging)
2771
+ return;
2772
+ event.stopPropagation();
2773
+ this.updatePositionFromMouse(event, relativeStartPos);
2774
+ };
2775
+ this.onMouseUp = () => {
2776
+ isDragging = false;
2777
+ };
2778
+ }
2779
+ updatePositionFromMouse(event, relativeStartPos) {
2780
+ const trackStart = this.orientation === "vertical" ? this.y : this.x;
2781
+ const trackSize = this.orientation === "vertical" ? this.height : this.width;
2782
+ const mousePos = this.orientation === "vertical" ? event.y : event.x;
2783
+ const thumbStartPos = mousePos - trackStart - relativeStartPos;
2784
+ const maxThumbStartPos = trackSize - this._thumbSize;
2785
+ const clampedThumbStartPos = Math.max(0, Math.min(maxThumbStartPos, thumbStartPos));
2786
+ const newPosition = maxThumbStartPos > 0 ? clampedThumbStartPos / maxThumbStartPos : 0;
2787
+ this.thumbPosition = newPosition;
2788
+ }
2789
+ getThumbPosition() {
2790
+ const trackSize = this.orientation === "vertical" ? this.height : this.width;
2791
+ const maxPos = trackSize - this._thumbSize;
2792
+ return Math.round(this._thumbPosition * maxPos);
2793
+ }
2794
+ getThumbRect() {
2795
+ const thumbPos = this.getThumbPosition();
2796
+ if (this.orientation === "vertical") {
2797
+ return {
2798
+ x: this.x,
2799
+ y: this.y + thumbPos,
2800
+ width: this.width,
2801
+ height: this._thumbSize
2802
+ };
2803
+ } else {
2804
+ return {
2805
+ x: this.x + thumbPos,
2806
+ y: this.y,
2807
+ width: this._thumbSize,
2808
+ height: this.height
2809
+ };
2810
+ }
2811
+ }
2812
+ renderSelf(buffer) {
2813
+ buffer.fillRect(this.x, this.y, this.width, this.height, this._backgroundColor);
2814
+ const thumbRect = this.getThumbRect();
2815
+ buffer.fillRect(thumbRect.x, thumbRect.y, thumbRect.width, thumbRect.height, this._foregroundColor);
2816
+ }
2817
+ }
2818
+
2819
+ // src/renderables/ScrollBar.ts
2820
+ class ScrollBarRenderable extends Renderable {
2821
+ slider;
2822
+ startArrow;
2823
+ endArrow;
2824
+ orientation;
2825
+ focusable = true;
2826
+ _scrollSize = 0;
2827
+ _scrollPosition = 0;
2828
+ _viewportSize = 0;
2829
+ _showArrows = false;
2830
+ _manualVisibility = false;
2831
+ _onChange;
2832
+ scrollStep = null;
2833
+ get visible() {
2834
+ return super.visible;
2835
+ }
2836
+ set visible(value) {
2837
+ this._manualVisibility = true;
2838
+ super.visible = value;
2839
+ }
2840
+ resetVisibilityControl() {
2841
+ this._manualVisibility = false;
2842
+ this.recalculateVisibility();
2843
+ }
2844
+ get scrollSize() {
2845
+ return this._scrollSize;
2846
+ }
2847
+ get scrollPosition() {
2848
+ return this._scrollPosition;
2849
+ }
2850
+ get viewportSize() {
2851
+ return this._viewportSize;
2852
+ }
2853
+ set scrollSize(value) {
2854
+ if (value === this.scrollSize)
2855
+ return;
2856
+ this._scrollSize = value;
2857
+ this.recalculateVisibility();
2858
+ this.scrollPosition = this.scrollPosition;
2859
+ }
2860
+ set scrollPosition(value) {
2861
+ const newPosition = Math.round(Math.min(Math.max(0, value), this.scrollSize - this.viewportSize));
2862
+ if (newPosition !== this._scrollPosition) {
2863
+ this._scrollPosition = newPosition;
2864
+ this.updateSliderFromScrollState();
2865
+ this._onChange?.(newPosition);
2866
+ this.emit("change", { position: newPosition });
2867
+ }
2868
+ }
2869
+ set viewportSize(value) {
2870
+ if (value === this.viewportSize)
2871
+ return;
2872
+ this._viewportSize = value;
2873
+ this.recalculateVisibility();
2874
+ this.scrollPosition = this.scrollPosition;
2875
+ }
2876
+ get showArrows() {
2877
+ return this._showArrows;
2878
+ }
2879
+ set showArrows(value) {
2880
+ if (value === this._showArrows)
2881
+ return;
2882
+ this._showArrows = value;
2883
+ this.startArrow.visible = value;
2884
+ this.endArrow.visible = value;
2885
+ }
2886
+ constructor(ctx, { trackOptions, arrowOptions, orientation, showArrows = false, ...options }) {
2887
+ super(ctx, {
2888
+ flexDirection: orientation === "vertical" ? "column" : "row",
2889
+ alignSelf: "stretch",
2890
+ alignItems: "stretch",
2891
+ ...options
2892
+ });
2893
+ this._onChange = options.onChange;
2894
+ this.orientation = orientation;
2895
+ this._showArrows = showArrows;
2896
+ this.slider = new SliderRenderable(ctx, {
2897
+ orientation,
2898
+ onChange: (position) => {
2899
+ const scrollRange = Math.max(0, this._scrollSize - this._viewportSize);
2900
+ this._scrollPosition = Math.round(position * scrollRange);
2901
+ this._onChange?.(this._scrollPosition);
2902
+ this.emit("change", { position: this._scrollPosition });
2903
+ },
2904
+ ...orientation === "vertical" ? {
2905
+ width: 2,
2906
+ height: "100%",
2907
+ marginLeft: "auto"
2908
+ } : {
2909
+ width: "100%",
2910
+ height: 1,
2911
+ marginTop: "auto"
2912
+ },
2913
+ flexGrow: 1,
2914
+ flexShrink: 1,
2915
+ ...trackOptions
2916
+ });
2917
+ this.updateSliderFromScrollState();
2918
+ const arrowOpts = arrowOptions ? {
2919
+ foregroundColor: arrowOptions.backgroundColor,
2920
+ backgroundColor: arrowOptions.backgroundColor,
2921
+ attributes: arrowOptions.attributes,
2922
+ ...arrowOptions
2923
+ } : {};
2924
+ this.startArrow = new ArrowRenderable(ctx, {
2925
+ alignSelf: "center",
2926
+ visible: this.showArrows,
2927
+ direction: this.orientation === "vertical" ? "up" : "left",
2928
+ height: this.orientation === "vertical" ? 1 : 1,
2929
+ ...arrowOpts
2930
+ });
2931
+ this.endArrow = new ArrowRenderable(ctx, {
2932
+ alignSelf: "center",
2933
+ visible: this.showArrows,
2934
+ direction: this.orientation === "vertical" ? "down" : "right",
2935
+ height: this.orientation === "vertical" ? 1 : 1,
2936
+ ...arrowOpts
2937
+ });
2938
+ this.add(this.startArrow);
2939
+ this.add(this.slider);
2940
+ this.add(this.endArrow);
2941
+ let startArrowMouseTimeout = undefined;
2942
+ let endArrowMouseTimeout = undefined;
2943
+ this.startArrow.onMouseDown = (event) => {
2944
+ event.stopPropagation();
2945
+ this.scrollBy(-0.5, "viewport");
2946
+ startArrowMouseTimeout = setTimeout(() => {
2947
+ this.scrollBy(-0.5, "viewport");
2948
+ startArrowMouseTimeout = setInterval(() => {
2949
+ this.scrollBy(-0.2, "viewport");
2950
+ }, 200);
2951
+ }, 500);
2952
+ };
2953
+ this.startArrow.onMouseUp = (event) => {
2954
+ event.stopPropagation();
2955
+ clearInterval(startArrowMouseTimeout);
2956
+ };
2957
+ this.endArrow.onMouseDown = (event) => {
2958
+ event.stopPropagation();
2959
+ this.scrollBy(0.5, "viewport");
2960
+ endArrowMouseTimeout = setTimeout(() => {
2961
+ this.scrollBy(0.5, "viewport");
2962
+ endArrowMouseTimeout = setInterval(() => {
2963
+ this.scrollBy(0.2, "viewport");
2964
+ }, 200);
2965
+ }, 500);
2966
+ };
2967
+ this.endArrow.onMouseUp = (event) => {
2968
+ event.stopPropagation();
2969
+ clearInterval(endArrowMouseTimeout);
2970
+ };
2971
+ }
2972
+ set arrowOptions(options) {
2973
+ Object.assign(this.startArrow, options);
2974
+ Object.assign(this.endArrow, options);
2975
+ this.requestRender();
2976
+ }
2977
+ set trackOptions(options) {
2978
+ Object.assign(this.slider, options);
2979
+ this.requestRender();
2980
+ }
2981
+ updateSliderFromScrollState() {
2982
+ const trackSize = this.orientation === "vertical" ? this.slider.height : this.slider.width;
2983
+ const scrollRange = Math.max(0, this._scrollSize - this._viewportSize);
2984
+ if (scrollRange === 0) {
2985
+ this.slider.thumbSize = trackSize;
2986
+ this.slider.thumbPosition = 0;
2987
+ } else {
2988
+ const sizeRatio = this._viewportSize / this._scrollSize;
2989
+ this.slider.thumbSize = Math.max(1, Math.round(sizeRatio * trackSize));
2990
+ const positionRatio = this._scrollPosition / scrollRange;
2991
+ this.slider.thumbPosition = Math.max(0, Math.min(1, positionRatio));
2992
+ }
2993
+ }
2994
+ scrollBy(delta, unit = "absolute") {
2995
+ const multiplier = unit === "viewport" ? this.viewportSize : unit === "content" ? this.scrollSize : unit === "step" ? this.scrollStep ?? 1 : 1;
2996
+ const resolvedDelta = multiplier * delta;
2997
+ this.scrollPosition += resolvedDelta;
2998
+ }
2999
+ recalculateVisibility() {
3000
+ if (!this._manualVisibility) {
3001
+ const sizeRatio = this.scrollSize <= this.viewportSize ? 1 : this.viewportSize / this.scrollSize;
3002
+ super.visible = sizeRatio < 1;
3003
+ }
3004
+ }
3005
+ handleKeyPress(key) {
3006
+ const keyName = typeof key === "string" ? key : key.name;
3007
+ switch (keyName) {
3008
+ case "left":
3009
+ case "h":
3010
+ if (this.orientation !== "horizontal")
3011
+ return false;
3012
+ this.scrollBy(-1 / 5, "viewport");
3013
+ return true;
3014
+ case "right":
3015
+ case "l":
3016
+ if (this.orientation !== "horizontal")
3017
+ return false;
3018
+ this.scrollBy(1 / 5, "viewport");
3019
+ return true;
3020
+ case "up":
3021
+ case "k":
3022
+ if (this.orientation !== "vertical")
3023
+ return false;
3024
+ this.scrollBy(-1 / 5, "viewport");
3025
+ return true;
3026
+ case "down":
3027
+ case "j":
3028
+ if (this.orientation !== "vertical")
3029
+ return false;
3030
+ this.scrollBy(1 / 5, "viewport");
3031
+ return true;
3032
+ case "pageup":
3033
+ this.scrollBy(-1 / 2, "viewport");
3034
+ return true;
3035
+ case "pagedown":
3036
+ this.scrollBy(1 / 2, "viewport");
3037
+ return true;
3038
+ case "home":
3039
+ this.scrollBy(-1, "content");
3040
+ return true;
3041
+ case "end":
3042
+ this.scrollBy(1, "content");
3043
+ return true;
3044
+ }
3045
+ return false;
3046
+ }
3047
+ }
3048
+
3049
+ class ArrowRenderable extends Renderable {
3050
+ _direction;
3051
+ _foregroundColor;
3052
+ _backgroundColor;
3053
+ _attributes;
3054
+ _arrowChars;
3055
+ constructor(ctx, options) {
3056
+ super(ctx, options);
3057
+ this._direction = options.direction;
3058
+ this._foregroundColor = options.foregroundColor ? parseColor(options.foregroundColor) : RGBA.fromValues(1, 1, 1, 1);
3059
+ this._backgroundColor = options.backgroundColor ? parseColor(options.backgroundColor) : RGBA.fromValues(0, 0, 0, 0);
3060
+ this._attributes = options.attributes ?? 0;
3061
+ this._arrowChars = {
3062
+ up: "\u25E2\u25E3",
3063
+ down: "\u25E5\u25E4",
3064
+ left: " \u25C0 ",
3065
+ right: " \u25B6 ",
3066
+ ...options.arrowChars
3067
+ };
3068
+ if (!options.width) {
3069
+ this.width = Bun.stringWidth(this.getArrowChar());
3070
+ }
3071
+ }
3072
+ get direction() {
3073
+ return this._direction;
3074
+ }
3075
+ set direction(value) {
3076
+ if (this._direction !== value) {
3077
+ this._direction = value;
3078
+ this.requestRender();
3079
+ }
3080
+ }
3081
+ get foregroundColor() {
3082
+ return this._foregroundColor;
3083
+ }
3084
+ set foregroundColor(value) {
3085
+ if (this._foregroundColor !== value) {
3086
+ this._foregroundColor = parseColor(value);
3087
+ this.requestRender();
3088
+ }
3089
+ }
3090
+ get backgroundColor() {
3091
+ return this._backgroundColor;
3092
+ }
3093
+ set backgroundColor(value) {
3094
+ if (this._backgroundColor !== value) {
3095
+ this._backgroundColor = parseColor(value);
3096
+ this.requestRender();
3097
+ }
3098
+ }
3099
+ get attributes() {
3100
+ return this._attributes;
3101
+ }
3102
+ set attributes(value) {
3103
+ if (this._attributes !== value) {
3104
+ this._attributes = value;
3105
+ this.requestRender();
3106
+ }
3107
+ }
3108
+ set arrowChars(value) {
3109
+ this._arrowChars = {
3110
+ ...this._arrowChars,
3111
+ ...value
3112
+ };
3113
+ this.requestRender();
3114
+ }
3115
+ renderSelf(buffer) {
3116
+ const char = this.getArrowChar();
3117
+ buffer.drawText(char, this.x, this.y, this._foregroundColor, this._backgroundColor, this._attributes);
3118
+ }
3119
+ getArrowChar() {
3120
+ switch (this._direction) {
3121
+ case "up":
3122
+ return this._arrowChars.up;
3123
+ case "down":
3124
+ return this._arrowChars.down;
3125
+ case "left":
3126
+ return this._arrowChars.left;
3127
+ case "right":
3128
+ return this._arrowChars.right;
3129
+ default:
3130
+ return "?";
3131
+ }
3132
+ }
3133
+ }
3134
+
3135
+ // src/renderables/ScrollBox.ts
3136
+ class ContentRenderable extends BoxRenderable {
3137
+ viewport;
3138
+ constructor(ctx, viewport, options) {
3139
+ super(ctx, options);
3140
+ this.viewport = viewport;
3141
+ }
3142
+ _getChildren() {
3143
+ return this.getChildrenInViewport(this.viewport);
3144
+ }
3145
+ }
3146
+
3147
+ class ScrollBoxRenderable extends BoxRenderable {
3148
+ static idCounter = 0;
3149
+ internalId = 0;
3150
+ wrapper;
3151
+ viewport;
3152
+ content;
3153
+ horizontalScrollBar;
3154
+ verticalScrollBar;
3155
+ focusable = true;
3156
+ selectionListener;
3157
+ autoScrollMouseX = 0;
3158
+ autoScrollMouseY = 0;
3159
+ autoScrollThresholdVertical = 3;
3160
+ autoScrollThresholdHorizontal = 3;
3161
+ autoScrollSpeedSlow = 6;
3162
+ autoScrollSpeedMedium = 36;
3163
+ autoScrollSpeedFast = 72;
3164
+ isAutoScrolling = false;
3165
+ cachedAutoScrollSpeed = 3;
3166
+ autoScrollAccumulatorX = 0;
3167
+ autoScrollAccumulatorY = 0;
3168
+ get scrollTop() {
3169
+ return this.verticalScrollBar.scrollPosition;
3170
+ }
3171
+ set scrollTop(value) {
3172
+ this.verticalScrollBar.scrollPosition = value;
3173
+ }
3174
+ get scrollLeft() {
3175
+ return this.horizontalScrollBar.scrollPosition;
3176
+ }
3177
+ set scrollLeft(value) {
3178
+ this.horizontalScrollBar.scrollPosition = value;
3179
+ }
3180
+ get scrollWidth() {
3181
+ return this.horizontalScrollBar.scrollSize;
3182
+ }
3183
+ get scrollHeight() {
3184
+ return this.verticalScrollBar.scrollSize;
3185
+ }
3186
+ constructor(ctx, {
3187
+ wrapperOptions,
3188
+ viewportOptions,
3189
+ contentOptions,
3190
+ rootOptions,
3191
+ scrollbarOptions,
3192
+ verticalScrollbarOptions,
3193
+ horizontalScrollbarOptions,
3194
+ ...options
3195
+ }) {
3196
+ super(ctx, {
3197
+ flexShrink: 1,
3198
+ flexGrow: 1,
3199
+ flexDirection: "row",
3200
+ flexWrap: "wrap",
3201
+ alignItems: "stretch",
3202
+ ...options,
3203
+ ...rootOptions
3204
+ });
3205
+ this.internalId = ScrollBoxRenderable.idCounter++;
3206
+ this.wrapper = new BoxRenderable(ctx, {
3207
+ flexDirection: "column",
3208
+ flexGrow: 1,
3209
+ flexShrink: 1,
3210
+ flexBasis: "auto",
3211
+ maxHeight: "100%",
3212
+ maxWidth: "100%",
3213
+ ...wrapperOptions,
3214
+ id: `scroll-box-wrapper-${this.internalId}`
3215
+ });
3216
+ super.add(this.wrapper);
3217
+ this.viewport = new BoxRenderable(ctx, {
3218
+ flexDirection: "column",
3219
+ flexGrow: 1,
3220
+ flexShrink: 1,
3221
+ flexBasis: "auto",
3222
+ maxHeight: "100%",
3223
+ maxWidth: "100%",
3224
+ overflow: "scroll",
3225
+ onSizeChange: () => {
3226
+ this.recalculateBarProps();
3227
+ },
3228
+ ...viewportOptions,
3229
+ id: `scroll-box-viewport-${this.internalId}`
3230
+ });
3231
+ this.wrapper.add(this.viewport);
3232
+ this.content = new ContentRenderable(ctx, this.viewport, {
3233
+ alignSelf: "flex-start",
3234
+ onSizeChange: () => {
3235
+ this.recalculateBarProps();
3236
+ },
3237
+ ...contentOptions,
3238
+ id: `scroll-box-content-${this.internalId}`
3239
+ });
3240
+ this.viewport.add(this.content);
3241
+ this.verticalScrollBar = new ScrollBarRenderable(ctx, {
3242
+ ...scrollbarOptions,
3243
+ ...verticalScrollbarOptions,
3244
+ arrowOptions: {
3245
+ ...scrollbarOptions?.arrowOptions,
3246
+ ...verticalScrollbarOptions?.arrowOptions
3247
+ },
3248
+ id: `scroll-box-vertical-scrollbar-${this.internalId}`,
3249
+ orientation: "vertical",
3250
+ onChange: (position) => {
3251
+ this.content.translateY = -position;
3252
+ }
3253
+ });
3254
+ super.add(this.verticalScrollBar);
3255
+ this.horizontalScrollBar = new ScrollBarRenderable(ctx, {
3256
+ ...scrollbarOptions,
3257
+ ...horizontalScrollbarOptions,
3258
+ arrowOptions: {
3259
+ ...scrollbarOptions?.arrowOptions,
3260
+ ...horizontalScrollbarOptions?.arrowOptions
3261
+ },
3262
+ id: `scroll-box-horizontal-scrollbar-${this.internalId}`,
3263
+ orientation: "horizontal",
3264
+ onChange: (position) => {
3265
+ this.content.translateX = -position;
3266
+ }
3267
+ });
3268
+ this.wrapper.add(this.horizontalScrollBar);
3269
+ this.recalculateBarProps();
3270
+ this.selectionListener = () => {
3271
+ const selection = this._ctx.getSelection();
3272
+ if (!selection || !selection.isSelecting) {
3273
+ this.stopAutoScroll();
3274
+ }
3275
+ };
3276
+ this._ctx.on("selection", this.selectionListener);
3277
+ }
3278
+ onUpdate(deltaTime) {
3279
+ this.handleAutoScroll(deltaTime);
3280
+ }
3281
+ scrollBy(delta, unit = "absolute") {
3282
+ if (typeof delta === "number") {
3283
+ this.verticalScrollBar.scrollBy(delta, unit);
3284
+ } else {
3285
+ this.verticalScrollBar.scrollBy(delta.y, unit);
3286
+ this.horizontalScrollBar.scrollBy(delta.x, unit);
3287
+ }
3288
+ }
3289
+ scrollTo(position) {
3290
+ if (typeof position === "number") {
3291
+ this.scrollTop = position;
3292
+ } else {
3293
+ this.scrollTop = position.y;
3294
+ this.scrollLeft = position.x;
3295
+ }
3296
+ }
3297
+ add(obj, index) {
3298
+ return this.content.add(obj, index);
3299
+ }
3300
+ remove(id) {
3301
+ this.content.remove(id);
3302
+ }
3303
+ getChildren() {
3304
+ return this.content.getChildren();
3305
+ }
3306
+ onMouseEvent(event) {
3307
+ if (event.type === "scroll") {
3308
+ let dir = event.scroll?.direction;
3309
+ if (event.modifiers.shift)
3310
+ dir = dir === "up" ? "left" : dir === "down" ? "right" : dir === "right" ? "down" : "up";
3311
+ if (dir === "up")
3312
+ this.scrollTop -= event.scroll?.delta ?? 0;
3313
+ else if (dir === "down")
3314
+ this.scrollTop += event.scroll?.delta ?? 0;
3315
+ else if (dir === "left")
3316
+ this.scrollLeft -= event.scroll?.delta ?? 0;
3317
+ else if (dir === "right")
3318
+ this.scrollLeft += event.scroll?.delta ?? 0;
3319
+ }
3320
+ if (event.type === "drag" && event.isSelecting) {
3321
+ this.updateAutoScroll(event.x, event.y);
3322
+ } else if (event.type === "up") {
3323
+ this.stopAutoScroll();
3324
+ }
3325
+ }
3326
+ handleKeyPress(key) {
3327
+ if (this.verticalScrollBar.handleKeyPress(key))
3328
+ return true;
3329
+ if (this.horizontalScrollBar.handleKeyPress(key))
3330
+ return true;
3331
+ return false;
3332
+ }
3333
+ startAutoScroll(mouseX, mouseY) {
3334
+ this.stopAutoScroll();
3335
+ this.autoScrollMouseX = mouseX;
3336
+ this.autoScrollMouseY = mouseY;
3337
+ this.cachedAutoScrollSpeed = this.getAutoScrollSpeed(mouseX, mouseY);
3338
+ this.isAutoScrolling = true;
3339
+ if (!this.live) {
3340
+ this.live = true;
3341
+ }
3342
+ }
3343
+ updateAutoScroll(mouseX, mouseY) {
3344
+ this.autoScrollMouseX = mouseX;
3345
+ this.autoScrollMouseY = mouseY;
3346
+ this.cachedAutoScrollSpeed = this.getAutoScrollSpeed(mouseX, mouseY);
3347
+ const scrollX = this.getAutoScrollDirectionX(mouseX);
3348
+ const scrollY = this.getAutoScrollDirectionY(mouseY);
3349
+ if (scrollX === 0 && scrollY === 0) {
3350
+ this.stopAutoScroll();
3351
+ } else if (!this.isAutoScrolling) {
3352
+ this.startAutoScroll(mouseX, mouseY);
3353
+ }
3354
+ }
3355
+ stopAutoScroll() {
3356
+ const wasAutoScrolling = this.isAutoScrolling;
3357
+ this.isAutoScrolling = false;
3358
+ this.autoScrollAccumulatorX = 0;
3359
+ this.autoScrollAccumulatorY = 0;
3360
+ if (wasAutoScrolling && !this.hasOtherLiveReasons()) {
3361
+ this.live = false;
3362
+ }
3363
+ }
3364
+ hasOtherLiveReasons() {
3365
+ return false;
3366
+ }
3367
+ handleAutoScroll(deltaTime) {
3368
+ if (!this.isAutoScrolling)
3369
+ return;
3370
+ const scrollX = this.getAutoScrollDirectionX(this.autoScrollMouseX);
3371
+ const scrollY = this.getAutoScrollDirectionY(this.autoScrollMouseY);
3372
+ const scrollAmount = this.cachedAutoScrollSpeed * (deltaTime / 1000);
3373
+ let scrolled = false;
3374
+ if (scrollX !== 0) {
3375
+ this.autoScrollAccumulatorX += scrollX * scrollAmount;
3376
+ const integerScrollX = Math.trunc(this.autoScrollAccumulatorX);
3377
+ if (integerScrollX !== 0) {
3378
+ this.scrollLeft += integerScrollX;
3379
+ this.autoScrollAccumulatorX -= integerScrollX;
3380
+ scrolled = true;
3381
+ }
3382
+ }
3383
+ if (scrollY !== 0) {
3384
+ this.autoScrollAccumulatorY += scrollY * scrollAmount;
3385
+ const integerScrollY = Math.trunc(this.autoScrollAccumulatorY);
3386
+ if (integerScrollY !== 0) {
3387
+ this.scrollTop += integerScrollY;
3388
+ this.autoScrollAccumulatorY -= integerScrollY;
3389
+ scrolled = true;
3390
+ }
3391
+ }
3392
+ if (scrolled) {
3393
+ this._ctx.requestSelectionUpdate();
3394
+ }
3395
+ if (scrollX === 0 && scrollY === 0) {
3396
+ this.stopAutoScroll();
3397
+ }
3398
+ }
3399
+ getAutoScrollDirectionX(mouseX) {
3400
+ const relativeX = mouseX - this.x;
3401
+ const distToLeft = relativeX;
3402
+ const distToRight = this.width - relativeX;
3403
+ if (distToLeft <= this.autoScrollThresholdHorizontal) {
3404
+ return this.scrollLeft > 0 ? -1 : 0;
3405
+ } else if (distToRight <= this.autoScrollThresholdHorizontal) {
3406
+ const maxScrollLeft = this.scrollWidth - this.viewport.width;
3407
+ return this.scrollLeft < maxScrollLeft ? 1 : 0;
3408
+ }
3409
+ return 0;
3410
+ }
3411
+ getAutoScrollDirectionY(mouseY) {
3412
+ const relativeY = mouseY - this.y;
3413
+ const distToTop = relativeY;
3414
+ const distToBottom = this.height - relativeY;
3415
+ if (distToTop <= this.autoScrollThresholdVertical) {
3416
+ return this.scrollTop > 0 ? -1 : 0;
3417
+ } else if (distToBottom <= this.autoScrollThresholdVertical) {
3418
+ const maxScrollTop = this.scrollHeight - this.viewport.height;
3419
+ return this.scrollTop < maxScrollTop ? 1 : 0;
3420
+ }
3421
+ return 0;
3422
+ }
3423
+ getAutoScrollSpeed(mouseX, mouseY) {
3424
+ const relativeX = mouseX - this.x;
3425
+ const relativeY = mouseY - this.y;
3426
+ const distToLeft = relativeX;
3427
+ const distToRight = this.width - relativeX;
3428
+ const distToTop = relativeY;
3429
+ const distToBottom = this.height - relativeY;
3430
+ const minDistance = Math.min(distToLeft, distToRight, distToTop, distToBottom);
3431
+ if (minDistance <= 1) {
3432
+ return this.autoScrollSpeedFast;
3433
+ } else if (minDistance <= 2) {
3434
+ return this.autoScrollSpeedMedium;
3435
+ } else {
3436
+ return this.autoScrollSpeedSlow;
3437
+ }
3438
+ }
3439
+ recalculateBarProps() {
3440
+ this.verticalScrollBar.scrollSize = this.content.height;
3441
+ this.verticalScrollBar.viewportSize = this.viewport.height;
3442
+ this.horizontalScrollBar.scrollSize = this.content.width;
3443
+ this.horizontalScrollBar.viewportSize = this.viewport.width;
3444
+ }
3445
+ set rootOptions(options) {
3446
+ Object.assign(this, options);
3447
+ this.requestRender();
3448
+ }
3449
+ set wrapperOptions(options) {
3450
+ Object.assign(this.wrapper, options);
3451
+ this.requestRender();
3452
+ }
3453
+ set viewportOptions(options) {
3454
+ Object.assign(this.viewport, options);
3455
+ this.requestRender();
3456
+ }
3457
+ set contentOptions(options) {
3458
+ Object.assign(this.content, options);
3459
+ this.requestRender();
3460
+ }
3461
+ set scrollbarOptions(options) {
3462
+ Object.assign(this.verticalScrollBar, options);
3463
+ Object.assign(this.horizontalScrollBar, options);
3464
+ this.requestRender();
3465
+ }
3466
+ set verticalScrollbarOptions(options) {
3467
+ Object.assign(this.verticalScrollBar, options);
3468
+ this.requestRender();
3469
+ }
3470
+ set horizontalScrollbarOptions(options) {
3471
+ Object.assign(this.horizontalScrollBar, options);
3472
+ this.requestRender();
3473
+ }
3474
+ destroySelf() {
3475
+ if (this.selectionListener) {
3476
+ this._ctx.off("selection", this.selectionListener);
3477
+ this.selectionListener = undefined;
3478
+ }
3479
+ super.destroySelf();
2554
3480
  }
2555
3481
  }
2556
3482
  // src/renderables/composition/constructs.ts
@@ -2630,9 +3556,11 @@ export {
2630
3556
  isValidPercentage,
2631
3557
  isVNode,
2632
3558
  isSizeType,
2633
- isPostionTypeType,
3559
+ isRenderable,
3560
+ isPositionTypeType,
2634
3561
  isPositionType,
2635
3562
  isPaddingType,
3563
+ isOverflowType,
2636
3564
  isMarginType,
2637
3565
  isFlexBasisType,
2638
3566
  isDimensionType,
@@ -2658,6 +3586,7 @@ export {
2658
3586
  createTextAttributes,
2659
3587
  createCliRenderer,
2660
3588
  coordinateToCharacterIndex,
3589
+ convertGlobalToLocalSelection,
2661
3590
  capture,
2662
3591
  brightYellow,
2663
3592
  brightWhite,
@@ -2692,7 +3621,6 @@ export {
2692
3621
  VRenderable,
2693
3622
  TrackedNode,
2694
3623
  Timeline,
2695
- TextSelectionHelper,
2696
3624
  TextRenderable,
2697
3625
  TextBuffer,
2698
3626
  TextAttributes,
@@ -2707,6 +3635,8 @@ export {
2707
3635
  SelectRenderableEvents,
2708
3636
  SelectRenderable,
2709
3637
  Select,
3638
+ ScrollBoxRenderable,
3639
+ ScrollBarRenderable,
2710
3640
  RootRenderable,
2711
3641
  RenderableEvents,
2712
3642
  Renderable,
@@ -2715,6 +3645,7 @@ export {
2715
3645
  MouseParser,
2716
3646
  MouseEvent,
2717
3647
  MouseButton,
3648
+ LogLevel,
2718
3649
  LayoutEvents,
2719
3650
  KeyHandler,
2720
3651
  InputRenderableEvents,
@@ -2735,10 +3666,11 @@ export {
2735
3666
  BorderCharArrays,
2736
3667
  BlurEffect,
2737
3668
  BloomEffect,
3669
+ ArrowRenderable,
2738
3670
  ASCIIFontSelectionHelper,
2739
3671
  ASCIIFontRenderable,
2740
3672
  ASCIIFont
2741
3673
  };
2742
3674
 
2743
- //# debugId=90AD54F99873C73864756E2164756E21
3675
+ //# debugId=B627209D76BECE1A64756E2164756E21
2744
3676
  //# sourceMappingURL=index.js.map