@austinthesing/magic-shell 0.1.1 → 0.1.3

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.
Files changed (4) hide show
  1. package/LICENSE +1 -1
  2. package/dist/cli.js +1582 -186
  3. package/dist/index.js +1580 -180
  4. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -1430,6 +1430,13 @@ function t(strings, ...values) {
1430
1430
  }
1431
1431
  return new StyledText(chunks);
1432
1432
  }
1433
+
1434
+ class LinearScrollAccel {
1435
+ tick(_now) {
1436
+ return 1;
1437
+ }
1438
+ reset() {}
1439
+ }
1433
1440
  function isCompleteSequence(data) {
1434
1441
  if (!data.startsWith(ESC)) {
1435
1442
  return "not-escape";
@@ -17249,7 +17256,7 @@ function canonicalize(obj, stack, replacementStack, replacer, key) {
17249
17256
  }
17250
17257
  return canonicalizedObj;
17251
17258
  }
17252
- var EditBuffer, engine, BoxRenderable, TextBufferRenderable, BrandedTextNodeRenderable, TextNodeRenderable, RootTextNodeRenderable, CharacterDiff, characterDiff, extendedWordChars = "a-zA-Z0-9_\\u{C0}-\\u{FF}\\u{D8}-\\u{F6}\\u{F8}-\\u{2C6}\\u{2C8}-\\u{2D7}\\u{2DE}-\\u{2FF}\\u{1E00}-\\u{1EFF}", tokenizeIncludingWhitespace, WordDiff, wordDiff, WordsWithSpaceDiff, wordsWithSpaceDiff, LineDiff, lineDiff, SentenceDiff, sentenceDiff, CssDiff, cssDiff, JsonDiff, jsonDiff, ArrayDiff, arrayDiff, TextRenderable, defaultInputKeybindings, InputRenderableEvents, InputRenderable, defaultThumbBackgroundColor, defaultTrackBackgroundColor, defaultSelectKeybindings, SelectRenderableEvents, SelectRenderable, TabSelectRenderableEvents, EditBufferRenderable;
17259
+ var EditBuffer, engine, BoxRenderable, TextBufferRenderable, BrandedTextNodeRenderable, TextNodeRenderable, RootTextNodeRenderable, CharacterDiff, characterDiff, extendedWordChars = "a-zA-Z0-9_\\u{C0}-\\u{FF}\\u{D8}-\\u{F6}\\u{F8}-\\u{2C6}\\u{2C8}-\\u{2D7}\\u{2DE}-\\u{2FF}\\u{1E00}-\\u{1EFF}", tokenizeIncludingWhitespace, WordDiff, wordDiff, WordsWithSpaceDiff, wordsWithSpaceDiff, LineDiff, lineDiff, SentenceDiff, sentenceDiff, CssDiff, cssDiff, JsonDiff, jsonDiff, ArrayDiff, arrayDiff, TextRenderable, defaultInputKeybindings, InputRenderableEvents, InputRenderable, defaultThumbBackgroundColor, defaultTrackBackgroundColor, SliderRenderable, ScrollBarRenderable, ArrowRenderable, ContentRenderable, ScrollBoxRenderable, defaultSelectKeybindings, SelectRenderableEvents, SelectRenderable, TabSelectRenderableEvents, EditBufferRenderable;
17253
17260
  var init_core = __esm(async () => {
17254
17261
  await init_index_93qf6w1k();
17255
17262
  EditBuffer = class EditBuffer extends EventEmitter10 {
@@ -18968,6 +18975,1146 @@ var init_core = __esm(async () => {
18968
18975
  };
18969
18976
  defaultThumbBackgroundColor = RGBA.fromHex("#9a9ea3");
18970
18977
  defaultTrackBackgroundColor = RGBA.fromHex("#252527");
18978
+ SliderRenderable = class SliderRenderable extends Renderable {
18979
+ orientation;
18980
+ _value;
18981
+ _min;
18982
+ _max;
18983
+ _viewPortSize;
18984
+ _backgroundColor;
18985
+ _foregroundColor;
18986
+ _onChange;
18987
+ constructor(ctx, options) {
18988
+ super(ctx, { flexShrink: 0, ...options });
18989
+ this.orientation = options.orientation;
18990
+ this._min = options.min ?? 0;
18991
+ this._max = options.max ?? 100;
18992
+ this._value = options.value ?? this._min;
18993
+ this._viewPortSize = options.viewPortSize ?? Math.max(1, (this._max - this._min) * 0.1);
18994
+ this._onChange = options.onChange;
18995
+ this._backgroundColor = options.backgroundColor ? parseColor(options.backgroundColor) : defaultTrackBackgroundColor;
18996
+ this._foregroundColor = options.foregroundColor ? parseColor(options.foregroundColor) : defaultThumbBackgroundColor;
18997
+ this.setupMouseHandling();
18998
+ }
18999
+ get value() {
19000
+ return this._value;
19001
+ }
19002
+ set value(newValue) {
19003
+ const clamped = Math.max(this._min, Math.min(this._max, newValue));
19004
+ if (clamped !== this._value) {
19005
+ this._value = clamped;
19006
+ this._onChange?.(clamped);
19007
+ this.emit("change", { value: clamped });
19008
+ this.requestRender();
19009
+ }
19010
+ }
19011
+ get min() {
19012
+ return this._min;
19013
+ }
19014
+ set min(newMin) {
19015
+ if (newMin !== this._min) {
19016
+ this._min = newMin;
19017
+ if (this._value < newMin) {
19018
+ this.value = newMin;
19019
+ }
19020
+ this.requestRender();
19021
+ }
19022
+ }
19023
+ get max() {
19024
+ return this._max;
19025
+ }
19026
+ set max(newMax) {
19027
+ if (newMax !== this._max) {
19028
+ this._max = newMax;
19029
+ if (this._value > newMax) {
19030
+ this.value = newMax;
19031
+ }
19032
+ this.requestRender();
19033
+ }
19034
+ }
19035
+ set viewPortSize(size) {
19036
+ const clampedSize = Math.max(0.01, Math.min(size, this._max - this._min));
19037
+ if (clampedSize !== this._viewPortSize) {
19038
+ this._viewPortSize = clampedSize;
19039
+ this.requestRender();
19040
+ }
19041
+ }
19042
+ get viewPortSize() {
19043
+ return this._viewPortSize;
19044
+ }
19045
+ get backgroundColor() {
19046
+ return this._backgroundColor;
19047
+ }
19048
+ set backgroundColor(value) {
19049
+ this._backgroundColor = parseColor(value);
19050
+ this.requestRender();
19051
+ }
19052
+ get foregroundColor() {
19053
+ return this._foregroundColor;
19054
+ }
19055
+ set foregroundColor(value) {
19056
+ this._foregroundColor = parseColor(value);
19057
+ this.requestRender();
19058
+ }
19059
+ calculateDragOffsetVirtual(event) {
19060
+ const trackStart = this.orientation === "vertical" ? this.y : this.x;
19061
+ const mousePos = (this.orientation === "vertical" ? event.y : event.x) - trackStart;
19062
+ const virtualMousePos = Math.max(0, Math.min((this.orientation === "vertical" ? this.height : this.width) * 2, mousePos * 2));
19063
+ const virtualThumbStart = this.getVirtualThumbStart();
19064
+ const virtualThumbSize = this.getVirtualThumbSize();
19065
+ return Math.max(0, Math.min(virtualThumbSize, virtualMousePos - virtualThumbStart));
19066
+ }
19067
+ setupMouseHandling() {
19068
+ let isDragging = false;
19069
+ let dragOffsetVirtual = 0;
19070
+ this.onMouseDown = (event) => {
19071
+ event.stopPropagation();
19072
+ event.preventDefault();
19073
+ const thumb = this.getThumbRect();
19074
+ const inThumb = event.x >= thumb.x && event.x < thumb.x + thumb.width && event.y >= thumb.y && event.y < thumb.y + thumb.height;
19075
+ if (inThumb) {
19076
+ isDragging = true;
19077
+ dragOffsetVirtual = this.calculateDragOffsetVirtual(event);
19078
+ } else {
19079
+ this.updateValueFromMouseDirect(event);
19080
+ isDragging = true;
19081
+ dragOffsetVirtual = this.calculateDragOffsetVirtual(event);
19082
+ }
19083
+ };
19084
+ this.onMouseDrag = (event) => {
19085
+ if (!isDragging)
19086
+ return;
19087
+ event.stopPropagation();
19088
+ this.updateValueFromMouseWithOffset(event, dragOffsetVirtual);
19089
+ };
19090
+ this.onMouseUp = (event) => {
19091
+ if (isDragging) {
19092
+ this.updateValueFromMouseWithOffset(event, dragOffsetVirtual);
19093
+ }
19094
+ isDragging = false;
19095
+ };
19096
+ }
19097
+ updateValueFromMouseDirect(event) {
19098
+ const trackStart = this.orientation === "vertical" ? this.y : this.x;
19099
+ const trackSize = this.orientation === "vertical" ? this.height : this.width;
19100
+ const mousePos = this.orientation === "vertical" ? event.y : event.x;
19101
+ const relativeMousePos = mousePos - trackStart;
19102
+ const clampedMousePos = Math.max(0, Math.min(trackSize, relativeMousePos));
19103
+ const ratio = trackSize === 0 ? 0 : clampedMousePos / trackSize;
19104
+ const range = this._max - this._min;
19105
+ const newValue = this._min + ratio * range;
19106
+ this.value = newValue;
19107
+ }
19108
+ updateValueFromMouseWithOffset(event, offsetVirtual) {
19109
+ const trackStart = this.orientation === "vertical" ? this.y : this.x;
19110
+ const trackSize = this.orientation === "vertical" ? this.height : this.width;
19111
+ const mousePos = this.orientation === "vertical" ? event.y : event.x;
19112
+ const virtualTrackSize = trackSize * 2;
19113
+ const relativeMousePos = mousePos - trackStart;
19114
+ const clampedMousePos = Math.max(0, Math.min(trackSize, relativeMousePos));
19115
+ const virtualMousePos = clampedMousePos * 2;
19116
+ const virtualThumbSize = this.getVirtualThumbSize();
19117
+ const maxThumbStart = Math.max(0, virtualTrackSize - virtualThumbSize);
19118
+ let desiredThumbStart = virtualMousePos - offsetVirtual;
19119
+ desiredThumbStart = Math.max(0, Math.min(maxThumbStart, desiredThumbStart));
19120
+ const ratio = maxThumbStart === 0 ? 0 : desiredThumbStart / maxThumbStart;
19121
+ const range = this._max - this._min;
19122
+ const newValue = this._min + ratio * range;
19123
+ this.value = newValue;
19124
+ }
19125
+ getThumbRect() {
19126
+ const virtualThumbSize = this.getVirtualThumbSize();
19127
+ const virtualThumbStart = this.getVirtualThumbStart();
19128
+ const realThumbStart = Math.floor(virtualThumbStart / 2);
19129
+ const realThumbSize = Math.ceil((virtualThumbStart + virtualThumbSize) / 2) - realThumbStart;
19130
+ if (this.orientation === "vertical") {
19131
+ return {
19132
+ x: this.x,
19133
+ y: this.y + realThumbStart,
19134
+ width: this.width,
19135
+ height: Math.max(1, realThumbSize)
19136
+ };
19137
+ } else {
19138
+ return {
19139
+ x: this.x + realThumbStart,
19140
+ y: this.y,
19141
+ width: Math.max(1, realThumbSize),
19142
+ height: this.height
19143
+ };
19144
+ }
19145
+ }
19146
+ renderSelf(buffer) {
19147
+ if (this.orientation === "horizontal") {
19148
+ this.renderHorizontal(buffer);
19149
+ } else {
19150
+ this.renderVertical(buffer);
19151
+ }
19152
+ }
19153
+ renderHorizontal(buffer) {
19154
+ const virtualThumbSize = this.getVirtualThumbSize();
19155
+ const virtualThumbStart = this.getVirtualThumbStart();
19156
+ const virtualThumbEnd = virtualThumbStart + virtualThumbSize;
19157
+ buffer.fillRect(this.x, this.y, this.width, this.height, this._backgroundColor);
19158
+ const realStartCell = Math.floor(virtualThumbStart / 2);
19159
+ const realEndCell = Math.ceil(virtualThumbEnd / 2) - 1;
19160
+ const startX = Math.max(0, realStartCell);
19161
+ const endX = Math.min(this.width - 1, realEndCell);
19162
+ for (let realX = startX;realX <= endX; realX++) {
19163
+ const virtualCellStart = realX * 2;
19164
+ const virtualCellEnd = virtualCellStart + 2;
19165
+ const thumbStartInCell = Math.max(virtualThumbStart, virtualCellStart);
19166
+ const thumbEndInCell = Math.min(virtualThumbEnd, virtualCellEnd);
19167
+ const coverage = thumbEndInCell - thumbStartInCell;
19168
+ let char = " ";
19169
+ if (coverage >= 2) {
19170
+ char = "█";
19171
+ } else {
19172
+ const isLeftHalf = thumbStartInCell === virtualCellStart;
19173
+ if (isLeftHalf) {
19174
+ char = "▌";
19175
+ } else {
19176
+ char = "▐";
19177
+ }
19178
+ }
19179
+ for (let y = 0;y < this.height; y++) {
19180
+ buffer.setCellWithAlphaBlending(this.x + realX, this.y + y, char, this._foregroundColor, this._backgroundColor);
19181
+ }
19182
+ }
19183
+ }
19184
+ renderVertical(buffer) {
19185
+ const virtualThumbSize = this.getVirtualThumbSize();
19186
+ const virtualThumbStart = this.getVirtualThumbStart();
19187
+ const virtualThumbEnd = virtualThumbStart + virtualThumbSize;
19188
+ buffer.fillRect(this.x, this.y, this.width, this.height, this._backgroundColor);
19189
+ const realStartCell = Math.floor(virtualThumbStart / 2);
19190
+ const realEndCell = Math.ceil(virtualThumbEnd / 2) - 1;
19191
+ const startY = Math.max(0, realStartCell);
19192
+ const endY = Math.min(this.height - 1, realEndCell);
19193
+ for (let realY = startY;realY <= endY; realY++) {
19194
+ const virtualCellStart = realY * 2;
19195
+ const virtualCellEnd = virtualCellStart + 2;
19196
+ const thumbStartInCell = Math.max(virtualThumbStart, virtualCellStart);
19197
+ const thumbEndInCell = Math.min(virtualThumbEnd, virtualCellEnd);
19198
+ const coverage = thumbEndInCell - thumbStartInCell;
19199
+ let char = " ";
19200
+ if (coverage >= 2) {
19201
+ char = "█";
19202
+ } else if (coverage > 0) {
19203
+ const virtualPositionInCell = thumbStartInCell - virtualCellStart;
19204
+ if (virtualPositionInCell === 0) {
19205
+ char = "▀";
19206
+ } else {
19207
+ char = "▄";
19208
+ }
19209
+ }
19210
+ for (let x = 0;x < this.width; x++) {
19211
+ buffer.setCellWithAlphaBlending(this.x + x, this.y + realY, char, this._foregroundColor, this._backgroundColor);
19212
+ }
19213
+ }
19214
+ }
19215
+ getVirtualThumbSize() {
19216
+ const virtualTrackSize = this.orientation === "vertical" ? this.height * 2 : this.width * 2;
19217
+ const range = this._max - this._min;
19218
+ if (range === 0)
19219
+ return virtualTrackSize;
19220
+ const viewportSize = Math.max(1, this._viewPortSize);
19221
+ const contentSize = range + viewportSize;
19222
+ if (contentSize <= viewportSize)
19223
+ return virtualTrackSize;
19224
+ const thumbRatio = viewportSize / contentSize;
19225
+ const calculatedSize = Math.floor(virtualTrackSize * thumbRatio);
19226
+ return Math.max(1, Math.min(calculatedSize, virtualTrackSize));
19227
+ }
19228
+ getVirtualThumbStart() {
19229
+ const virtualTrackSize = this.orientation === "vertical" ? this.height * 2 : this.width * 2;
19230
+ const range = this._max - this._min;
19231
+ if (range === 0)
19232
+ return 0;
19233
+ const valueRatio = (this._value - this._min) / range;
19234
+ const virtualThumbSize = this.getVirtualThumbSize();
19235
+ return Math.round(valueRatio * (virtualTrackSize - virtualThumbSize));
19236
+ }
19237
+ };
19238
+ ScrollBarRenderable = class ScrollBarRenderable extends Renderable {
19239
+ slider;
19240
+ startArrow;
19241
+ endArrow;
19242
+ orientation;
19243
+ _focusable = true;
19244
+ _scrollSize = 0;
19245
+ _scrollPosition = 0;
19246
+ _viewportSize = 0;
19247
+ _showArrows = false;
19248
+ _manualVisibility = false;
19249
+ _onChange;
19250
+ scrollStep = null;
19251
+ get visible() {
19252
+ return super.visible;
19253
+ }
19254
+ set visible(value) {
19255
+ this._manualVisibility = true;
19256
+ super.visible = value;
19257
+ }
19258
+ resetVisibilityControl() {
19259
+ this._manualVisibility = false;
19260
+ this.recalculateVisibility();
19261
+ }
19262
+ get scrollSize() {
19263
+ return this._scrollSize;
19264
+ }
19265
+ get scrollPosition() {
19266
+ return this._scrollPosition;
19267
+ }
19268
+ get viewportSize() {
19269
+ return this._viewportSize;
19270
+ }
19271
+ set scrollSize(value) {
19272
+ if (value === this.scrollSize)
19273
+ return;
19274
+ this._scrollSize = value;
19275
+ this.recalculateVisibility();
19276
+ this.updateSliderFromScrollState();
19277
+ this.scrollPosition = this.scrollPosition;
19278
+ }
19279
+ set scrollPosition(value) {
19280
+ const newPosition = Math.round(Math.min(Math.max(0, value), this.scrollSize - this.viewportSize));
19281
+ if (newPosition !== this._scrollPosition) {
19282
+ this._scrollPosition = newPosition;
19283
+ this.updateSliderFromScrollState();
19284
+ }
19285
+ }
19286
+ set viewportSize(value) {
19287
+ if (value === this.viewportSize)
19288
+ return;
19289
+ this._viewportSize = value;
19290
+ this.slider.viewPortSize = Math.max(1, this._viewportSize);
19291
+ this.recalculateVisibility();
19292
+ this.updateSliderFromScrollState();
19293
+ this.scrollPosition = this.scrollPosition;
19294
+ }
19295
+ get showArrows() {
19296
+ return this._showArrows;
19297
+ }
19298
+ set showArrows(value) {
19299
+ if (value === this._showArrows)
19300
+ return;
19301
+ this._showArrows = value;
19302
+ this.startArrow.visible = value;
19303
+ this.endArrow.visible = value;
19304
+ }
19305
+ constructor(ctx, { trackOptions, arrowOptions, orientation, showArrows = false, ...options }) {
19306
+ super(ctx, {
19307
+ flexDirection: orientation === "vertical" ? "column" : "row",
19308
+ alignSelf: "stretch",
19309
+ alignItems: "stretch",
19310
+ ...options
19311
+ });
19312
+ this._onChange = options.onChange;
19313
+ this.orientation = orientation;
19314
+ this._showArrows = showArrows;
19315
+ const scrollRange = Math.max(0, this._scrollSize - this._viewportSize);
19316
+ const defaultStepSize = Math.max(1, this._viewportSize);
19317
+ const stepSize = trackOptions?.viewPortSize ?? defaultStepSize;
19318
+ this.slider = new SliderRenderable(ctx, {
19319
+ orientation,
19320
+ min: 0,
19321
+ max: scrollRange,
19322
+ value: this._scrollPosition,
19323
+ viewPortSize: stepSize,
19324
+ onChange: (value) => {
19325
+ this._scrollPosition = Math.round(value);
19326
+ this._onChange?.(this._scrollPosition);
19327
+ this.emit("change", { position: this._scrollPosition });
19328
+ },
19329
+ ...orientation === "vertical" ? {
19330
+ width: Math.max(1, Math.min(2, this.width)),
19331
+ height: "100%",
19332
+ marginLeft: "auto"
19333
+ } : {
19334
+ width: "100%",
19335
+ height: 1,
19336
+ marginTop: "auto"
19337
+ },
19338
+ flexGrow: 1,
19339
+ flexShrink: 1,
19340
+ ...trackOptions
19341
+ });
19342
+ this.updateSliderFromScrollState();
19343
+ const arrowOpts = arrowOptions ? {
19344
+ foregroundColor: arrowOptions.backgroundColor,
19345
+ backgroundColor: arrowOptions.backgroundColor,
19346
+ attributes: arrowOptions.attributes,
19347
+ ...arrowOptions
19348
+ } : {};
19349
+ this.startArrow = new ArrowRenderable(ctx, {
19350
+ alignSelf: "center",
19351
+ visible: this.showArrows,
19352
+ direction: this.orientation === "vertical" ? "up" : "left",
19353
+ height: this.orientation === "vertical" ? 1 : 1,
19354
+ ...arrowOpts
19355
+ });
19356
+ this.endArrow = new ArrowRenderable(ctx, {
19357
+ alignSelf: "center",
19358
+ visible: this.showArrows,
19359
+ direction: this.orientation === "vertical" ? "down" : "right",
19360
+ height: this.orientation === "vertical" ? 1 : 1,
19361
+ ...arrowOpts
19362
+ });
19363
+ this.add(this.startArrow);
19364
+ this.add(this.slider);
19365
+ this.add(this.endArrow);
19366
+ let startArrowMouseTimeout = undefined;
19367
+ let endArrowMouseTimeout = undefined;
19368
+ this.startArrow.onMouseDown = (event) => {
19369
+ event.stopPropagation();
19370
+ event.preventDefault();
19371
+ this.scrollBy(-0.5, "viewport");
19372
+ startArrowMouseTimeout = setTimeout(() => {
19373
+ this.scrollBy(-0.5, "viewport");
19374
+ startArrowMouseTimeout = setInterval(() => {
19375
+ this.scrollBy(-0.2, "viewport");
19376
+ }, 200);
19377
+ }, 500);
19378
+ };
19379
+ this.startArrow.onMouseUp = (event) => {
19380
+ event.stopPropagation();
19381
+ clearInterval(startArrowMouseTimeout);
19382
+ };
19383
+ this.endArrow.onMouseDown = (event) => {
19384
+ event.stopPropagation();
19385
+ event.preventDefault();
19386
+ this.scrollBy(0.5, "viewport");
19387
+ endArrowMouseTimeout = setTimeout(() => {
19388
+ this.scrollBy(0.5, "viewport");
19389
+ endArrowMouseTimeout = setInterval(() => {
19390
+ this.scrollBy(0.2, "viewport");
19391
+ }, 200);
19392
+ }, 500);
19393
+ };
19394
+ this.endArrow.onMouseUp = (event) => {
19395
+ event.stopPropagation();
19396
+ clearInterval(endArrowMouseTimeout);
19397
+ };
19398
+ }
19399
+ set arrowOptions(options) {
19400
+ Object.assign(this.startArrow, options);
19401
+ Object.assign(this.endArrow, options);
19402
+ this.requestRender();
19403
+ }
19404
+ set trackOptions(options) {
19405
+ Object.assign(this.slider, options);
19406
+ this.requestRender();
19407
+ }
19408
+ updateSliderFromScrollState() {
19409
+ const scrollRange = Math.max(0, this._scrollSize - this._viewportSize);
19410
+ this.slider.min = 0;
19411
+ this.slider.max = scrollRange;
19412
+ this.slider.value = Math.min(this._scrollPosition, scrollRange);
19413
+ }
19414
+ scrollBy(delta, unit = "absolute") {
19415
+ const multiplier = unit === "viewport" ? this.viewportSize : unit === "content" ? this.scrollSize : unit === "step" ? this.scrollStep ?? 1 : 1;
19416
+ const resolvedDelta = multiplier * delta;
19417
+ this.scrollPosition += resolvedDelta;
19418
+ }
19419
+ recalculateVisibility() {
19420
+ if (!this._manualVisibility) {
19421
+ const sizeRatio = this.scrollSize <= this.viewportSize ? 1 : this.viewportSize / this.scrollSize;
19422
+ super.visible = sizeRatio < 1;
19423
+ }
19424
+ }
19425
+ handleKeyPress(key) {
19426
+ switch (key.name) {
19427
+ case "left":
19428
+ case "h":
19429
+ if (this.orientation !== "horizontal")
19430
+ return false;
19431
+ this.scrollBy(-1 / 5, "viewport");
19432
+ return true;
19433
+ case "right":
19434
+ case "l":
19435
+ if (this.orientation !== "horizontal")
19436
+ return false;
19437
+ this.scrollBy(1 / 5, "viewport");
19438
+ return true;
19439
+ case "up":
19440
+ case "k":
19441
+ if (this.orientation !== "vertical")
19442
+ return false;
19443
+ this.scrollBy(-1 / 5, "viewport");
19444
+ return true;
19445
+ case "down":
19446
+ case "j":
19447
+ if (this.orientation !== "vertical")
19448
+ return false;
19449
+ this.scrollBy(1 / 5, "viewport");
19450
+ return true;
19451
+ case "pageup":
19452
+ this.scrollBy(-1 / 2, "viewport");
19453
+ return true;
19454
+ case "pagedown":
19455
+ this.scrollBy(1 / 2, "viewport");
19456
+ return true;
19457
+ case "home":
19458
+ this.scrollBy(-1, "content");
19459
+ return true;
19460
+ case "end":
19461
+ this.scrollBy(1, "content");
19462
+ return true;
19463
+ }
19464
+ return false;
19465
+ }
19466
+ };
19467
+ ArrowRenderable = class ArrowRenderable extends Renderable {
19468
+ _direction;
19469
+ _foregroundColor;
19470
+ _backgroundColor;
19471
+ _attributes;
19472
+ _arrowChars;
19473
+ constructor(ctx, options) {
19474
+ super(ctx, options);
19475
+ this._direction = options.direction;
19476
+ this._foregroundColor = options.foregroundColor ? parseColor(options.foregroundColor) : RGBA.fromValues(1, 1, 1, 1);
19477
+ this._backgroundColor = options.backgroundColor ? parseColor(options.backgroundColor) : RGBA.fromValues(0, 0, 0, 0);
19478
+ this._attributes = options.attributes ?? 0;
19479
+ this._arrowChars = {
19480
+ up: "▲",
19481
+ down: "▼",
19482
+ left: "◀",
19483
+ right: "▶",
19484
+ ...options.arrowChars
19485
+ };
19486
+ if (!options.width) {
19487
+ this.width = Bun.stringWidth(this.getArrowChar());
19488
+ }
19489
+ }
19490
+ get direction() {
19491
+ return this._direction;
19492
+ }
19493
+ set direction(value) {
19494
+ if (this._direction !== value) {
19495
+ this._direction = value;
19496
+ this.requestRender();
19497
+ }
19498
+ }
19499
+ get foregroundColor() {
19500
+ return this._foregroundColor;
19501
+ }
19502
+ set foregroundColor(value) {
19503
+ if (this._foregroundColor !== value) {
19504
+ this._foregroundColor = parseColor(value);
19505
+ this.requestRender();
19506
+ }
19507
+ }
19508
+ get backgroundColor() {
19509
+ return this._backgroundColor;
19510
+ }
19511
+ set backgroundColor(value) {
19512
+ if (this._backgroundColor !== value) {
19513
+ this._backgroundColor = parseColor(value);
19514
+ this.requestRender();
19515
+ }
19516
+ }
19517
+ get attributes() {
19518
+ return this._attributes;
19519
+ }
19520
+ set attributes(value) {
19521
+ if (this._attributes !== value) {
19522
+ this._attributes = value;
19523
+ this.requestRender();
19524
+ }
19525
+ }
19526
+ set arrowChars(value) {
19527
+ this._arrowChars = {
19528
+ ...this._arrowChars,
19529
+ ...value
19530
+ };
19531
+ this.requestRender();
19532
+ }
19533
+ renderSelf(buffer) {
19534
+ const char = this.getArrowChar();
19535
+ buffer.drawText(char, this.x, this.y, this._foregroundColor, this._backgroundColor, this._attributes);
19536
+ }
19537
+ getArrowChar() {
19538
+ switch (this._direction) {
19539
+ case "up":
19540
+ return this._arrowChars.up;
19541
+ case "down":
19542
+ return this._arrowChars.down;
19543
+ case "left":
19544
+ return this._arrowChars.left;
19545
+ case "right":
19546
+ return this._arrowChars.right;
19547
+ default:
19548
+ return "?";
19549
+ }
19550
+ }
19551
+ };
19552
+ ContentRenderable = class ContentRenderable extends BoxRenderable {
19553
+ viewport;
19554
+ _viewportCulling;
19555
+ constructor(ctx, viewport, viewportCulling, options) {
19556
+ super(ctx, options);
19557
+ this.viewport = viewport;
19558
+ this._viewportCulling = viewportCulling;
19559
+ }
19560
+ get viewportCulling() {
19561
+ return this._viewportCulling;
19562
+ }
19563
+ set viewportCulling(value) {
19564
+ this._viewportCulling = value;
19565
+ }
19566
+ _getVisibleChildren() {
19567
+ if (this._viewportCulling) {
19568
+ return getObjectsInViewport(this.viewport, this.getChildrenSortedByPrimaryAxis(), this.primaryAxis, 0).map((child) => child.num);
19569
+ }
19570
+ return this.getChildrenSortedByPrimaryAxis().map((child) => child.num);
19571
+ }
19572
+ };
19573
+ ScrollBoxRenderable = class ScrollBoxRenderable extends BoxRenderable {
19574
+ static idCounter = 0;
19575
+ internalId = 0;
19576
+ wrapper;
19577
+ viewport;
19578
+ content;
19579
+ horizontalScrollBar;
19580
+ verticalScrollBar;
19581
+ _focusable = true;
19582
+ selectionListener;
19583
+ autoScrollMouseX = 0;
19584
+ autoScrollMouseY = 0;
19585
+ autoScrollThresholdVertical = 3;
19586
+ autoScrollThresholdHorizontal = 3;
19587
+ autoScrollSpeedSlow = 6;
19588
+ autoScrollSpeedMedium = 36;
19589
+ autoScrollSpeedFast = 72;
19590
+ isAutoScrolling = false;
19591
+ cachedAutoScrollSpeed = 3;
19592
+ autoScrollAccumulatorX = 0;
19593
+ autoScrollAccumulatorY = 0;
19594
+ scrollAccumulatorX = 0;
19595
+ scrollAccumulatorY = 0;
19596
+ _stickyScroll;
19597
+ _stickyScrollTop = false;
19598
+ _stickyScrollBottom = false;
19599
+ _stickyScrollLeft = false;
19600
+ _stickyScrollRight = false;
19601
+ _stickyStart;
19602
+ _hasManualScroll = false;
19603
+ _isApplyingStickyScroll = false;
19604
+ scrollAccel;
19605
+ get stickyScroll() {
19606
+ return this._stickyScroll;
19607
+ }
19608
+ set stickyScroll(value) {
19609
+ this._stickyScroll = value;
19610
+ this.updateStickyState();
19611
+ }
19612
+ get stickyStart() {
19613
+ return this._stickyStart;
19614
+ }
19615
+ set stickyStart(value) {
19616
+ this._stickyStart = value;
19617
+ this.updateStickyState();
19618
+ }
19619
+ get scrollTop() {
19620
+ return this.verticalScrollBar.scrollPosition;
19621
+ }
19622
+ set scrollTop(value) {
19623
+ this.verticalScrollBar.scrollPosition = value;
19624
+ if (!this._isApplyingStickyScroll) {
19625
+ const maxScrollTop = Math.max(0, this.scrollHeight - this.viewport.height);
19626
+ if (!this.isAtStickyPosition() && maxScrollTop > 1) {
19627
+ this._hasManualScroll = true;
19628
+ }
19629
+ }
19630
+ this.updateStickyState();
19631
+ }
19632
+ get scrollLeft() {
19633
+ return this.horizontalScrollBar.scrollPosition;
19634
+ }
19635
+ set scrollLeft(value) {
19636
+ this.horizontalScrollBar.scrollPosition = value;
19637
+ if (!this._isApplyingStickyScroll) {
19638
+ const maxScrollLeft = Math.max(0, this.scrollWidth - this.viewport.width);
19639
+ if (!this.isAtStickyPosition() && maxScrollLeft > 1) {
19640
+ this._hasManualScroll = true;
19641
+ }
19642
+ }
19643
+ this.updateStickyState();
19644
+ }
19645
+ get scrollWidth() {
19646
+ return this.horizontalScrollBar.scrollSize;
19647
+ }
19648
+ get scrollHeight() {
19649
+ return this.verticalScrollBar.scrollSize;
19650
+ }
19651
+ updateStickyState() {
19652
+ if (!this._stickyScroll)
19653
+ return;
19654
+ const maxScrollTop = Math.max(0, this.scrollHeight - this.viewport.height);
19655
+ const maxScrollLeft = Math.max(0, this.scrollWidth - this.viewport.width);
19656
+ if (this.scrollTop <= 0) {
19657
+ this._stickyScrollTop = true;
19658
+ this._stickyScrollBottom = false;
19659
+ } else if (this.scrollTop >= maxScrollTop) {
19660
+ this._stickyScrollTop = false;
19661
+ this._stickyScrollBottom = true;
19662
+ } else {
19663
+ this._stickyScrollTop = false;
19664
+ this._stickyScrollBottom = false;
19665
+ }
19666
+ if (this.scrollLeft <= 0) {
19667
+ this._stickyScrollLeft = true;
19668
+ this._stickyScrollRight = false;
19669
+ } else if (this.scrollLeft >= maxScrollLeft) {
19670
+ this._stickyScrollLeft = false;
19671
+ this._stickyScrollRight = true;
19672
+ } else {
19673
+ this._stickyScrollLeft = false;
19674
+ this._stickyScrollRight = false;
19675
+ }
19676
+ }
19677
+ applyStickyStart(stickyStart) {
19678
+ this._isApplyingStickyScroll = true;
19679
+ switch (stickyStart) {
19680
+ case "top":
19681
+ this._stickyScrollTop = true;
19682
+ this._stickyScrollBottom = false;
19683
+ this.verticalScrollBar.scrollPosition = 0;
19684
+ break;
19685
+ case "bottom":
19686
+ this._stickyScrollTop = false;
19687
+ this._stickyScrollBottom = true;
19688
+ this.verticalScrollBar.scrollPosition = Math.max(0, this.scrollHeight - this.viewport.height);
19689
+ break;
19690
+ case "left":
19691
+ this._stickyScrollLeft = true;
19692
+ this._stickyScrollRight = false;
19693
+ this.horizontalScrollBar.scrollPosition = 0;
19694
+ break;
19695
+ case "right":
19696
+ this._stickyScrollLeft = false;
19697
+ this._stickyScrollRight = true;
19698
+ this.horizontalScrollBar.scrollPosition = Math.max(0, this.scrollWidth - this.viewport.width);
19699
+ break;
19700
+ }
19701
+ this._isApplyingStickyScroll = false;
19702
+ }
19703
+ constructor(ctx, {
19704
+ wrapperOptions,
19705
+ viewportOptions,
19706
+ contentOptions,
19707
+ rootOptions,
19708
+ scrollbarOptions,
19709
+ verticalScrollbarOptions,
19710
+ horizontalScrollbarOptions,
19711
+ stickyScroll = false,
19712
+ stickyStart,
19713
+ scrollX = false,
19714
+ scrollY = true,
19715
+ scrollAcceleration,
19716
+ viewportCulling = true,
19717
+ ...options
19718
+ }) {
19719
+ super(ctx, {
19720
+ flexDirection: "row",
19721
+ alignItems: "stretch",
19722
+ ...options,
19723
+ ...rootOptions
19724
+ });
19725
+ this.internalId = ScrollBoxRenderable.idCounter++;
19726
+ this._stickyScroll = stickyScroll;
19727
+ this._stickyStart = stickyStart;
19728
+ this.scrollAccel = scrollAcceleration ?? new LinearScrollAccel;
19729
+ this.wrapper = new BoxRenderable(ctx, {
19730
+ flexDirection: "column",
19731
+ flexGrow: 1,
19732
+ ...wrapperOptions,
19733
+ id: `scroll-box-wrapper-${this.internalId}`
19734
+ });
19735
+ super.add(this.wrapper);
19736
+ this.viewport = new BoxRenderable(ctx, {
19737
+ flexDirection: "column",
19738
+ flexGrow: 1,
19739
+ overflow: "hidden",
19740
+ onSizeChange: () => {
19741
+ this.recalculateBarProps();
19742
+ },
19743
+ ...viewportOptions,
19744
+ id: `scroll-box-viewport-${this.internalId}`
19745
+ });
19746
+ this.wrapper.add(this.viewport);
19747
+ this.content = new ContentRenderable(ctx, this.viewport, viewportCulling, {
19748
+ alignSelf: "flex-start",
19749
+ flexShrink: 0,
19750
+ ...scrollX ? { minWidth: "100%" } : { minWidth: "100%", maxWidth: "100%" },
19751
+ ...scrollY ? { minHeight: "100%" } : { minHeight: "100%", maxHeight: "100%" },
19752
+ onSizeChange: () => {
19753
+ this.recalculateBarProps();
19754
+ },
19755
+ ...contentOptions,
19756
+ id: `scroll-box-content-${this.internalId}`
19757
+ });
19758
+ this.viewport.add(this.content);
19759
+ this.verticalScrollBar = new ScrollBarRenderable(ctx, {
19760
+ ...scrollbarOptions,
19761
+ ...verticalScrollbarOptions,
19762
+ arrowOptions: {
19763
+ ...scrollbarOptions?.arrowOptions,
19764
+ ...verticalScrollbarOptions?.arrowOptions
19765
+ },
19766
+ id: `scroll-box-vertical-scrollbar-${this.internalId}`,
19767
+ orientation: "vertical",
19768
+ onChange: (position) => {
19769
+ this.content.translateY = -position;
19770
+ if (!this._isApplyingStickyScroll) {
19771
+ const maxScrollTop = Math.max(0, this.scrollHeight - this.viewport.height);
19772
+ if (!this.isAtStickyPosition() && maxScrollTop > 1) {
19773
+ this._hasManualScroll = true;
19774
+ }
19775
+ }
19776
+ this.updateStickyState();
19777
+ }
19778
+ });
19779
+ super.add(this.verticalScrollBar);
19780
+ this.horizontalScrollBar = new ScrollBarRenderable(ctx, {
19781
+ ...scrollbarOptions,
19782
+ ...horizontalScrollbarOptions,
19783
+ arrowOptions: {
19784
+ ...scrollbarOptions?.arrowOptions,
19785
+ ...horizontalScrollbarOptions?.arrowOptions
19786
+ },
19787
+ id: `scroll-box-horizontal-scrollbar-${this.internalId}`,
19788
+ orientation: "horizontal",
19789
+ onChange: (position) => {
19790
+ this.content.translateX = -position;
19791
+ if (!this._isApplyingStickyScroll) {
19792
+ const maxScrollLeft = Math.max(0, this.scrollWidth - this.viewport.width);
19793
+ if (!this.isAtStickyPosition() && maxScrollLeft > 1) {
19794
+ this._hasManualScroll = true;
19795
+ }
19796
+ }
19797
+ this.updateStickyState();
19798
+ }
19799
+ });
19800
+ this.wrapper.add(this.horizontalScrollBar);
19801
+ this.recalculateBarProps();
19802
+ if (stickyStart && stickyScroll) {
19803
+ this.applyStickyStart(stickyStart);
19804
+ }
19805
+ this.selectionListener = () => {
19806
+ const selection = this._ctx.getSelection();
19807
+ if (!selection || !selection.isSelecting) {
19808
+ this.stopAutoScroll();
19809
+ }
19810
+ };
19811
+ this._ctx.on("selection", this.selectionListener);
19812
+ }
19813
+ onUpdate(deltaTime) {
19814
+ this.handleAutoScroll(deltaTime);
19815
+ }
19816
+ scrollBy(delta, unit = "absolute") {
19817
+ if (typeof delta === "number") {
19818
+ this.verticalScrollBar.scrollBy(delta, unit);
19819
+ } else {
19820
+ this.verticalScrollBar.scrollBy(delta.y, unit);
19821
+ this.horizontalScrollBar.scrollBy(delta.x, unit);
19822
+ }
19823
+ }
19824
+ scrollTo(position) {
19825
+ if (typeof position === "number") {
19826
+ this.scrollTop = position;
19827
+ } else {
19828
+ this.scrollTop = position.y;
19829
+ this.scrollLeft = position.x;
19830
+ }
19831
+ }
19832
+ isAtStickyPosition() {
19833
+ if (!this._stickyScroll || !this._stickyStart) {
19834
+ return false;
19835
+ }
19836
+ const maxScrollTop = Math.max(0, this.scrollHeight - this.viewport.height);
19837
+ const maxScrollLeft = Math.max(0, this.scrollWidth - this.viewport.width);
19838
+ switch (this._stickyStart) {
19839
+ case "top":
19840
+ return this.scrollTop === 0;
19841
+ case "bottom":
19842
+ return this.scrollTop >= maxScrollTop;
19843
+ case "left":
19844
+ return this.scrollLeft === 0;
19845
+ case "right":
19846
+ return this.scrollLeft >= maxScrollLeft;
19847
+ default:
19848
+ return false;
19849
+ }
19850
+ }
19851
+ add(obj, index) {
19852
+ return this.content.add(obj, index);
19853
+ }
19854
+ insertBefore(obj, anchor) {
19855
+ return this.content.insertBefore(obj, anchor);
19856
+ }
19857
+ remove(id) {
19858
+ this.content.remove(id);
19859
+ }
19860
+ getChildren() {
19861
+ return this.content.getChildren();
19862
+ }
19863
+ onMouseEvent(event) {
19864
+ if (event.type === "scroll") {
19865
+ let dir = event.scroll?.direction;
19866
+ if (event.modifiers.shift)
19867
+ dir = dir === "up" ? "left" : dir === "down" ? "right" : dir === "right" ? "down" : "up";
19868
+ const baseDelta = event.scroll?.delta ?? 0;
19869
+ const now = Date.now();
19870
+ const multiplier = this.scrollAccel.tick(now);
19871
+ const scrollAmount = baseDelta * multiplier;
19872
+ if (dir === "up") {
19873
+ this.scrollAccumulatorY -= scrollAmount;
19874
+ const integerScroll = Math.trunc(this.scrollAccumulatorY);
19875
+ if (integerScroll !== 0) {
19876
+ this.scrollTop += integerScroll;
19877
+ this.scrollAccumulatorY -= integerScroll;
19878
+ }
19879
+ } else if (dir === "down") {
19880
+ this.scrollAccumulatorY += scrollAmount;
19881
+ const integerScroll = Math.trunc(this.scrollAccumulatorY);
19882
+ if (integerScroll !== 0) {
19883
+ this.scrollTop += integerScroll;
19884
+ this.scrollAccumulatorY -= integerScroll;
19885
+ }
19886
+ } else if (dir === "left") {
19887
+ this.scrollAccumulatorX -= scrollAmount;
19888
+ const integerScroll = Math.trunc(this.scrollAccumulatorX);
19889
+ if (integerScroll !== 0) {
19890
+ this.scrollLeft += integerScroll;
19891
+ this.scrollAccumulatorX -= integerScroll;
19892
+ }
19893
+ } else if (dir === "right") {
19894
+ this.scrollAccumulatorX += scrollAmount;
19895
+ const integerScroll = Math.trunc(this.scrollAccumulatorX);
19896
+ if (integerScroll !== 0) {
19897
+ this.scrollLeft += integerScroll;
19898
+ this.scrollAccumulatorX -= integerScroll;
19899
+ }
19900
+ }
19901
+ const maxScrollTop = Math.max(0, this.scrollHeight - this.viewport.height);
19902
+ const maxScrollLeft = Math.max(0, this.scrollWidth - this.viewport.width);
19903
+ if (maxScrollTop > 1 || maxScrollLeft > 1) {
19904
+ this._hasManualScroll = true;
19905
+ }
19906
+ }
19907
+ if (event.type === "drag" && event.isSelecting) {
19908
+ this.updateAutoScroll(event.x, event.y);
19909
+ } else if (event.type === "up") {
19910
+ this.stopAutoScroll();
19911
+ }
19912
+ }
19913
+ handleKeyPress(key) {
19914
+ if (this.verticalScrollBar.handleKeyPress(key)) {
19915
+ this._hasManualScroll = true;
19916
+ this.scrollAccel.reset();
19917
+ this.resetScrollAccumulators();
19918
+ return true;
19919
+ }
19920
+ if (this.horizontalScrollBar.handleKeyPress(key)) {
19921
+ this._hasManualScroll = true;
19922
+ this.scrollAccel.reset();
19923
+ this.resetScrollAccumulators();
19924
+ return true;
19925
+ }
19926
+ return false;
19927
+ }
19928
+ resetScrollAccumulators() {
19929
+ this.scrollAccumulatorX = 0;
19930
+ this.scrollAccumulatorY = 0;
19931
+ }
19932
+ startAutoScroll(mouseX, mouseY) {
19933
+ this.stopAutoScroll();
19934
+ this.autoScrollMouseX = mouseX;
19935
+ this.autoScrollMouseY = mouseY;
19936
+ this.cachedAutoScrollSpeed = this.getAutoScrollSpeed(mouseX, mouseY);
19937
+ this.isAutoScrolling = true;
19938
+ if (!this.live) {
19939
+ this.live = true;
19940
+ }
19941
+ }
19942
+ updateAutoScroll(mouseX, mouseY) {
19943
+ this.autoScrollMouseX = mouseX;
19944
+ this.autoScrollMouseY = mouseY;
19945
+ this.cachedAutoScrollSpeed = this.getAutoScrollSpeed(mouseX, mouseY);
19946
+ const scrollX = this.getAutoScrollDirectionX(mouseX);
19947
+ const scrollY = this.getAutoScrollDirectionY(mouseY);
19948
+ if (scrollX === 0 && scrollY === 0) {
19949
+ this.stopAutoScroll();
19950
+ } else if (!this.isAutoScrolling) {
19951
+ this.startAutoScroll(mouseX, mouseY);
19952
+ }
19953
+ }
19954
+ stopAutoScroll() {
19955
+ const wasAutoScrolling = this.isAutoScrolling;
19956
+ this.isAutoScrolling = false;
19957
+ this.autoScrollAccumulatorX = 0;
19958
+ this.autoScrollAccumulatorY = 0;
19959
+ if (wasAutoScrolling && !this.hasOtherLiveReasons()) {
19960
+ this.live = false;
19961
+ }
19962
+ }
19963
+ hasOtherLiveReasons() {
19964
+ return false;
19965
+ }
19966
+ handleAutoScroll(deltaTime) {
19967
+ if (!this.isAutoScrolling)
19968
+ return;
19969
+ const scrollX = this.getAutoScrollDirectionX(this.autoScrollMouseX);
19970
+ const scrollY = this.getAutoScrollDirectionY(this.autoScrollMouseY);
19971
+ const scrollAmount = this.cachedAutoScrollSpeed * (deltaTime / 1000);
19972
+ let scrolled = false;
19973
+ if (scrollX !== 0) {
19974
+ this.autoScrollAccumulatorX += scrollX * scrollAmount;
19975
+ const integerScrollX = Math.trunc(this.autoScrollAccumulatorX);
19976
+ if (integerScrollX !== 0) {
19977
+ this.scrollLeft += integerScrollX;
19978
+ this.autoScrollAccumulatorX -= integerScrollX;
19979
+ scrolled = true;
19980
+ }
19981
+ }
19982
+ if (scrollY !== 0) {
19983
+ this.autoScrollAccumulatorY += scrollY * scrollAmount;
19984
+ const integerScrollY = Math.trunc(this.autoScrollAccumulatorY);
19985
+ if (integerScrollY !== 0) {
19986
+ this.scrollTop += integerScrollY;
19987
+ this.autoScrollAccumulatorY -= integerScrollY;
19988
+ scrolled = true;
19989
+ }
19990
+ }
19991
+ if (scrolled) {
19992
+ this._ctx.requestSelectionUpdate();
19993
+ }
19994
+ if (scrollX === 0 && scrollY === 0) {
19995
+ this.stopAutoScroll();
19996
+ }
19997
+ }
19998
+ getAutoScrollDirectionX(mouseX) {
19999
+ const relativeX = mouseX - this.x;
20000
+ const distToLeft = relativeX;
20001
+ const distToRight = this.width - relativeX;
20002
+ if (distToLeft <= this.autoScrollThresholdHorizontal) {
20003
+ return this.scrollLeft > 0 ? -1 : 0;
20004
+ } else if (distToRight <= this.autoScrollThresholdHorizontal) {
20005
+ const maxScrollLeft = this.scrollWidth - this.viewport.width;
20006
+ return this.scrollLeft < maxScrollLeft ? 1 : 0;
20007
+ }
20008
+ return 0;
20009
+ }
20010
+ getAutoScrollDirectionY(mouseY) {
20011
+ const relativeY = mouseY - this.y;
20012
+ const distToTop = relativeY;
20013
+ const distToBottom = this.height - relativeY;
20014
+ if (distToTop <= this.autoScrollThresholdVertical) {
20015
+ return this.scrollTop > 0 ? -1 : 0;
20016
+ } else if (distToBottom <= this.autoScrollThresholdVertical) {
20017
+ const maxScrollTop = this.scrollHeight - this.viewport.height;
20018
+ return this.scrollTop < maxScrollTop ? 1 : 0;
20019
+ }
20020
+ return 0;
20021
+ }
20022
+ getAutoScrollSpeed(mouseX, mouseY) {
20023
+ const relativeX = mouseX - this.x;
20024
+ const relativeY = mouseY - this.y;
20025
+ const distToLeft = relativeX;
20026
+ const distToRight = this.width - relativeX;
20027
+ const distToTop = relativeY;
20028
+ const distToBottom = this.height - relativeY;
20029
+ const minDistance = Math.min(distToLeft, distToRight, distToTop, distToBottom);
20030
+ if (minDistance <= 1) {
20031
+ return this.autoScrollSpeedFast;
20032
+ } else if (minDistance <= 2) {
20033
+ return this.autoScrollSpeedMedium;
20034
+ } else {
20035
+ return this.autoScrollSpeedSlow;
20036
+ }
20037
+ }
20038
+ recalculateBarProps() {
20039
+ const wasApplyingStickyScroll = this._isApplyingStickyScroll;
20040
+ this._isApplyingStickyScroll = true;
20041
+ this.verticalScrollBar.scrollSize = this.content.height;
20042
+ this.verticalScrollBar.viewportSize = this.viewport.height;
20043
+ this.horizontalScrollBar.scrollSize = this.content.width;
20044
+ this.horizontalScrollBar.viewportSize = this.viewport.width;
20045
+ if (this._stickyScroll) {
20046
+ const newMaxScrollTop = Math.max(0, this.scrollHeight - this.viewport.height);
20047
+ const newMaxScrollLeft = Math.max(0, this.scrollWidth - this.viewport.width);
20048
+ if (this._stickyStart && !this._hasManualScroll) {
20049
+ this.applyStickyStart(this._stickyStart);
20050
+ } else {
20051
+ if (this._stickyScrollTop) {
20052
+ this.scrollTop = 0;
20053
+ } else if (this._stickyScrollBottom && newMaxScrollTop > 0) {
20054
+ this.scrollTop = newMaxScrollTop;
20055
+ }
20056
+ if (this._stickyScrollLeft) {
20057
+ this.scrollLeft = 0;
20058
+ } else if (this._stickyScrollRight && newMaxScrollLeft > 0) {
20059
+ this.scrollLeft = newMaxScrollLeft;
20060
+ }
20061
+ }
20062
+ }
20063
+ this._isApplyingStickyScroll = wasApplyingStickyScroll;
20064
+ process.nextTick(() => {
20065
+ this.requestRender();
20066
+ });
20067
+ }
20068
+ set rootOptions(options) {
20069
+ Object.assign(this, options);
20070
+ this.requestRender();
20071
+ }
20072
+ set wrapperOptions(options) {
20073
+ Object.assign(this.wrapper, options);
20074
+ this.requestRender();
20075
+ }
20076
+ set viewportOptions(options) {
20077
+ Object.assign(this.viewport, options);
20078
+ this.requestRender();
20079
+ }
20080
+ set contentOptions(options) {
20081
+ Object.assign(this.content, options);
20082
+ this.requestRender();
20083
+ }
20084
+ set scrollbarOptions(options) {
20085
+ Object.assign(this.verticalScrollBar, options);
20086
+ Object.assign(this.horizontalScrollBar, options);
20087
+ this.requestRender();
20088
+ }
20089
+ set verticalScrollbarOptions(options) {
20090
+ Object.assign(this.verticalScrollBar, options);
20091
+ this.requestRender();
20092
+ }
20093
+ set horizontalScrollbarOptions(options) {
20094
+ Object.assign(this.horizontalScrollBar, options);
20095
+ this.requestRender();
20096
+ }
20097
+ get scrollAcceleration() {
20098
+ return this.scrollAccel;
20099
+ }
20100
+ set scrollAcceleration(value) {
20101
+ this.scrollAccel = value;
20102
+ }
20103
+ get viewportCulling() {
20104
+ return this.content.viewportCulling;
20105
+ }
20106
+ set viewportCulling(value) {
20107
+ this.content.viewportCulling = value;
20108
+ this.requestRender();
20109
+ }
20110
+ destroySelf() {
20111
+ if (this.selectionListener) {
20112
+ this._ctx.off("selection", this.selectionListener);
20113
+ this.selectionListener = undefined;
20114
+ }
20115
+ super.destroySelf();
20116
+ }
20117
+ };
18971
20118
  defaultSelectKeybindings = [
18972
20119
  { name: "up", action: "move-up" },
18973
20120
  { name: "k", action: "move-up" },
@@ -20870,6 +22017,9 @@ __export(exports_cli, {
20870
22017
  });
20871
22018
  import { spawn } from "child_process";
20872
22019
  import { cwd as getCwd } from "process";
22020
+ function generateMessageId() {
22021
+ return `msg-${++messageIdCounter}`;
22022
+ }
20873
22023
  async function main2() {
20874
22024
  config = loadConfig2();
20875
22025
  history = loadHistory2();
@@ -21047,44 +22197,65 @@ function createMainUI() {
21047
22197
  id: "header-row",
21048
22198
  flexDirection: "row",
21049
22199
  width: "100%",
22200
+ alignItems: "center",
21050
22201
  marginBottom: 1
21051
22202
  });
21052
22203
  mainContainer.add(headerRow);
21053
22204
  headerText = new TextRenderable(renderer, {
21054
22205
  id: "header-text",
21055
- content: t`${bold(fg(theme.colors.primary)("magic-shell"))} ${fg(theme.colors.textMuted)("- natural language to terminal commands")}`,
22206
+ content: t`${bold(fg(theme.colors.primary)("magic-shell"))}`,
21056
22207
  flexGrow: 1
21057
22208
  });
21058
22209
  headerRow.add(headerText);
21059
- const statusRow = new BoxRenderable(renderer, {
21060
- id: "status-row",
21061
- flexDirection: "row",
21062
- width: "100%",
21063
- marginBottom: 1
22210
+ const modelBadge = new TextRenderable(renderer, {
22211
+ id: "model-badge",
22212
+ content: getModelDisplay()
21064
22213
  });
21065
- mainContainer.add(statusRow);
21066
- cwdText = new TextRenderable(renderer, {
21067
- id: "cwd-text",
21068
- content: t`${fg(theme.colors.textMuted)("cwd:")} ${fg(theme.colors.success)(currentCwd)}`,
21069
- flexGrow: 1
22214
+ headerRow.add(modelBadge);
22215
+ statusBarText = new TextRenderable(renderer, {
22216
+ id: "status-bar-text",
22217
+ content: getStatusBarContent(),
22218
+ marginBottom: 1
21070
22219
  });
21071
- statusRow.add(cwdText);
21072
- modelText = new TextRenderable(renderer, {
21073
- id: "model-text",
21074
- content: getModelDisplay()
22220
+ mainContainer.add(statusBarText);
22221
+ chatScrollBox = new ScrollBoxRenderable(renderer, {
22222
+ id: "chat-scroll-box",
22223
+ flexGrow: 1,
22224
+ width: "100%",
22225
+ scrollY: true,
22226
+ scrollX: false,
22227
+ stickyScroll: true,
22228
+ stickyStart: "bottom",
22229
+ rootOptions: {
22230
+ border: true,
22231
+ borderColor: theme.colors.border,
22232
+ borderStyle: "single"
22233
+ },
22234
+ viewportOptions: {
22235
+ backgroundColor: theme.colors.background,
22236
+ paddingLeft: 1,
22237
+ paddingRight: 1,
22238
+ paddingTop: 1
22239
+ },
22240
+ contentOptions: {
22241
+ flexDirection: "column",
22242
+ gap: 1
22243
+ }
21075
22244
  });
21076
- statusRow.add(modelText);
22245
+ mainContainer.add(chatScrollBox);
22246
+ addSystemMessage(getWelcomeMessage());
21077
22247
  const inputRow = new BoxRenderable(renderer, {
21078
22248
  id: "input-row",
21079
22249
  flexDirection: "row",
21080
22250
  width: "100%",
21081
- marginBottom: 1
22251
+ marginTop: 1,
22252
+ alignItems: "center"
21082
22253
  });
21083
22254
  mainContainer.add(inputRow);
21084
22255
  const promptText = new TextRenderable(renderer, {
21085
22256
  id: "prompt-text",
21086
- content: t`${fg(theme.colors.success)(">")} `,
21087
- width: 2
22257
+ content: t`${fg(theme.colors.primary)("~>")} `,
22258
+ width: 3
21088
22259
  });
21089
22260
  inputRow.add(promptText);
21090
22261
  inputField = new InputRenderable(renderer, {
@@ -21102,58 +22273,268 @@ function createMainUI() {
21102
22273
  }
21103
22274
  });
21104
22275
  inputRow.add(inputField);
21105
- commandPreview = new TextRenderable(renderer, {
21106
- id: "command-preview",
21107
- content: "",
21108
- marginBottom: 1
21109
- });
21110
- mainContainer.add(commandPreview);
21111
- safetyWarning = new TextRenderable(renderer, {
21112
- id: "safety-warning",
21113
- content: ""
22276
+ helpBarText = new TextRenderable(renderer, {
22277
+ id: "help-bar-text",
22278
+ content: getHelpBarContent(),
22279
+ marginTop: 1
21114
22280
  });
21115
- mainContainer.add(safetyWarning);
21116
- confirmPrompt = new BoxRenderable(renderer, {
21117
- id: "confirm-prompt",
22281
+ mainContainer.add(helpBarText);
22282
+ inputField.on(InputRenderableEvents.ENTER, handleInput);
22283
+ renderer.keyInput.on("keypress", handleKeypress);
22284
+ inputField.focus();
22285
+ }
22286
+ function getStatusBarContent() {
22287
+ const theme = getTheme2();
22288
+ const providerName = config.provider === "opencode-zen" ? "OpenCode Zen" : "OpenRouter";
22289
+ const safeModeIndicator = dryRunMode ? fg(theme.colors.warning)("[DRY RUN]") : fg(theme.colors.success)("Safe");
22290
+ return t`${fg(theme.colors.textMuted)("Provider:")} ${fg(theme.colors.text)(providerName)} ${fg(theme.colors.textMuted)("Model:")} ${fg(theme.colors.text)(currentModel.name)} ${safeModeIndicator}`;
22291
+ }
22292
+ function getHelpBarContent() {
22293
+ const theme = getTheme2();
22294
+ if (awaitingConfirmation) {
22295
+ return t`${fg(theme.colors.warning)("[Enter] Run")} ${fg(theme.colors.textMuted)("|")} ${fg(theme.colors.error)("[Esc] Cancel")} ${fg(theme.colors.textMuted)("|")} ${fg(theme.colors.primary)("[e] Edit")}`;
22296
+ }
22297
+ return t`${fg(theme.colors.textMuted)("Ctrl+X")} ${fg(theme.colors.primary)("P")}${fg(theme.colors.textMuted)(" Palette")} ${fg(theme.colors.primary)("M")}${fg(theme.colors.textMuted)(" Model")} ${fg(theme.colors.primary)("T")}${fg(theme.colors.textMuted)(" Theme")} ${fg(theme.colors.primary)("D")}${fg(theme.colors.textMuted)(" Dry-run")} ${fg(theme.colors.primary)("?")}${fg(theme.colors.textMuted)(" Help")}`;
22298
+ }
22299
+ function getWelcomeMessage() {
22300
+ const providerName = config.provider === "opencode-zen" ? "OpenCode Zen" : "OpenRouter";
22301
+ const freeNote = config.provider === "opencode-zen" ? `
22302
+ Free models: grok-code, glm-4.7-free` : "";
22303
+ return `Ready. Using ${providerName}.${freeNote}
22304
+ Type what you want to do, or press Ctrl+X P for command palette.`;
22305
+ }
22306
+ function addSystemMessage(content) {
22307
+ const msg = {
22308
+ id: generateMessageId(),
22309
+ type: "system",
22310
+ content,
22311
+ timestamp: Date.now()
22312
+ };
22313
+ chatMessages.push(msg);
22314
+ renderMessage(msg);
22315
+ return msg;
22316
+ }
22317
+ function addUserMessage(content) {
22318
+ const msg = {
22319
+ id: generateMessageId(),
22320
+ type: "user",
22321
+ content,
22322
+ timestamp: Date.now()
22323
+ };
22324
+ chatMessages.push(msg);
22325
+ renderMessage(msg);
22326
+ return msg;
22327
+ }
22328
+ function addAssistantMessage(content, command, safety) {
22329
+ const msg = {
22330
+ id: generateMessageId(),
22331
+ type: "assistant",
22332
+ content,
22333
+ command,
22334
+ safety,
22335
+ timestamp: Date.now(),
22336
+ executed: false
22337
+ };
22338
+ chatMessages.push(msg);
22339
+ renderMessage(msg);
22340
+ return msg;
22341
+ }
22342
+ function addResultMessage(content, exitCode) {
22343
+ const msg = {
22344
+ id: generateMessageId(),
22345
+ type: "result",
22346
+ content,
22347
+ timestamp: Date.now(),
22348
+ exitCode
22349
+ };
22350
+ chatMessages.push(msg);
22351
+ renderMessage(msg);
22352
+ return msg;
22353
+ }
22354
+ function renderMessage(msg) {
22355
+ const theme = getTheme2();
22356
+ const msgBox = createMessageRenderable(msg, theme);
22357
+ chatScrollBox.add(msgBox);
22358
+ }
22359
+ function createMessageRenderable(msg, theme) {
22360
+ switch (msg.type) {
22361
+ case "user":
22362
+ return createUserMessageRenderable(msg, theme);
22363
+ case "assistant":
22364
+ return createAssistantMessageRenderable(msg, theme);
22365
+ case "result":
22366
+ return createResultMessageRenderable(msg, theme);
22367
+ case "system":
22368
+ default:
22369
+ return createSystemMessageRenderable(msg, theme);
22370
+ }
22371
+ }
22372
+ function createUserMessageRenderable(msg, theme) {
22373
+ const box = new BoxRenderable(renderer, {
22374
+ id: `msg-${msg.id}`,
21118
22375
  flexDirection: "row",
21119
- visible: false,
21120
- marginBottom: 1
22376
+ width: "100%"
21121
22377
  });
21122
- mainContainer.add(confirmPrompt);
21123
- const confirmText = new TextRenderable(renderer, {
21124
- id: "confirm-text",
21125
- content: t`${fg(theme.colors.warning)("[Enter] Execute")} ${fg(theme.colors.textMuted)("|")} ${fg(theme.colors.error)("[Esc] Cancel")} ${fg(theme.colors.textMuted)("|")} ${fg(theme.colors.primary)("[e] Edit")}`
22378
+ const text = new TextRenderable(renderer, {
22379
+ id: `msg-${msg.id}-text`,
22380
+ content: t`${fg(theme.colors.success)(">")} ${fg(theme.colors.text)(msg.content)}`
21126
22381
  });
21127
- confirmPrompt.add(confirmText);
21128
- outputContainer = new BoxRenderable(renderer, {
21129
- id: "output-container",
21130
- flexGrow: 1,
22382
+ box.add(text);
22383
+ return box;
22384
+ }
22385
+ function createAssistantMessageRenderable(msg, theme) {
22386
+ const isSelected = pendingMessageId === msg.id;
22387
+ const card = new BoxRenderable(renderer, {
22388
+ id: `msg-${msg.id}`,
22389
+ flexDirection: "column",
22390
+ width: "100%",
21131
22391
  border: true,
21132
- borderColor: theme.colors.border,
22392
+ borderColor: isSelected ? theme.colors.primary : theme.colors.border,
21133
22393
  borderStyle: "single",
21134
- title: "Output",
21135
- padding: 1
22394
+ paddingLeft: 1,
22395
+ paddingRight: 1,
22396
+ paddingTop: 0,
22397
+ paddingBottom: 0,
22398
+ backgroundColor: theme.colors.backgroundPanel
21136
22399
  });
21137
- mainContainer.add(outputContainer);
21138
- const providerName = config.provider === "opencode-zen" ? "OpenCode Zen" : "OpenRouter";
21139
- const freeModelsNote = config.provider === "opencode-zen" ? `
21140
- ${fg(theme.colors.success)("Free models available!")} Try: grok-code, glm-4.7-free` : "";
21141
- outputText = new TextRenderable(renderer, {
21142
- id: "output-text",
21143
- content: t`${fg(theme.colors.textMuted)(`Ready. Using ${providerName}.`)}${freeModelsNote}
21144
-
21145
- ${fg(theme.colors.textMuted)("Type what you want to do, or press")} ${fg(theme.colors.primary)("Ctrl+X P")} ${fg(theme.colors.textMuted)("for command palette.")}`
22400
+ const commandText = new TextRenderable(renderer, {
22401
+ id: `msg-${msg.id}-cmd`,
22402
+ content: t`${fg(theme.colors.textMuted)("Command:")} ${fg(theme.colors.text)(msg.command || "")}`
21146
22403
  });
21147
- outputContainer.add(outputText);
21148
- statusText = new TextRenderable(renderer, {
21149
- id: "status-text",
21150
- content: getDryRunStatus(),
21151
- marginTop: 1
22404
+ card.add(commandText);
22405
+ if (msg.safety) {
22406
+ const severityColor = getSeverityColor(msg.safety.severity);
22407
+ const severityText = msg.safety.isDangerous ? `${msg.safety.severity.toUpperCase()} risk${msg.safety.reason ? ` - ${msg.safety.reason}` : ""}` : "Low risk";
22408
+ const safetyText = new TextRenderable(renderer, {
22409
+ id: `msg-${msg.id}-safety`,
22410
+ content: t`${fg(severityColor)("●")} ${fg(theme.colors.textMuted)(severityText)}`
22411
+ });
22412
+ card.add(safetyText);
22413
+ }
22414
+ if (isSelected && !msg.executed) {
22415
+ const actionsText = new TextRenderable(renderer, {
22416
+ id: `msg-${msg.id}-actions`,
22417
+ content: t`${fg(theme.colors.warning)("[Enter]")} ${fg(theme.colors.textMuted)("Run")} ${fg(theme.colors.primary)("[c]")} ${fg(theme.colors.textMuted)("Copy")} ${fg(theme.colors.primary)("[e]")} ${fg(theme.colors.textMuted)("Edit")}`
22418
+ });
22419
+ card.add(actionsText);
22420
+ }
22421
+ if (msg.executed) {
22422
+ const execText = new TextRenderable(renderer, {
22423
+ id: `msg-${msg.id}-exec`,
22424
+ content: t`${fg(theme.colors.success)("Executed")}`
22425
+ });
22426
+ card.add(execText);
22427
+ }
22428
+ return card;
22429
+ }
22430
+ function createResultMessageRenderable(msg, theme) {
22431
+ const isSuccess = msg.exitCode === undefined || msg.exitCode === 0;
22432
+ const isExpanded = msg.expanded ?? false;
22433
+ const hasOutput = msg.content && msg.content.trim().length > 0;
22434
+ const outputLines = hasOutput ? msg.content.trim().split(`
22435
+ `) : [];
22436
+ const isLongOutput = outputLines.length > 5;
22437
+ const PREVIEW_LINES = 3;
22438
+ const card = new BoxRenderable(renderer, {
22439
+ id: `msg-${msg.id}`,
22440
+ flexDirection: "column",
22441
+ width: "100%",
22442
+ border: true,
22443
+ borderColor: isSuccess ? theme.colors.success : theme.colors.error,
22444
+ borderStyle: "single",
22445
+ paddingLeft: 1,
22446
+ paddingRight: 1,
22447
+ backgroundColor: theme.colors.backgroundPanel,
22448
+ onMouseDown: isLongOutput ? () => {
22449
+ toggleResultExpand(msg.id);
22450
+ } : undefined
21152
22451
  });
21153
- mainContainer.add(statusText);
21154
- inputField.on(InputRenderableEvents.ENTER, handleInput);
21155
- renderer.keyInput.on("keypress", handleKeypress);
21156
- inputField.focus();
22452
+ const statusIcon = isSuccess ? "✓" : "✗";
22453
+ const statusColor = isSuccess ? theme.colors.success : theme.colors.error;
22454
+ const statusLabel = isSuccess ? "Executed successfully" : `Exit code: ${msg.exitCode}`;
22455
+ const expandIcon = isLongOutput ? isExpanded ? "▼" : "▶" : "";
22456
+ const lineCount = isLongOutput ? ` (${outputLines.length} lines)` : "";
22457
+ const statusText = new TextRenderable(renderer, {
22458
+ id: `msg-${msg.id}-status`,
22459
+ content: t`${fg(statusColor)(statusIcon)} ${fg(theme.colors.text)(statusLabel)}${fg(theme.colors.textMuted)(lineCount)} ${fg(theme.colors.primary)(expandIcon)}`
22460
+ });
22461
+ card.add(statusText);
22462
+ if (hasOutput) {
22463
+ let displayContent;
22464
+ if (isExpanded || !isLongOutput) {
22465
+ displayContent = msg.content.trim();
22466
+ } else {
22467
+ const previewLines = outputLines.slice(0, PREVIEW_LINES);
22468
+ displayContent = previewLines.join(`
22469
+ `) + `
22470
+ ... ${outputLines.length - PREVIEW_LINES} more lines`;
22471
+ }
22472
+ const outputText = new TextRenderable(renderer, {
22473
+ id: `msg-${msg.id}-output`,
22474
+ content: t`${fg(theme.colors.textMuted)(displayContent)}`
22475
+ });
22476
+ card.add(outputText);
22477
+ if (isLongOutput) {
22478
+ const hintText = new TextRenderable(renderer, {
22479
+ id: `msg-${msg.id}-hint`,
22480
+ content: t`${fg(theme.colors.primary)("[o]")} ${fg(theme.colors.textMuted)(isExpanded ? "Collapse" : "Expand output")}`
22481
+ });
22482
+ card.add(hintText);
22483
+ }
22484
+ }
22485
+ return card;
22486
+ }
22487
+ function createSystemMessageRenderable(msg, theme) {
22488
+ const box = new BoxRenderable(renderer, {
22489
+ id: `msg-${msg.id}`,
22490
+ flexDirection: "column",
22491
+ width: "100%"
22492
+ });
22493
+ const text = new TextRenderable(renderer, {
22494
+ id: `msg-${msg.id}-text`,
22495
+ content: t`${fg(theme.colors.textMuted)(msg.content)}`
22496
+ });
22497
+ box.add(text);
22498
+ return box;
22499
+ }
22500
+ function updateAssistantMessage(msgId, updates) {
22501
+ const msgIndex = chatMessages.findIndex((m) => m.id === msgId);
22502
+ if (msgIndex === -1)
22503
+ return;
22504
+ const msg = chatMessages[msgIndex];
22505
+ Object.assign(msg, updates);
22506
+ chatScrollBox.remove(`msg-${msgId}`);
22507
+ const theme = getTheme2();
22508
+ const newBox = createMessageRenderable(msg, theme);
22509
+ chatScrollBox.add(newBox);
22510
+ }
22511
+ function updateResultMessage(msgId, updates) {
22512
+ const msgIndex = chatMessages.findIndex((m) => m.id === msgId);
22513
+ if (msgIndex === -1)
22514
+ return;
22515
+ const msg = chatMessages[msgIndex];
22516
+ Object.assign(msg, updates);
22517
+ chatScrollBox.remove(`msg-${msgId}`);
22518
+ const theme = getTheme2();
22519
+ const newBox = createMessageRenderable(msg, theme);
22520
+ chatScrollBox.add(newBox);
22521
+ }
22522
+ function toggleResultExpand(msgId) {
22523
+ const msg = chatMessages.find((m) => m.id === msgId);
22524
+ if (!msg || msg.type !== "result")
22525
+ return;
22526
+ const outputLines = msg.content?.trim().split(`
22527
+ `) || [];
22528
+ if (outputLines.length <= 5)
22529
+ return;
22530
+ updateResultMessage(msgId, { expanded: !msg.expanded });
22531
+ }
22532
+ function toggleLastResultExpand() {
22533
+ const resultMessages = chatMessages.filter((m) => m.type === "result");
22534
+ if (resultMessages.length === 0)
22535
+ return;
22536
+ const lastResult = resultMessages[resultMessages.length - 1];
22537
+ toggleResultExpand(lastResult.id);
21157
22538
  }
21158
22539
  function getModelDisplay() {
21159
22540
  const theme = getTheme2();
@@ -21162,30 +22543,22 @@ function getModelDisplay() {
21162
22543
  const freeBadge = currentModel.free ? fg(theme.colors.success)(" FREE") : "";
21163
22544
  return t`${providerBadge} ${fg(categoryColor)(currentModel.name)}${freeBadge}`;
21164
22545
  }
21165
- function getDryRunStatus() {
21166
- const theme = getTheme2();
21167
- if (dryRunMode) {
21168
- return t`${fg(theme.colors.warning)("[DRY RUN]")} ${fg(theme.colors.textMuted)("Ctrl+X P palette | Ctrl+X M model | Ctrl+X D dry-run")}`;
21169
- }
21170
- return t`${fg(theme.colors.textMuted)("Ctrl+X P palette | Ctrl+X M model | Ctrl+X ? help")}`;
21171
- }
21172
22546
  function refreshThemeColors() {
21173
22547
  const theme = getTheme2();
21174
22548
  renderer.setBackgroundColor(theme.colors.background);
21175
22549
  if (headerText) {
21176
- headerText.content = t`${bold(fg(theme.colors.primary)("magic-shell"))} ${fg(theme.colors.textMuted)("- natural language to terminal commands")}`;
21177
- }
21178
- if (cwdText) {
21179
- cwdText.content = t`${fg(theme.colors.textMuted)("cwd:")} ${fg(theme.colors.success)(currentCwd)}`;
22550
+ headerText.content = t`${bold(fg(theme.colors.primary)("magic-shell"))}`;
21180
22551
  }
21181
- if (modelText) {
21182
- modelText.content = getModelDisplay();
22552
+ if (statusBarText) {
22553
+ statusBarText.content = getStatusBarContent();
21183
22554
  }
21184
- if (statusText) {
21185
- statusText.content = getDryRunStatus();
22555
+ if (helpBarText) {
22556
+ helpBarText.content = getHelpBarContent();
21186
22557
  }
21187
- if (outputContainer) {
21188
- outputContainer.borderColor = theme.colors.border;
22558
+ if (chatScrollBox) {
22559
+ chatScrollBox.rootOptions = {
22560
+ borderColor: theme.colors.border
22561
+ };
21189
22562
  }
21190
22563
  if (inputField) {
21191
22564
  inputField.focusedBackgroundColor = theme.colors.backgroundPanel;
@@ -21193,14 +22566,6 @@ function refreshThemeColors() {
21193
22566
  inputField.placeholderColor = theme.colors.textMuted;
21194
22567
  inputField.cursorColor = theme.colors.primary;
21195
22568
  }
21196
- const providerName = config.provider === "opencode-zen" ? "OpenCode Zen" : "OpenRouter";
21197
- const freeModelsNote = config.provider === "opencode-zen" ? `
21198
- ${fg(theme.colors.success)("Free models available!")} Try: grok-code, glm-4.7-free` : "";
21199
- if (outputText) {
21200
- outputText.content = t`${fg(theme.colors.textMuted)(`Ready. Using ${providerName}.`)}${freeModelsNote}
21201
-
21202
- ${fg(theme.colors.textMuted)("Type what you want to do, or press")} ${fg(theme.colors.primary)("Ctrl+X P")} ${fg(theme.colors.textMuted)("for command palette.")}`;
21203
- }
21204
22569
  }
21205
22570
  async function handleInput(value) {
21206
22571
  const input = value.trim();
@@ -21211,8 +22576,9 @@ async function handleInput(value) {
21211
22576
  await handleSpecialCommand(input);
21212
22577
  return;
21213
22578
  }
22579
+ addUserMessage(input);
21214
22580
  if (isDirectCommand(input)) {
21215
- await processCommand(input, input);
22581
+ await processDirectCommand(input, input);
21216
22582
  return;
21217
22583
  }
21218
22584
  await translateAndProcess(input);
@@ -21246,38 +22612,50 @@ function isDirectCommand(input) {
21246
22612
  async function translateAndProcess(input) {
21247
22613
  const apiKey = await getApiKey2(config.provider);
21248
22614
  if (!apiKey) {
21249
- setOutput(t`${fg("#ef4444")("Error: No API key configured. Run !provider to set up.")}`);
22615
+ addSystemMessage("Error: No API key configured. Run !provider to set up.");
21250
22616
  return;
21251
22617
  }
21252
- setOutput(t`${fg("#64748b")("Translating...")}`);
22618
+ const loadingMsg = addSystemMessage("Translating...");
21253
22619
  try {
21254
22620
  const command = await translateToCommand2(apiKey, currentModel, input, currentCwd, history);
21255
- commandPreview.content = t`${fg("#64748b")("Command:")} ${fg("#f8fafc")(command)}`;
22621
+ chatScrollBox.remove(`msg-${loadingMsg.id}`);
22622
+ chatMessages = chatMessages.filter((m) => m.id !== loadingMsg.id);
21256
22623
  const safety = analyzeCommand2(command, config);
22624
+ const assistantMsg = addAssistantMessage(input, command, safety);
21257
22625
  if (safety.isDangerous) {
21258
- safetyWarning.content = t`${fg(getSeverityColor(safety.severity))(`[${safety.severity.toUpperCase()}] ${safety.reason}`)}`;
21259
- pendingCommand = command;
22626
+ pendingMessageId = assistantMsg.id;
21260
22627
  awaitingConfirmation = true;
21261
- confirmPrompt.visible = true;
21262
- setOutput(t`${fg("#fbbf24")("Command requires confirmation. Press Enter to execute or Esc to cancel.")}`);
22628
+ helpBarText.content = getHelpBarContent();
21263
22629
  } else {
21264
- safetyWarning.content = "";
21265
- await processCommand(input, command);
22630
+ await executeAndShowResult(input, command, assistantMsg.id);
21266
22631
  }
21267
22632
  } catch (error) {
22633
+ chatScrollBox.remove(`msg-${loadingMsg.id}`);
22634
+ chatMessages = chatMessages.filter((m) => m.id !== loadingMsg.id);
21268
22635
  const message = error instanceof Error ? error.message : String(error);
21269
- setOutput(t`${fg("#ef4444")(`Error: ${message}`)}`);
22636
+ addSystemMessage(`Error: ${message}`);
21270
22637
  }
21271
22638
  }
21272
- async function processCommand(input, command) {
22639
+ async function processDirectCommand(input, command) {
22640
+ const safety = analyzeCommand2(command, config);
22641
+ const assistantMsg = addAssistantMessage(input, command, safety);
22642
+ if (safety.isDangerous) {
22643
+ pendingMessageId = assistantMsg.id;
22644
+ awaitingConfirmation = true;
22645
+ helpBarText.content = getHelpBarContent();
22646
+ } else {
22647
+ await executeAndShowResult(input, command, assistantMsg.id);
22648
+ }
22649
+ }
22650
+ async function executeAndShowResult(input, command, assistantMsgId) {
21273
22651
  if (command.startsWith("cd ")) {
21274
22652
  const path2 = command.slice(3).trim().replace(/^["']|["']$/g, "");
21275
22653
  try {
21276
22654
  const expandedPath = path2.startsWith("~") ? path2.replace("~", process.env.HOME || "") : path2;
21277
22655
  process.chdir(expandedPath);
21278
22656
  currentCwd = getCwd();
21279
- cwdText.content = t`${fg("#64748b")("cwd:")} ${fg("#22c55e")(currentCwd)}`;
21280
- setOutput(t`${fg("#22c55e")(`Changed directory to ${currentCwd}`)}`);
22657
+ statusBarText.content = getStatusBarContent();
22658
+ addResultMessage(`Changed directory to ${currentCwd}`, 0);
21281
22659
  addToHistory({
21282
22660
  input,
21283
22661
  command,
@@ -21285,35 +22663,37 @@ async function processCommand(input, command) {
21285
22663
  timestamp: Date.now()
21286
22664
  });
21287
22665
  history = loadHistory2();
22666
+ updateAssistantMessage(assistantMsgId, { executed: true });
21288
22667
  } catch (err) {
21289
- setOutput(t`${fg("#ef4444")(`cd: ${err instanceof Error ? err.message : String(err)}`)}`);
22668
+ addResultMessage(`cd: ${err instanceof Error ? err.message : String(err)}`, 1);
21290
22669
  }
21291
22670
  clearCommandState();
21292
22671
  return;
21293
22672
  }
21294
22673
  if (dryRunMode) {
21295
- setOutput(t`${fg("#fbbf24")("[DRY RUN]")} Would execute: ${fg("#f8fafc")(command)}`);
22674
+ addResultMessage(`[DRY RUN] Would execute: ${command}`, 0);
22675
+ updateAssistantMessage(assistantMsgId, { executed: true });
21296
22676
  clearCommandState();
21297
22677
  return;
21298
22678
  }
21299
- setOutput(t`${fg("#64748b")("Executing...")}`);
21300
22679
  try {
21301
- const result = await executeCommand(command);
21302
- setOutput(result || t`${fg("#22c55e")("Command completed successfully")}`);
22680
+ const { output, exitCode } = await executeCommandWithCode(command);
22681
+ addResultMessage(output || "Command completed successfully", exitCode);
21303
22682
  addToHistory({
21304
22683
  input,
21305
22684
  command,
21306
- output: result.slice(0, 500),
22685
+ output: output.slice(0, 500),
21307
22686
  timestamp: Date.now()
21308
22687
  });
21309
22688
  history = loadHistory2();
22689
+ updateAssistantMessage(assistantMsgId, { executed: true });
21310
22690
  } catch (error) {
21311
22691
  const message = error instanceof Error ? error.message : String(error);
21312
- setOutput(t`${fg("#ef4444")(`Error: ${message}`)}`);
22692
+ addResultMessage(`Error: ${message}`, 1);
21313
22693
  }
21314
22694
  clearCommandState();
21315
22695
  }
21316
- function executeCommand(command) {
22696
+ function executeCommandWithCode(command) {
21317
22697
  return new Promise((resolve3, reject) => {
21318
22698
  const child = spawn(command, {
21319
22699
  shell: true,
@@ -21332,23 +22712,16 @@ function executeCommand(command) {
21332
22712
  reject(error);
21333
22713
  });
21334
22714
  child.on("close", (code) => {
21335
- if (code === 0) {
21336
- resolve3(stdout || stderr);
21337
- } else {
21338
- resolve3(stderr || stdout || `Command exited with code ${code}`);
21339
- }
22715
+ const exitCode = code ?? 0;
22716
+ const output = stdout || stderr || (exitCode === 0 ? "" : `Command exited with code ${exitCode}`);
22717
+ resolve3({ output, exitCode });
21340
22718
  });
21341
22719
  });
21342
22720
  }
21343
22721
  function clearCommandState() {
21344
- pendingCommand = null;
22722
+ pendingMessageId = null;
21345
22723
  awaitingConfirmation = false;
21346
- confirmPrompt.visible = false;
21347
- commandPreview.content = "";
21348
- safetyWarning.content = "";
21349
- }
21350
- function setOutput(content) {
21351
- outputText.content = content;
22724
+ helpBarText.content = getHelpBarContent();
21352
22725
  }
21353
22726
  async function handleSpecialCommand(input) {
21354
22727
  const cmd = input.slice(1).toLowerCase().trim();
@@ -21364,8 +22737,8 @@ async function handleSpecialCommand(input) {
21364
22737
  break;
21365
22738
  case "dry":
21366
22739
  dryRunMode = !dryRunMode;
21367
- statusText.content = getDryRunStatus();
21368
- setOutput(t`${fg("#22c55e")(`Dry-run mode: ${dryRunMode ? "ON" : "OFF"}`)}`);
22740
+ statusBarText.content = getStatusBarContent();
22741
+ addSystemMessage(`Dry-run mode: ${dryRunMode ? "ON" : "OFF"}`);
21369
22742
  break;
21370
22743
  case "config":
21371
22744
  await showConfig();
@@ -21374,65 +22747,72 @@ async function handleSpecialCommand(input) {
21374
22747
  showHistory();
21375
22748
  break;
21376
22749
  case "clear":
21377
- setOutput("");
22750
+ clearChat();
21378
22751
  break;
21379
22752
  default:
21380
22753
  if (cmd) {
21381
- await processCommand(input, cmd);
22754
+ addUserMessage(cmd);
22755
+ await processDirectCommand(input, cmd);
21382
22756
  }
21383
22757
  }
21384
22758
  }
22759
+ function clearChat() {
22760
+ for (const msg of chatMessages) {
22761
+ chatScrollBox.remove(`msg-${msg.id}`);
22762
+ }
22763
+ chatMessages = [];
22764
+ addSystemMessage(getWelcomeMessage());
22765
+ }
21385
22766
  function showHelp() {
21386
- const theme = getTheme2();
21387
- setOutput(t`${bold(fg(theme.colors.primary)("Magic Shell"))}
22767
+ const helpText = `Keyboard Shortcuts (Ctrl+X then...):
22768
+ P Command palette M Change model
22769
+ S Switch provider D Toggle dry-run
22770
+ T Change theme H Show history
22771
+ C Show config L Clear chat
22772
+ ? This help Q Exit
21388
22773
 
21389
- ${bold(fg(theme.colors.textMuted)("Keyboard Shortcuts (Ctrl+X then...):"))}
21390
- ${fg(theme.colors.primary)("P")} ${fg(theme.colors.textMuted)("Command palette")} ${fg(theme.colors.primary)("M")} ${fg(theme.colors.textMuted)("Change model")}
21391
- ${fg(theme.colors.primary)("S")} ${fg(theme.colors.textMuted)("Switch provider")} ${fg(theme.colors.primary)("D")} ${fg(theme.colors.textMuted)("Toggle dry-run")}
21392
- ${fg(theme.colors.primary)("T")} ${fg(theme.colors.textMuted)("Change theme")} ${fg(theme.colors.primary)("H")} ${fg(theme.colors.textMuted)("Show history")}
21393
- ${fg(theme.colors.primary)("C")} ${fg(theme.colors.textMuted)("Show config")} ${fg(theme.colors.primary)("L")} ${fg(theme.colors.textMuted)("Clear output")}
21394
- ${fg(theme.colors.primary)("?")} ${fg(theme.colors.textMuted)("This help")} ${fg(theme.colors.primary)("Q")} ${fg(theme.colors.textMuted)("Exit")}
22774
+ Other:
22775
+ Ctrl+C Exit / Cancel Esc Close palette
21395
22776
 
21396
- ${bold(fg(theme.colors.textMuted)("Other:"))}
21397
- ${fg(theme.colors.primary)("Ctrl+C")} ${fg(theme.colors.textMuted)("Exit / Cancel")} ${fg(theme.colors.primary)("Esc")} ${fg(theme.colors.textMuted)("Close palette")}
21398
-
21399
- ${bold(fg(theme.colors.textMuted)("Tips:"))}
22777
+ Tips:
21400
22778
  - Type naturally: "list all files" -> ls -la
21401
22779
  - Reference history: "do that again", "undo"
21402
- - ${fg(theme.colors.success)("Free models:")} gpt-5-nano, grok-code, glm-4.7-free`);
22780
+ - Free models: gpt-5-nano, grok-code, glm-4.7-free`;
22781
+ addSystemMessage(helpText);
21403
22782
  }
21404
22783
  async function showConfig() {
21405
22784
  const theme = getTheme2();
21406
22785
  const providerName = config.provider === "opencode-zen" ? "OpenCode Zen" : "OpenRouter";
21407
22786
  const apiKey = await getApiKey2(config.provider);
21408
- const apiKeyStatus = apiKey ? fg(theme.colors.success)("configured") : fg(theme.colors.error)("not set");
21409
- const freeBadge = currentModel.free ? fg(theme.colors.success)(" (FREE)") : "";
22787
+ const apiKeyStatus = apiKey ? "configured" : "not set";
22788
+ const freeBadge = currentModel.free ? " (FREE)" : "";
21410
22789
  const shellInfo = getShellInfo2();
21411
- setOutput(t`${bold(fg(theme.colors.primary)("Current Configuration"))}
22790
+ const configText = `Current Configuration
21412
22791
 
21413
- ${fg(theme.colors.textMuted)("Provider:")} ${fg(theme.colors.text)(providerName)}
21414
- ${fg(theme.colors.textMuted)("Model:")} ${fg(theme.colors.text)(currentModel.name)}${freeBadge}
21415
- ${fg(theme.colors.textMuted)("Model ID:")} ${fg(theme.colors.textMuted)(currentModel.id)}
21416
- ${fg(theme.colors.textMuted)("Category:")} ${fg(theme.colors.text)(currentModel.category)}
21417
- ${fg(theme.colors.textMuted)("Theme:")} ${fg(theme.colors.text)(theme.name)}
21418
- ${fg(theme.colors.textMuted)("Shell:")} ${fg(theme.colors.text)(shellInfo.shell)} ${fg(theme.colors.textMuted)(`(${shellInfo.shellPath})`)}
21419
- ${fg(theme.colors.textMuted)("Platform:")} ${fg(theme.colors.text)(shellInfo.platform)}${shellInfo.isWSL ? fg(theme.colors.textMuted)(" (WSL)") : ""}
21420
- ${fg(theme.colors.textMuted)("Safety:")} ${fg(theme.colors.text)(config.safetyLevel)}
21421
- ${fg(theme.colors.textMuted)("Dry-run:")} ${fg(theme.colors.text)(dryRunMode ? "ON" : "OFF")}
21422
- ${fg(theme.colors.textMuted)("API Key:")} ${apiKeyStatus}
21423
- ${fg(theme.colors.textMuted)("History:")} ${fg(theme.colors.text)(`${history.length} commands`)}`);
22792
+ Provider: ${providerName}
22793
+ Model: ${currentModel.name}${freeBadge}
22794
+ Model ID: ${currentModel.id}
22795
+ Category: ${currentModel.category}
22796
+ Theme: ${theme.name}
22797
+ Shell: ${shellInfo.shell} (${shellInfo.shellPath})
22798
+ Platform: ${shellInfo.platform}${shellInfo.isWSL ? " (WSL)" : ""}
22799
+ Safety: ${config.safetyLevel}
22800
+ Dry-run: ${dryRunMode ? "ON" : "OFF"}
22801
+ API Key: ${apiKeyStatus}
22802
+ History: ${history.length} commands`;
22803
+ addSystemMessage(configText);
21424
22804
  }
21425
22805
  function showHistory() {
21426
22806
  if (history.length === 0) {
21427
- setOutput(t`${fg("#64748b")("No command history yet.")}`);
22807
+ addSystemMessage("No command history yet.");
21428
22808
  return;
21429
22809
  }
21430
22810
  const recent = history.slice(-10);
21431
22811
  const lines = recent.map((entry, i) => {
21432
22812
  const date = new Date(entry.timestamp).toLocaleTimeString();
21433
- return t`${fg("#64748b")(`${i + 1}.`)} ${fg("#94a3b8")(`[${date}]`)} ${fg("#f8fafc")(entry.command)}`;
22813
+ return `${i + 1}. [${date}] ${entry.command}`;
21434
22814
  });
21435
- setOutput(t`${bold(fg("#60a5fa")("Recent Command History"))}
22815
+ addSystemMessage(`Recent Command History
21436
22816
 
21437
22817
  ${lines.join(`
21438
22818
  `)}`);
@@ -21498,10 +22878,10 @@ async function switchProvider() {
21498
22878
  currentModel = models.find((m) => m.id === config.defaultModel) || models[0];
21499
22879
  config.defaultModel = currentModel.id;
21500
22880
  saveConfig2(config);
21501
- modelText.content = getModelDisplay();
22881
+ statusBarText.content = getStatusBarContent();
21502
22882
  closeSelector();
21503
22883
  const providerName = newProvider === "opencode-zen" ? "OpenCode Zen" : "OpenRouter";
21504
- setOutput(t`${fg("#22c55e")(`Switched to ${providerName}. Model: ${currentModel.name}`)}`);
22884
+ addSystemMessage(`Switched to ${providerName}. Model: ${currentModel.name}`);
21505
22885
  } else {
21506
22886
  closeSelector();
21507
22887
  renderer.root.remove("main-container");
@@ -21567,12 +22947,12 @@ function showModelSelector() {
21567
22947
  currentModel = option.value;
21568
22948
  config.defaultModel = currentModel.id;
21569
22949
  saveConfig2(config);
21570
- modelText.content = getModelDisplay();
22950
+ statusBarText.content = getStatusBarContent();
21571
22951
  renderer.root.remove("model-selector-container");
21572
22952
  modelSelector = null;
21573
22953
  inputField.focus();
21574
22954
  const freeBadge = currentModel.free ? " (FREE)" : "";
21575
- setOutput(t`${fg("#22c55e")(`Model changed to ${currentModel.name}${freeBadge}`)}`);
22955
+ addSystemMessage(`Model changed to ${currentModel.name}${freeBadge}`);
21576
22956
  });
21577
22957
  modelSelector.focus();
21578
22958
  }
@@ -21629,8 +23009,7 @@ function showThemeSelector() {
21629
23009
  renderer.root.remove("theme-selector-container");
21630
23010
  themeSelector = null;
21631
23011
  refreshThemeColors();
21632
- const newTheme = getTheme2();
21633
- setOutput(t`${fg(newTheme.colors.success)(`Theme changed to ${themeName}`)}`);
23012
+ addSystemMessage(`Theme changed to ${themeName}`);
21634
23013
  inputField.focus();
21635
23014
  });
21636
23015
  const escHandler = (key) => {
@@ -21674,8 +23053,8 @@ function getCommandPaletteOptions() {
21674
23053
  chord: "d",
21675
23054
  action: () => {
21676
23055
  dryRunMode = !dryRunMode;
21677
- statusText.content = getDryRunStatus();
21678
- setOutput(t`${fg("#22c55e")(`Dry-run mode: ${dryRunMode ? "ON" : "OFF"}`)}`);
23056
+ statusBarText.content = getStatusBarContent();
23057
+ addSystemMessage(`Dry-run mode: ${dryRunMode ? "ON" : "OFF"}`);
21679
23058
  }
21680
23059
  },
21681
23060
  {
@@ -21700,11 +23079,11 @@ function getCommandPaletteOptions() {
21700
23079
  action: () => showThemeSelector()
21701
23080
  },
21702
23081
  {
21703
- name: "Clear Output",
21704
- description: "Clear the output area",
23082
+ name: "Clear Chat",
23083
+ description: "Clear the chat history",
21705
23084
  key: "l",
21706
23085
  chord: "l",
21707
- action: () => setOutput("")
23086
+ action: () => clearChat()
21708
23087
  },
21709
23088
  {
21710
23089
  name: "Show Help",
@@ -21845,24 +23224,44 @@ function handleKeypress(key) {
21845
23224
  inputField.focus();
21846
23225
  return;
21847
23226
  }
21848
- if (awaitingConfirmation) {
23227
+ if (awaitingConfirmation && pendingMessageId) {
21849
23228
  clearCommandState();
21850
- setOutput(t`${fg("#64748b")("Command cancelled.")}`);
23229
+ addSystemMessage("Command cancelled.");
21851
23230
  inputField.focus();
21852
23231
  }
21853
23232
  }
21854
- if (key.name === "return" && awaitingConfirmation && pendingCommand) {
21855
- const cmd = pendingCommand;
21856
- clearCommandState();
21857
- processCommand("", cmd);
23233
+ if (key.name === "return" && awaitingConfirmation && pendingMessageId) {
23234
+ const msg = chatMessages.find((m) => m.id === pendingMessageId);
23235
+ if (msg && msg.command) {
23236
+ const command = msg.command;
23237
+ const msgId = pendingMessageId;
23238
+ clearCommandState();
23239
+ executeAndShowResult(msg.content, command, msgId);
23240
+ }
21858
23241
  }
21859
- if (key.name === "e" && awaitingConfirmation && pendingCommand) {
21860
- inputField.value = pendingCommand;
21861
- clearCommandState();
21862
- inputField.focus();
23242
+ if (key.name === "e" && awaitingConfirmation && pendingMessageId) {
23243
+ const msg = chatMessages.find((m) => m.id === pendingMessageId);
23244
+ if (msg && msg.command) {
23245
+ inputField.value = msg.command;
23246
+ clearCommandState();
23247
+ inputField.focus();
23248
+ }
23249
+ }
23250
+ if (key.name === "c" && awaitingConfirmation && pendingMessageId) {
23251
+ const msg = chatMessages.find((m) => m.id === pendingMessageId);
23252
+ if (msg && msg.command) {
23253
+ const copyCmd = process.platform === "darwin" ? "pbcopy" : "xclip -selection clipboard";
23254
+ const child = spawn(copyCmd, { shell: true });
23255
+ child.stdin?.write(msg.command);
23256
+ child.stdin?.end();
23257
+ addSystemMessage(`Copied to clipboard: ${msg.command}`);
23258
+ }
23259
+ }
23260
+ if (key.name === "o" && !awaitingConfirmation && !commandPalette && !modelSelector) {
23261
+ toggleLastResultExpand();
21863
23262
  }
21864
23263
  }
21865
- var renderer, currentModel, config, history, currentCwd, dryRunMode = false, mainContainer, headerText, cwdText, modelText, inputField, outputContainer, outputText, statusText, commandPreview, safetyWarning, confirmPrompt, modelSelector = null, providerSelector = null, pendingCommand = null, awaitingConfirmation = false, themeSelector = null, commandPalette = null, chordMode = "none", cli_default;
23264
+ var renderer, currentModel, config, history, currentCwd, dryRunMode = false, chatMessages, messageIdCounter = 0, mainContainer, headerText, statusBarText, chatScrollBox, inputField, helpBarText, modelSelector = null, providerSelector = null, pendingMessageId = null, awaitingConfirmation = false, themeSelector = null, commandPalette = null, chordMode = "none", cli_default;
21866
23265
  var init_cli = __esm(async () => {
21867
23266
  init_types();
21868
23267
  init_config();
@@ -21873,6 +23272,7 @@ var init_cli = __esm(async () => {
21873
23272
  currentModel = OPENCODE_ZEN_MODELS2[0];
21874
23273
  history = [];
21875
23274
  currentCwd = getCwd();
23275
+ chatMessages = [];
21876
23276
  if (false) {}
21877
23277
  cli_default = main2;
21878
23278
  });
@@ -23165,7 +24565,7 @@ Try: ${colors.cyan}msh "list all files"${colors.reset}
23165
24565
  `);
23166
24566
  rl.close();
23167
24567
  }
23168
- function executeCommand2(command) {
24568
+ function executeCommand(command) {
23169
24569
  return new Promise((resolve3) => {
23170
24570
  const child = spawn2(command, {
23171
24571
  shell: true,
@@ -23228,7 +24628,7 @@ async function translate(query, options) {
23228
24628
  console.error(`${colors.yellow}Use -n to preview, or run the command manually.${colors.reset}`);
23229
24629
  process.exit(1);
23230
24630
  }
23231
- const result = await executeCommand2(command);
24631
+ const result = await executeCommand(command);
23232
24632
  process.exit(result.code);
23233
24633
  } else {
23234
24634
  console.log(command);