@austinthesing/magic-shell 0.1.2 → 0.2.0
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/README.md +15 -25
- package/dist/cli.js +1724 -194
- package/dist/index.js +2154 -400
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -831,6 +831,13 @@ function t(strings, ...values) {
|
|
|
831
831
|
}
|
|
832
832
|
return new StyledText(chunks);
|
|
833
833
|
}
|
|
834
|
+
|
|
835
|
+
class LinearScrollAccel {
|
|
836
|
+
tick(_now) {
|
|
837
|
+
return 1;
|
|
838
|
+
}
|
|
839
|
+
reset() {}
|
|
840
|
+
}
|
|
834
841
|
function isCompleteSequence(data) {
|
|
835
842
|
if (!data.startsWith(ESC)) {
|
|
836
843
|
return "not-escape";
|
|
@@ -16650,7 +16657,7 @@ function canonicalize(obj, stack, replacementStack, replacer, key) {
|
|
|
16650
16657
|
}
|
|
16651
16658
|
return canonicalizedObj;
|
|
16652
16659
|
}
|
|
16653
|
-
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;
|
|
16660
|
+
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;
|
|
16654
16661
|
var init_core = __esm(async () => {
|
|
16655
16662
|
await init_index_93qf6w1k();
|
|
16656
16663
|
EditBuffer = class EditBuffer extends EventEmitter10 {
|
|
@@ -18369,6 +18376,1146 @@ var init_core = __esm(async () => {
|
|
|
18369
18376
|
};
|
|
18370
18377
|
defaultThumbBackgroundColor = RGBA.fromHex("#9a9ea3");
|
|
18371
18378
|
defaultTrackBackgroundColor = RGBA.fromHex("#252527");
|
|
18379
|
+
SliderRenderable = class SliderRenderable extends Renderable {
|
|
18380
|
+
orientation;
|
|
18381
|
+
_value;
|
|
18382
|
+
_min;
|
|
18383
|
+
_max;
|
|
18384
|
+
_viewPortSize;
|
|
18385
|
+
_backgroundColor;
|
|
18386
|
+
_foregroundColor;
|
|
18387
|
+
_onChange;
|
|
18388
|
+
constructor(ctx, options) {
|
|
18389
|
+
super(ctx, { flexShrink: 0, ...options });
|
|
18390
|
+
this.orientation = options.orientation;
|
|
18391
|
+
this._min = options.min ?? 0;
|
|
18392
|
+
this._max = options.max ?? 100;
|
|
18393
|
+
this._value = options.value ?? this._min;
|
|
18394
|
+
this._viewPortSize = options.viewPortSize ?? Math.max(1, (this._max - this._min) * 0.1);
|
|
18395
|
+
this._onChange = options.onChange;
|
|
18396
|
+
this._backgroundColor = options.backgroundColor ? parseColor(options.backgroundColor) : defaultTrackBackgroundColor;
|
|
18397
|
+
this._foregroundColor = options.foregroundColor ? parseColor(options.foregroundColor) : defaultThumbBackgroundColor;
|
|
18398
|
+
this.setupMouseHandling();
|
|
18399
|
+
}
|
|
18400
|
+
get value() {
|
|
18401
|
+
return this._value;
|
|
18402
|
+
}
|
|
18403
|
+
set value(newValue) {
|
|
18404
|
+
const clamped = Math.max(this._min, Math.min(this._max, newValue));
|
|
18405
|
+
if (clamped !== this._value) {
|
|
18406
|
+
this._value = clamped;
|
|
18407
|
+
this._onChange?.(clamped);
|
|
18408
|
+
this.emit("change", { value: clamped });
|
|
18409
|
+
this.requestRender();
|
|
18410
|
+
}
|
|
18411
|
+
}
|
|
18412
|
+
get min() {
|
|
18413
|
+
return this._min;
|
|
18414
|
+
}
|
|
18415
|
+
set min(newMin) {
|
|
18416
|
+
if (newMin !== this._min) {
|
|
18417
|
+
this._min = newMin;
|
|
18418
|
+
if (this._value < newMin) {
|
|
18419
|
+
this.value = newMin;
|
|
18420
|
+
}
|
|
18421
|
+
this.requestRender();
|
|
18422
|
+
}
|
|
18423
|
+
}
|
|
18424
|
+
get max() {
|
|
18425
|
+
return this._max;
|
|
18426
|
+
}
|
|
18427
|
+
set max(newMax) {
|
|
18428
|
+
if (newMax !== this._max) {
|
|
18429
|
+
this._max = newMax;
|
|
18430
|
+
if (this._value > newMax) {
|
|
18431
|
+
this.value = newMax;
|
|
18432
|
+
}
|
|
18433
|
+
this.requestRender();
|
|
18434
|
+
}
|
|
18435
|
+
}
|
|
18436
|
+
set viewPortSize(size) {
|
|
18437
|
+
const clampedSize = Math.max(0.01, Math.min(size, this._max - this._min));
|
|
18438
|
+
if (clampedSize !== this._viewPortSize) {
|
|
18439
|
+
this._viewPortSize = clampedSize;
|
|
18440
|
+
this.requestRender();
|
|
18441
|
+
}
|
|
18442
|
+
}
|
|
18443
|
+
get viewPortSize() {
|
|
18444
|
+
return this._viewPortSize;
|
|
18445
|
+
}
|
|
18446
|
+
get backgroundColor() {
|
|
18447
|
+
return this._backgroundColor;
|
|
18448
|
+
}
|
|
18449
|
+
set backgroundColor(value) {
|
|
18450
|
+
this._backgroundColor = parseColor(value);
|
|
18451
|
+
this.requestRender();
|
|
18452
|
+
}
|
|
18453
|
+
get foregroundColor() {
|
|
18454
|
+
return this._foregroundColor;
|
|
18455
|
+
}
|
|
18456
|
+
set foregroundColor(value) {
|
|
18457
|
+
this._foregroundColor = parseColor(value);
|
|
18458
|
+
this.requestRender();
|
|
18459
|
+
}
|
|
18460
|
+
calculateDragOffsetVirtual(event) {
|
|
18461
|
+
const trackStart = this.orientation === "vertical" ? this.y : this.x;
|
|
18462
|
+
const mousePos = (this.orientation === "vertical" ? event.y : event.x) - trackStart;
|
|
18463
|
+
const virtualMousePos = Math.max(0, Math.min((this.orientation === "vertical" ? this.height : this.width) * 2, mousePos * 2));
|
|
18464
|
+
const virtualThumbStart = this.getVirtualThumbStart();
|
|
18465
|
+
const virtualThumbSize = this.getVirtualThumbSize();
|
|
18466
|
+
return Math.max(0, Math.min(virtualThumbSize, virtualMousePos - virtualThumbStart));
|
|
18467
|
+
}
|
|
18468
|
+
setupMouseHandling() {
|
|
18469
|
+
let isDragging = false;
|
|
18470
|
+
let dragOffsetVirtual = 0;
|
|
18471
|
+
this.onMouseDown = (event) => {
|
|
18472
|
+
event.stopPropagation();
|
|
18473
|
+
event.preventDefault();
|
|
18474
|
+
const thumb = this.getThumbRect();
|
|
18475
|
+
const inThumb = event.x >= thumb.x && event.x < thumb.x + thumb.width && event.y >= thumb.y && event.y < thumb.y + thumb.height;
|
|
18476
|
+
if (inThumb) {
|
|
18477
|
+
isDragging = true;
|
|
18478
|
+
dragOffsetVirtual = this.calculateDragOffsetVirtual(event);
|
|
18479
|
+
} else {
|
|
18480
|
+
this.updateValueFromMouseDirect(event);
|
|
18481
|
+
isDragging = true;
|
|
18482
|
+
dragOffsetVirtual = this.calculateDragOffsetVirtual(event);
|
|
18483
|
+
}
|
|
18484
|
+
};
|
|
18485
|
+
this.onMouseDrag = (event) => {
|
|
18486
|
+
if (!isDragging)
|
|
18487
|
+
return;
|
|
18488
|
+
event.stopPropagation();
|
|
18489
|
+
this.updateValueFromMouseWithOffset(event, dragOffsetVirtual);
|
|
18490
|
+
};
|
|
18491
|
+
this.onMouseUp = (event) => {
|
|
18492
|
+
if (isDragging) {
|
|
18493
|
+
this.updateValueFromMouseWithOffset(event, dragOffsetVirtual);
|
|
18494
|
+
}
|
|
18495
|
+
isDragging = false;
|
|
18496
|
+
};
|
|
18497
|
+
}
|
|
18498
|
+
updateValueFromMouseDirect(event) {
|
|
18499
|
+
const trackStart = this.orientation === "vertical" ? this.y : this.x;
|
|
18500
|
+
const trackSize = this.orientation === "vertical" ? this.height : this.width;
|
|
18501
|
+
const mousePos = this.orientation === "vertical" ? event.y : event.x;
|
|
18502
|
+
const relativeMousePos = mousePos - trackStart;
|
|
18503
|
+
const clampedMousePos = Math.max(0, Math.min(trackSize, relativeMousePos));
|
|
18504
|
+
const ratio = trackSize === 0 ? 0 : clampedMousePos / trackSize;
|
|
18505
|
+
const range = this._max - this._min;
|
|
18506
|
+
const newValue = this._min + ratio * range;
|
|
18507
|
+
this.value = newValue;
|
|
18508
|
+
}
|
|
18509
|
+
updateValueFromMouseWithOffset(event, offsetVirtual) {
|
|
18510
|
+
const trackStart = this.orientation === "vertical" ? this.y : this.x;
|
|
18511
|
+
const trackSize = this.orientation === "vertical" ? this.height : this.width;
|
|
18512
|
+
const mousePos = this.orientation === "vertical" ? event.y : event.x;
|
|
18513
|
+
const virtualTrackSize = trackSize * 2;
|
|
18514
|
+
const relativeMousePos = mousePos - trackStart;
|
|
18515
|
+
const clampedMousePos = Math.max(0, Math.min(trackSize, relativeMousePos));
|
|
18516
|
+
const virtualMousePos = clampedMousePos * 2;
|
|
18517
|
+
const virtualThumbSize = this.getVirtualThumbSize();
|
|
18518
|
+
const maxThumbStart = Math.max(0, virtualTrackSize - virtualThumbSize);
|
|
18519
|
+
let desiredThumbStart = virtualMousePos - offsetVirtual;
|
|
18520
|
+
desiredThumbStart = Math.max(0, Math.min(maxThumbStart, desiredThumbStart));
|
|
18521
|
+
const ratio = maxThumbStart === 0 ? 0 : desiredThumbStart / maxThumbStart;
|
|
18522
|
+
const range = this._max - this._min;
|
|
18523
|
+
const newValue = this._min + ratio * range;
|
|
18524
|
+
this.value = newValue;
|
|
18525
|
+
}
|
|
18526
|
+
getThumbRect() {
|
|
18527
|
+
const virtualThumbSize = this.getVirtualThumbSize();
|
|
18528
|
+
const virtualThumbStart = this.getVirtualThumbStart();
|
|
18529
|
+
const realThumbStart = Math.floor(virtualThumbStart / 2);
|
|
18530
|
+
const realThumbSize = Math.ceil((virtualThumbStart + virtualThumbSize) / 2) - realThumbStart;
|
|
18531
|
+
if (this.orientation === "vertical") {
|
|
18532
|
+
return {
|
|
18533
|
+
x: this.x,
|
|
18534
|
+
y: this.y + realThumbStart,
|
|
18535
|
+
width: this.width,
|
|
18536
|
+
height: Math.max(1, realThumbSize)
|
|
18537
|
+
};
|
|
18538
|
+
} else {
|
|
18539
|
+
return {
|
|
18540
|
+
x: this.x + realThumbStart,
|
|
18541
|
+
y: this.y,
|
|
18542
|
+
width: Math.max(1, realThumbSize),
|
|
18543
|
+
height: this.height
|
|
18544
|
+
};
|
|
18545
|
+
}
|
|
18546
|
+
}
|
|
18547
|
+
renderSelf(buffer) {
|
|
18548
|
+
if (this.orientation === "horizontal") {
|
|
18549
|
+
this.renderHorizontal(buffer);
|
|
18550
|
+
} else {
|
|
18551
|
+
this.renderVertical(buffer);
|
|
18552
|
+
}
|
|
18553
|
+
}
|
|
18554
|
+
renderHorizontal(buffer) {
|
|
18555
|
+
const virtualThumbSize = this.getVirtualThumbSize();
|
|
18556
|
+
const virtualThumbStart = this.getVirtualThumbStart();
|
|
18557
|
+
const virtualThumbEnd = virtualThumbStart + virtualThumbSize;
|
|
18558
|
+
buffer.fillRect(this.x, this.y, this.width, this.height, this._backgroundColor);
|
|
18559
|
+
const realStartCell = Math.floor(virtualThumbStart / 2);
|
|
18560
|
+
const realEndCell = Math.ceil(virtualThumbEnd / 2) - 1;
|
|
18561
|
+
const startX = Math.max(0, realStartCell);
|
|
18562
|
+
const endX = Math.min(this.width - 1, realEndCell);
|
|
18563
|
+
for (let realX = startX;realX <= endX; realX++) {
|
|
18564
|
+
const virtualCellStart = realX * 2;
|
|
18565
|
+
const virtualCellEnd = virtualCellStart + 2;
|
|
18566
|
+
const thumbStartInCell = Math.max(virtualThumbStart, virtualCellStart);
|
|
18567
|
+
const thumbEndInCell = Math.min(virtualThumbEnd, virtualCellEnd);
|
|
18568
|
+
const coverage = thumbEndInCell - thumbStartInCell;
|
|
18569
|
+
let char = " ";
|
|
18570
|
+
if (coverage >= 2) {
|
|
18571
|
+
char = "█";
|
|
18572
|
+
} else {
|
|
18573
|
+
const isLeftHalf = thumbStartInCell === virtualCellStart;
|
|
18574
|
+
if (isLeftHalf) {
|
|
18575
|
+
char = "▌";
|
|
18576
|
+
} else {
|
|
18577
|
+
char = "▐";
|
|
18578
|
+
}
|
|
18579
|
+
}
|
|
18580
|
+
for (let y = 0;y < this.height; y++) {
|
|
18581
|
+
buffer.setCellWithAlphaBlending(this.x + realX, this.y + y, char, this._foregroundColor, this._backgroundColor);
|
|
18582
|
+
}
|
|
18583
|
+
}
|
|
18584
|
+
}
|
|
18585
|
+
renderVertical(buffer) {
|
|
18586
|
+
const virtualThumbSize = this.getVirtualThumbSize();
|
|
18587
|
+
const virtualThumbStart = this.getVirtualThumbStart();
|
|
18588
|
+
const virtualThumbEnd = virtualThumbStart + virtualThumbSize;
|
|
18589
|
+
buffer.fillRect(this.x, this.y, this.width, this.height, this._backgroundColor);
|
|
18590
|
+
const realStartCell = Math.floor(virtualThumbStart / 2);
|
|
18591
|
+
const realEndCell = Math.ceil(virtualThumbEnd / 2) - 1;
|
|
18592
|
+
const startY = Math.max(0, realStartCell);
|
|
18593
|
+
const endY = Math.min(this.height - 1, realEndCell);
|
|
18594
|
+
for (let realY = startY;realY <= endY; realY++) {
|
|
18595
|
+
const virtualCellStart = realY * 2;
|
|
18596
|
+
const virtualCellEnd = virtualCellStart + 2;
|
|
18597
|
+
const thumbStartInCell = Math.max(virtualThumbStart, virtualCellStart);
|
|
18598
|
+
const thumbEndInCell = Math.min(virtualThumbEnd, virtualCellEnd);
|
|
18599
|
+
const coverage = thumbEndInCell - thumbStartInCell;
|
|
18600
|
+
let char = " ";
|
|
18601
|
+
if (coverage >= 2) {
|
|
18602
|
+
char = "█";
|
|
18603
|
+
} else if (coverage > 0) {
|
|
18604
|
+
const virtualPositionInCell = thumbStartInCell - virtualCellStart;
|
|
18605
|
+
if (virtualPositionInCell === 0) {
|
|
18606
|
+
char = "▀";
|
|
18607
|
+
} else {
|
|
18608
|
+
char = "▄";
|
|
18609
|
+
}
|
|
18610
|
+
}
|
|
18611
|
+
for (let x = 0;x < this.width; x++) {
|
|
18612
|
+
buffer.setCellWithAlphaBlending(this.x + x, this.y + realY, char, this._foregroundColor, this._backgroundColor);
|
|
18613
|
+
}
|
|
18614
|
+
}
|
|
18615
|
+
}
|
|
18616
|
+
getVirtualThumbSize() {
|
|
18617
|
+
const virtualTrackSize = this.orientation === "vertical" ? this.height * 2 : this.width * 2;
|
|
18618
|
+
const range = this._max - this._min;
|
|
18619
|
+
if (range === 0)
|
|
18620
|
+
return virtualTrackSize;
|
|
18621
|
+
const viewportSize = Math.max(1, this._viewPortSize);
|
|
18622
|
+
const contentSize = range + viewportSize;
|
|
18623
|
+
if (contentSize <= viewportSize)
|
|
18624
|
+
return virtualTrackSize;
|
|
18625
|
+
const thumbRatio = viewportSize / contentSize;
|
|
18626
|
+
const calculatedSize = Math.floor(virtualTrackSize * thumbRatio);
|
|
18627
|
+
return Math.max(1, Math.min(calculatedSize, virtualTrackSize));
|
|
18628
|
+
}
|
|
18629
|
+
getVirtualThumbStart() {
|
|
18630
|
+
const virtualTrackSize = this.orientation === "vertical" ? this.height * 2 : this.width * 2;
|
|
18631
|
+
const range = this._max - this._min;
|
|
18632
|
+
if (range === 0)
|
|
18633
|
+
return 0;
|
|
18634
|
+
const valueRatio = (this._value - this._min) / range;
|
|
18635
|
+
const virtualThumbSize = this.getVirtualThumbSize();
|
|
18636
|
+
return Math.round(valueRatio * (virtualTrackSize - virtualThumbSize));
|
|
18637
|
+
}
|
|
18638
|
+
};
|
|
18639
|
+
ScrollBarRenderable = class ScrollBarRenderable extends Renderable {
|
|
18640
|
+
slider;
|
|
18641
|
+
startArrow;
|
|
18642
|
+
endArrow;
|
|
18643
|
+
orientation;
|
|
18644
|
+
_focusable = true;
|
|
18645
|
+
_scrollSize = 0;
|
|
18646
|
+
_scrollPosition = 0;
|
|
18647
|
+
_viewportSize = 0;
|
|
18648
|
+
_showArrows = false;
|
|
18649
|
+
_manualVisibility = false;
|
|
18650
|
+
_onChange;
|
|
18651
|
+
scrollStep = null;
|
|
18652
|
+
get visible() {
|
|
18653
|
+
return super.visible;
|
|
18654
|
+
}
|
|
18655
|
+
set visible(value) {
|
|
18656
|
+
this._manualVisibility = true;
|
|
18657
|
+
super.visible = value;
|
|
18658
|
+
}
|
|
18659
|
+
resetVisibilityControl() {
|
|
18660
|
+
this._manualVisibility = false;
|
|
18661
|
+
this.recalculateVisibility();
|
|
18662
|
+
}
|
|
18663
|
+
get scrollSize() {
|
|
18664
|
+
return this._scrollSize;
|
|
18665
|
+
}
|
|
18666
|
+
get scrollPosition() {
|
|
18667
|
+
return this._scrollPosition;
|
|
18668
|
+
}
|
|
18669
|
+
get viewportSize() {
|
|
18670
|
+
return this._viewportSize;
|
|
18671
|
+
}
|
|
18672
|
+
set scrollSize(value) {
|
|
18673
|
+
if (value === this.scrollSize)
|
|
18674
|
+
return;
|
|
18675
|
+
this._scrollSize = value;
|
|
18676
|
+
this.recalculateVisibility();
|
|
18677
|
+
this.updateSliderFromScrollState();
|
|
18678
|
+
this.scrollPosition = this.scrollPosition;
|
|
18679
|
+
}
|
|
18680
|
+
set scrollPosition(value) {
|
|
18681
|
+
const newPosition = Math.round(Math.min(Math.max(0, value), this.scrollSize - this.viewportSize));
|
|
18682
|
+
if (newPosition !== this._scrollPosition) {
|
|
18683
|
+
this._scrollPosition = newPosition;
|
|
18684
|
+
this.updateSliderFromScrollState();
|
|
18685
|
+
}
|
|
18686
|
+
}
|
|
18687
|
+
set viewportSize(value) {
|
|
18688
|
+
if (value === this.viewportSize)
|
|
18689
|
+
return;
|
|
18690
|
+
this._viewportSize = value;
|
|
18691
|
+
this.slider.viewPortSize = Math.max(1, this._viewportSize);
|
|
18692
|
+
this.recalculateVisibility();
|
|
18693
|
+
this.updateSliderFromScrollState();
|
|
18694
|
+
this.scrollPosition = this.scrollPosition;
|
|
18695
|
+
}
|
|
18696
|
+
get showArrows() {
|
|
18697
|
+
return this._showArrows;
|
|
18698
|
+
}
|
|
18699
|
+
set showArrows(value) {
|
|
18700
|
+
if (value === this._showArrows)
|
|
18701
|
+
return;
|
|
18702
|
+
this._showArrows = value;
|
|
18703
|
+
this.startArrow.visible = value;
|
|
18704
|
+
this.endArrow.visible = value;
|
|
18705
|
+
}
|
|
18706
|
+
constructor(ctx, { trackOptions, arrowOptions, orientation, showArrows = false, ...options }) {
|
|
18707
|
+
super(ctx, {
|
|
18708
|
+
flexDirection: orientation === "vertical" ? "column" : "row",
|
|
18709
|
+
alignSelf: "stretch",
|
|
18710
|
+
alignItems: "stretch",
|
|
18711
|
+
...options
|
|
18712
|
+
});
|
|
18713
|
+
this._onChange = options.onChange;
|
|
18714
|
+
this.orientation = orientation;
|
|
18715
|
+
this._showArrows = showArrows;
|
|
18716
|
+
const scrollRange = Math.max(0, this._scrollSize - this._viewportSize);
|
|
18717
|
+
const defaultStepSize = Math.max(1, this._viewportSize);
|
|
18718
|
+
const stepSize = trackOptions?.viewPortSize ?? defaultStepSize;
|
|
18719
|
+
this.slider = new SliderRenderable(ctx, {
|
|
18720
|
+
orientation,
|
|
18721
|
+
min: 0,
|
|
18722
|
+
max: scrollRange,
|
|
18723
|
+
value: this._scrollPosition,
|
|
18724
|
+
viewPortSize: stepSize,
|
|
18725
|
+
onChange: (value) => {
|
|
18726
|
+
this._scrollPosition = Math.round(value);
|
|
18727
|
+
this._onChange?.(this._scrollPosition);
|
|
18728
|
+
this.emit("change", { position: this._scrollPosition });
|
|
18729
|
+
},
|
|
18730
|
+
...orientation === "vertical" ? {
|
|
18731
|
+
width: Math.max(1, Math.min(2, this.width)),
|
|
18732
|
+
height: "100%",
|
|
18733
|
+
marginLeft: "auto"
|
|
18734
|
+
} : {
|
|
18735
|
+
width: "100%",
|
|
18736
|
+
height: 1,
|
|
18737
|
+
marginTop: "auto"
|
|
18738
|
+
},
|
|
18739
|
+
flexGrow: 1,
|
|
18740
|
+
flexShrink: 1,
|
|
18741
|
+
...trackOptions
|
|
18742
|
+
});
|
|
18743
|
+
this.updateSliderFromScrollState();
|
|
18744
|
+
const arrowOpts = arrowOptions ? {
|
|
18745
|
+
foregroundColor: arrowOptions.backgroundColor,
|
|
18746
|
+
backgroundColor: arrowOptions.backgroundColor,
|
|
18747
|
+
attributes: arrowOptions.attributes,
|
|
18748
|
+
...arrowOptions
|
|
18749
|
+
} : {};
|
|
18750
|
+
this.startArrow = new ArrowRenderable(ctx, {
|
|
18751
|
+
alignSelf: "center",
|
|
18752
|
+
visible: this.showArrows,
|
|
18753
|
+
direction: this.orientation === "vertical" ? "up" : "left",
|
|
18754
|
+
height: this.orientation === "vertical" ? 1 : 1,
|
|
18755
|
+
...arrowOpts
|
|
18756
|
+
});
|
|
18757
|
+
this.endArrow = new ArrowRenderable(ctx, {
|
|
18758
|
+
alignSelf: "center",
|
|
18759
|
+
visible: this.showArrows,
|
|
18760
|
+
direction: this.orientation === "vertical" ? "down" : "right",
|
|
18761
|
+
height: this.orientation === "vertical" ? 1 : 1,
|
|
18762
|
+
...arrowOpts
|
|
18763
|
+
});
|
|
18764
|
+
this.add(this.startArrow);
|
|
18765
|
+
this.add(this.slider);
|
|
18766
|
+
this.add(this.endArrow);
|
|
18767
|
+
let startArrowMouseTimeout = undefined;
|
|
18768
|
+
let endArrowMouseTimeout = undefined;
|
|
18769
|
+
this.startArrow.onMouseDown = (event) => {
|
|
18770
|
+
event.stopPropagation();
|
|
18771
|
+
event.preventDefault();
|
|
18772
|
+
this.scrollBy(-0.5, "viewport");
|
|
18773
|
+
startArrowMouseTimeout = setTimeout(() => {
|
|
18774
|
+
this.scrollBy(-0.5, "viewport");
|
|
18775
|
+
startArrowMouseTimeout = setInterval(() => {
|
|
18776
|
+
this.scrollBy(-0.2, "viewport");
|
|
18777
|
+
}, 200);
|
|
18778
|
+
}, 500);
|
|
18779
|
+
};
|
|
18780
|
+
this.startArrow.onMouseUp = (event) => {
|
|
18781
|
+
event.stopPropagation();
|
|
18782
|
+
clearInterval(startArrowMouseTimeout);
|
|
18783
|
+
};
|
|
18784
|
+
this.endArrow.onMouseDown = (event) => {
|
|
18785
|
+
event.stopPropagation();
|
|
18786
|
+
event.preventDefault();
|
|
18787
|
+
this.scrollBy(0.5, "viewport");
|
|
18788
|
+
endArrowMouseTimeout = setTimeout(() => {
|
|
18789
|
+
this.scrollBy(0.5, "viewport");
|
|
18790
|
+
endArrowMouseTimeout = setInterval(() => {
|
|
18791
|
+
this.scrollBy(0.2, "viewport");
|
|
18792
|
+
}, 200);
|
|
18793
|
+
}, 500);
|
|
18794
|
+
};
|
|
18795
|
+
this.endArrow.onMouseUp = (event) => {
|
|
18796
|
+
event.stopPropagation();
|
|
18797
|
+
clearInterval(endArrowMouseTimeout);
|
|
18798
|
+
};
|
|
18799
|
+
}
|
|
18800
|
+
set arrowOptions(options) {
|
|
18801
|
+
Object.assign(this.startArrow, options);
|
|
18802
|
+
Object.assign(this.endArrow, options);
|
|
18803
|
+
this.requestRender();
|
|
18804
|
+
}
|
|
18805
|
+
set trackOptions(options) {
|
|
18806
|
+
Object.assign(this.slider, options);
|
|
18807
|
+
this.requestRender();
|
|
18808
|
+
}
|
|
18809
|
+
updateSliderFromScrollState() {
|
|
18810
|
+
const scrollRange = Math.max(0, this._scrollSize - this._viewportSize);
|
|
18811
|
+
this.slider.min = 0;
|
|
18812
|
+
this.slider.max = scrollRange;
|
|
18813
|
+
this.slider.value = Math.min(this._scrollPosition, scrollRange);
|
|
18814
|
+
}
|
|
18815
|
+
scrollBy(delta, unit = "absolute") {
|
|
18816
|
+
const multiplier = unit === "viewport" ? this.viewportSize : unit === "content" ? this.scrollSize : unit === "step" ? this.scrollStep ?? 1 : 1;
|
|
18817
|
+
const resolvedDelta = multiplier * delta;
|
|
18818
|
+
this.scrollPosition += resolvedDelta;
|
|
18819
|
+
}
|
|
18820
|
+
recalculateVisibility() {
|
|
18821
|
+
if (!this._manualVisibility) {
|
|
18822
|
+
const sizeRatio = this.scrollSize <= this.viewportSize ? 1 : this.viewportSize / this.scrollSize;
|
|
18823
|
+
super.visible = sizeRatio < 1;
|
|
18824
|
+
}
|
|
18825
|
+
}
|
|
18826
|
+
handleKeyPress(key) {
|
|
18827
|
+
switch (key.name) {
|
|
18828
|
+
case "left":
|
|
18829
|
+
case "h":
|
|
18830
|
+
if (this.orientation !== "horizontal")
|
|
18831
|
+
return false;
|
|
18832
|
+
this.scrollBy(-1 / 5, "viewport");
|
|
18833
|
+
return true;
|
|
18834
|
+
case "right":
|
|
18835
|
+
case "l":
|
|
18836
|
+
if (this.orientation !== "horizontal")
|
|
18837
|
+
return false;
|
|
18838
|
+
this.scrollBy(1 / 5, "viewport");
|
|
18839
|
+
return true;
|
|
18840
|
+
case "up":
|
|
18841
|
+
case "k":
|
|
18842
|
+
if (this.orientation !== "vertical")
|
|
18843
|
+
return false;
|
|
18844
|
+
this.scrollBy(-1 / 5, "viewport");
|
|
18845
|
+
return true;
|
|
18846
|
+
case "down":
|
|
18847
|
+
case "j":
|
|
18848
|
+
if (this.orientation !== "vertical")
|
|
18849
|
+
return false;
|
|
18850
|
+
this.scrollBy(1 / 5, "viewport");
|
|
18851
|
+
return true;
|
|
18852
|
+
case "pageup":
|
|
18853
|
+
this.scrollBy(-1 / 2, "viewport");
|
|
18854
|
+
return true;
|
|
18855
|
+
case "pagedown":
|
|
18856
|
+
this.scrollBy(1 / 2, "viewport");
|
|
18857
|
+
return true;
|
|
18858
|
+
case "home":
|
|
18859
|
+
this.scrollBy(-1, "content");
|
|
18860
|
+
return true;
|
|
18861
|
+
case "end":
|
|
18862
|
+
this.scrollBy(1, "content");
|
|
18863
|
+
return true;
|
|
18864
|
+
}
|
|
18865
|
+
return false;
|
|
18866
|
+
}
|
|
18867
|
+
};
|
|
18868
|
+
ArrowRenderable = class ArrowRenderable extends Renderable {
|
|
18869
|
+
_direction;
|
|
18870
|
+
_foregroundColor;
|
|
18871
|
+
_backgroundColor;
|
|
18872
|
+
_attributes;
|
|
18873
|
+
_arrowChars;
|
|
18874
|
+
constructor(ctx, options) {
|
|
18875
|
+
super(ctx, options);
|
|
18876
|
+
this._direction = options.direction;
|
|
18877
|
+
this._foregroundColor = options.foregroundColor ? parseColor(options.foregroundColor) : RGBA.fromValues(1, 1, 1, 1);
|
|
18878
|
+
this._backgroundColor = options.backgroundColor ? parseColor(options.backgroundColor) : RGBA.fromValues(0, 0, 0, 0);
|
|
18879
|
+
this._attributes = options.attributes ?? 0;
|
|
18880
|
+
this._arrowChars = {
|
|
18881
|
+
up: "▲",
|
|
18882
|
+
down: "▼",
|
|
18883
|
+
left: "◀",
|
|
18884
|
+
right: "▶",
|
|
18885
|
+
...options.arrowChars
|
|
18886
|
+
};
|
|
18887
|
+
if (!options.width) {
|
|
18888
|
+
this.width = Bun.stringWidth(this.getArrowChar());
|
|
18889
|
+
}
|
|
18890
|
+
}
|
|
18891
|
+
get direction() {
|
|
18892
|
+
return this._direction;
|
|
18893
|
+
}
|
|
18894
|
+
set direction(value) {
|
|
18895
|
+
if (this._direction !== value) {
|
|
18896
|
+
this._direction = value;
|
|
18897
|
+
this.requestRender();
|
|
18898
|
+
}
|
|
18899
|
+
}
|
|
18900
|
+
get foregroundColor() {
|
|
18901
|
+
return this._foregroundColor;
|
|
18902
|
+
}
|
|
18903
|
+
set foregroundColor(value) {
|
|
18904
|
+
if (this._foregroundColor !== value) {
|
|
18905
|
+
this._foregroundColor = parseColor(value);
|
|
18906
|
+
this.requestRender();
|
|
18907
|
+
}
|
|
18908
|
+
}
|
|
18909
|
+
get backgroundColor() {
|
|
18910
|
+
return this._backgroundColor;
|
|
18911
|
+
}
|
|
18912
|
+
set backgroundColor(value) {
|
|
18913
|
+
if (this._backgroundColor !== value) {
|
|
18914
|
+
this._backgroundColor = parseColor(value);
|
|
18915
|
+
this.requestRender();
|
|
18916
|
+
}
|
|
18917
|
+
}
|
|
18918
|
+
get attributes() {
|
|
18919
|
+
return this._attributes;
|
|
18920
|
+
}
|
|
18921
|
+
set attributes(value) {
|
|
18922
|
+
if (this._attributes !== value) {
|
|
18923
|
+
this._attributes = value;
|
|
18924
|
+
this.requestRender();
|
|
18925
|
+
}
|
|
18926
|
+
}
|
|
18927
|
+
set arrowChars(value) {
|
|
18928
|
+
this._arrowChars = {
|
|
18929
|
+
...this._arrowChars,
|
|
18930
|
+
...value
|
|
18931
|
+
};
|
|
18932
|
+
this.requestRender();
|
|
18933
|
+
}
|
|
18934
|
+
renderSelf(buffer) {
|
|
18935
|
+
const char = this.getArrowChar();
|
|
18936
|
+
buffer.drawText(char, this.x, this.y, this._foregroundColor, this._backgroundColor, this._attributes);
|
|
18937
|
+
}
|
|
18938
|
+
getArrowChar() {
|
|
18939
|
+
switch (this._direction) {
|
|
18940
|
+
case "up":
|
|
18941
|
+
return this._arrowChars.up;
|
|
18942
|
+
case "down":
|
|
18943
|
+
return this._arrowChars.down;
|
|
18944
|
+
case "left":
|
|
18945
|
+
return this._arrowChars.left;
|
|
18946
|
+
case "right":
|
|
18947
|
+
return this._arrowChars.right;
|
|
18948
|
+
default:
|
|
18949
|
+
return "?";
|
|
18950
|
+
}
|
|
18951
|
+
}
|
|
18952
|
+
};
|
|
18953
|
+
ContentRenderable = class ContentRenderable extends BoxRenderable {
|
|
18954
|
+
viewport;
|
|
18955
|
+
_viewportCulling;
|
|
18956
|
+
constructor(ctx, viewport, viewportCulling, options) {
|
|
18957
|
+
super(ctx, options);
|
|
18958
|
+
this.viewport = viewport;
|
|
18959
|
+
this._viewportCulling = viewportCulling;
|
|
18960
|
+
}
|
|
18961
|
+
get viewportCulling() {
|
|
18962
|
+
return this._viewportCulling;
|
|
18963
|
+
}
|
|
18964
|
+
set viewportCulling(value) {
|
|
18965
|
+
this._viewportCulling = value;
|
|
18966
|
+
}
|
|
18967
|
+
_getVisibleChildren() {
|
|
18968
|
+
if (this._viewportCulling) {
|
|
18969
|
+
return getObjectsInViewport(this.viewport, this.getChildrenSortedByPrimaryAxis(), this.primaryAxis, 0).map((child) => child.num);
|
|
18970
|
+
}
|
|
18971
|
+
return this.getChildrenSortedByPrimaryAxis().map((child) => child.num);
|
|
18972
|
+
}
|
|
18973
|
+
};
|
|
18974
|
+
ScrollBoxRenderable = class ScrollBoxRenderable extends BoxRenderable {
|
|
18975
|
+
static idCounter = 0;
|
|
18976
|
+
internalId = 0;
|
|
18977
|
+
wrapper;
|
|
18978
|
+
viewport;
|
|
18979
|
+
content;
|
|
18980
|
+
horizontalScrollBar;
|
|
18981
|
+
verticalScrollBar;
|
|
18982
|
+
_focusable = true;
|
|
18983
|
+
selectionListener;
|
|
18984
|
+
autoScrollMouseX = 0;
|
|
18985
|
+
autoScrollMouseY = 0;
|
|
18986
|
+
autoScrollThresholdVertical = 3;
|
|
18987
|
+
autoScrollThresholdHorizontal = 3;
|
|
18988
|
+
autoScrollSpeedSlow = 6;
|
|
18989
|
+
autoScrollSpeedMedium = 36;
|
|
18990
|
+
autoScrollSpeedFast = 72;
|
|
18991
|
+
isAutoScrolling = false;
|
|
18992
|
+
cachedAutoScrollSpeed = 3;
|
|
18993
|
+
autoScrollAccumulatorX = 0;
|
|
18994
|
+
autoScrollAccumulatorY = 0;
|
|
18995
|
+
scrollAccumulatorX = 0;
|
|
18996
|
+
scrollAccumulatorY = 0;
|
|
18997
|
+
_stickyScroll;
|
|
18998
|
+
_stickyScrollTop = false;
|
|
18999
|
+
_stickyScrollBottom = false;
|
|
19000
|
+
_stickyScrollLeft = false;
|
|
19001
|
+
_stickyScrollRight = false;
|
|
19002
|
+
_stickyStart;
|
|
19003
|
+
_hasManualScroll = false;
|
|
19004
|
+
_isApplyingStickyScroll = false;
|
|
19005
|
+
scrollAccel;
|
|
19006
|
+
get stickyScroll() {
|
|
19007
|
+
return this._stickyScroll;
|
|
19008
|
+
}
|
|
19009
|
+
set stickyScroll(value) {
|
|
19010
|
+
this._stickyScroll = value;
|
|
19011
|
+
this.updateStickyState();
|
|
19012
|
+
}
|
|
19013
|
+
get stickyStart() {
|
|
19014
|
+
return this._stickyStart;
|
|
19015
|
+
}
|
|
19016
|
+
set stickyStart(value) {
|
|
19017
|
+
this._stickyStart = value;
|
|
19018
|
+
this.updateStickyState();
|
|
19019
|
+
}
|
|
19020
|
+
get scrollTop() {
|
|
19021
|
+
return this.verticalScrollBar.scrollPosition;
|
|
19022
|
+
}
|
|
19023
|
+
set scrollTop(value) {
|
|
19024
|
+
this.verticalScrollBar.scrollPosition = value;
|
|
19025
|
+
if (!this._isApplyingStickyScroll) {
|
|
19026
|
+
const maxScrollTop = Math.max(0, this.scrollHeight - this.viewport.height);
|
|
19027
|
+
if (!this.isAtStickyPosition() && maxScrollTop > 1) {
|
|
19028
|
+
this._hasManualScroll = true;
|
|
19029
|
+
}
|
|
19030
|
+
}
|
|
19031
|
+
this.updateStickyState();
|
|
19032
|
+
}
|
|
19033
|
+
get scrollLeft() {
|
|
19034
|
+
return this.horizontalScrollBar.scrollPosition;
|
|
19035
|
+
}
|
|
19036
|
+
set scrollLeft(value) {
|
|
19037
|
+
this.horizontalScrollBar.scrollPosition = value;
|
|
19038
|
+
if (!this._isApplyingStickyScroll) {
|
|
19039
|
+
const maxScrollLeft = Math.max(0, this.scrollWidth - this.viewport.width);
|
|
19040
|
+
if (!this.isAtStickyPosition() && maxScrollLeft > 1) {
|
|
19041
|
+
this._hasManualScroll = true;
|
|
19042
|
+
}
|
|
19043
|
+
}
|
|
19044
|
+
this.updateStickyState();
|
|
19045
|
+
}
|
|
19046
|
+
get scrollWidth() {
|
|
19047
|
+
return this.horizontalScrollBar.scrollSize;
|
|
19048
|
+
}
|
|
19049
|
+
get scrollHeight() {
|
|
19050
|
+
return this.verticalScrollBar.scrollSize;
|
|
19051
|
+
}
|
|
19052
|
+
updateStickyState() {
|
|
19053
|
+
if (!this._stickyScroll)
|
|
19054
|
+
return;
|
|
19055
|
+
const maxScrollTop = Math.max(0, this.scrollHeight - this.viewport.height);
|
|
19056
|
+
const maxScrollLeft = Math.max(0, this.scrollWidth - this.viewport.width);
|
|
19057
|
+
if (this.scrollTop <= 0) {
|
|
19058
|
+
this._stickyScrollTop = true;
|
|
19059
|
+
this._stickyScrollBottom = false;
|
|
19060
|
+
} else if (this.scrollTop >= maxScrollTop) {
|
|
19061
|
+
this._stickyScrollTop = false;
|
|
19062
|
+
this._stickyScrollBottom = true;
|
|
19063
|
+
} else {
|
|
19064
|
+
this._stickyScrollTop = false;
|
|
19065
|
+
this._stickyScrollBottom = false;
|
|
19066
|
+
}
|
|
19067
|
+
if (this.scrollLeft <= 0) {
|
|
19068
|
+
this._stickyScrollLeft = true;
|
|
19069
|
+
this._stickyScrollRight = false;
|
|
19070
|
+
} else if (this.scrollLeft >= maxScrollLeft) {
|
|
19071
|
+
this._stickyScrollLeft = false;
|
|
19072
|
+
this._stickyScrollRight = true;
|
|
19073
|
+
} else {
|
|
19074
|
+
this._stickyScrollLeft = false;
|
|
19075
|
+
this._stickyScrollRight = false;
|
|
19076
|
+
}
|
|
19077
|
+
}
|
|
19078
|
+
applyStickyStart(stickyStart) {
|
|
19079
|
+
this._isApplyingStickyScroll = true;
|
|
19080
|
+
switch (stickyStart) {
|
|
19081
|
+
case "top":
|
|
19082
|
+
this._stickyScrollTop = true;
|
|
19083
|
+
this._stickyScrollBottom = false;
|
|
19084
|
+
this.verticalScrollBar.scrollPosition = 0;
|
|
19085
|
+
break;
|
|
19086
|
+
case "bottom":
|
|
19087
|
+
this._stickyScrollTop = false;
|
|
19088
|
+
this._stickyScrollBottom = true;
|
|
19089
|
+
this.verticalScrollBar.scrollPosition = Math.max(0, this.scrollHeight - this.viewport.height);
|
|
19090
|
+
break;
|
|
19091
|
+
case "left":
|
|
19092
|
+
this._stickyScrollLeft = true;
|
|
19093
|
+
this._stickyScrollRight = false;
|
|
19094
|
+
this.horizontalScrollBar.scrollPosition = 0;
|
|
19095
|
+
break;
|
|
19096
|
+
case "right":
|
|
19097
|
+
this._stickyScrollLeft = false;
|
|
19098
|
+
this._stickyScrollRight = true;
|
|
19099
|
+
this.horizontalScrollBar.scrollPosition = Math.max(0, this.scrollWidth - this.viewport.width);
|
|
19100
|
+
break;
|
|
19101
|
+
}
|
|
19102
|
+
this._isApplyingStickyScroll = false;
|
|
19103
|
+
}
|
|
19104
|
+
constructor(ctx, {
|
|
19105
|
+
wrapperOptions,
|
|
19106
|
+
viewportOptions,
|
|
19107
|
+
contentOptions,
|
|
19108
|
+
rootOptions,
|
|
19109
|
+
scrollbarOptions,
|
|
19110
|
+
verticalScrollbarOptions,
|
|
19111
|
+
horizontalScrollbarOptions,
|
|
19112
|
+
stickyScroll = false,
|
|
19113
|
+
stickyStart,
|
|
19114
|
+
scrollX = false,
|
|
19115
|
+
scrollY = true,
|
|
19116
|
+
scrollAcceleration,
|
|
19117
|
+
viewportCulling = true,
|
|
19118
|
+
...options
|
|
19119
|
+
}) {
|
|
19120
|
+
super(ctx, {
|
|
19121
|
+
flexDirection: "row",
|
|
19122
|
+
alignItems: "stretch",
|
|
19123
|
+
...options,
|
|
19124
|
+
...rootOptions
|
|
19125
|
+
});
|
|
19126
|
+
this.internalId = ScrollBoxRenderable.idCounter++;
|
|
19127
|
+
this._stickyScroll = stickyScroll;
|
|
19128
|
+
this._stickyStart = stickyStart;
|
|
19129
|
+
this.scrollAccel = scrollAcceleration ?? new LinearScrollAccel;
|
|
19130
|
+
this.wrapper = new BoxRenderable(ctx, {
|
|
19131
|
+
flexDirection: "column",
|
|
19132
|
+
flexGrow: 1,
|
|
19133
|
+
...wrapperOptions,
|
|
19134
|
+
id: `scroll-box-wrapper-${this.internalId}`
|
|
19135
|
+
});
|
|
19136
|
+
super.add(this.wrapper);
|
|
19137
|
+
this.viewport = new BoxRenderable(ctx, {
|
|
19138
|
+
flexDirection: "column",
|
|
19139
|
+
flexGrow: 1,
|
|
19140
|
+
overflow: "hidden",
|
|
19141
|
+
onSizeChange: () => {
|
|
19142
|
+
this.recalculateBarProps();
|
|
19143
|
+
},
|
|
19144
|
+
...viewportOptions,
|
|
19145
|
+
id: `scroll-box-viewport-${this.internalId}`
|
|
19146
|
+
});
|
|
19147
|
+
this.wrapper.add(this.viewport);
|
|
19148
|
+
this.content = new ContentRenderable(ctx, this.viewport, viewportCulling, {
|
|
19149
|
+
alignSelf: "flex-start",
|
|
19150
|
+
flexShrink: 0,
|
|
19151
|
+
...scrollX ? { minWidth: "100%" } : { minWidth: "100%", maxWidth: "100%" },
|
|
19152
|
+
...scrollY ? { minHeight: "100%" } : { minHeight: "100%", maxHeight: "100%" },
|
|
19153
|
+
onSizeChange: () => {
|
|
19154
|
+
this.recalculateBarProps();
|
|
19155
|
+
},
|
|
19156
|
+
...contentOptions,
|
|
19157
|
+
id: `scroll-box-content-${this.internalId}`
|
|
19158
|
+
});
|
|
19159
|
+
this.viewport.add(this.content);
|
|
19160
|
+
this.verticalScrollBar = new ScrollBarRenderable(ctx, {
|
|
19161
|
+
...scrollbarOptions,
|
|
19162
|
+
...verticalScrollbarOptions,
|
|
19163
|
+
arrowOptions: {
|
|
19164
|
+
...scrollbarOptions?.arrowOptions,
|
|
19165
|
+
...verticalScrollbarOptions?.arrowOptions
|
|
19166
|
+
},
|
|
19167
|
+
id: `scroll-box-vertical-scrollbar-${this.internalId}`,
|
|
19168
|
+
orientation: "vertical",
|
|
19169
|
+
onChange: (position) => {
|
|
19170
|
+
this.content.translateY = -position;
|
|
19171
|
+
if (!this._isApplyingStickyScroll) {
|
|
19172
|
+
const maxScrollTop = Math.max(0, this.scrollHeight - this.viewport.height);
|
|
19173
|
+
if (!this.isAtStickyPosition() && maxScrollTop > 1) {
|
|
19174
|
+
this._hasManualScroll = true;
|
|
19175
|
+
}
|
|
19176
|
+
}
|
|
19177
|
+
this.updateStickyState();
|
|
19178
|
+
}
|
|
19179
|
+
});
|
|
19180
|
+
super.add(this.verticalScrollBar);
|
|
19181
|
+
this.horizontalScrollBar = new ScrollBarRenderable(ctx, {
|
|
19182
|
+
...scrollbarOptions,
|
|
19183
|
+
...horizontalScrollbarOptions,
|
|
19184
|
+
arrowOptions: {
|
|
19185
|
+
...scrollbarOptions?.arrowOptions,
|
|
19186
|
+
...horizontalScrollbarOptions?.arrowOptions
|
|
19187
|
+
},
|
|
19188
|
+
id: `scroll-box-horizontal-scrollbar-${this.internalId}`,
|
|
19189
|
+
orientation: "horizontal",
|
|
19190
|
+
onChange: (position) => {
|
|
19191
|
+
this.content.translateX = -position;
|
|
19192
|
+
if (!this._isApplyingStickyScroll) {
|
|
19193
|
+
const maxScrollLeft = Math.max(0, this.scrollWidth - this.viewport.width);
|
|
19194
|
+
if (!this.isAtStickyPosition() && maxScrollLeft > 1) {
|
|
19195
|
+
this._hasManualScroll = true;
|
|
19196
|
+
}
|
|
19197
|
+
}
|
|
19198
|
+
this.updateStickyState();
|
|
19199
|
+
}
|
|
19200
|
+
});
|
|
19201
|
+
this.wrapper.add(this.horizontalScrollBar);
|
|
19202
|
+
this.recalculateBarProps();
|
|
19203
|
+
if (stickyStart && stickyScroll) {
|
|
19204
|
+
this.applyStickyStart(stickyStart);
|
|
19205
|
+
}
|
|
19206
|
+
this.selectionListener = () => {
|
|
19207
|
+
const selection = this._ctx.getSelection();
|
|
19208
|
+
if (!selection || !selection.isSelecting) {
|
|
19209
|
+
this.stopAutoScroll();
|
|
19210
|
+
}
|
|
19211
|
+
};
|
|
19212
|
+
this._ctx.on("selection", this.selectionListener);
|
|
19213
|
+
}
|
|
19214
|
+
onUpdate(deltaTime) {
|
|
19215
|
+
this.handleAutoScroll(deltaTime);
|
|
19216
|
+
}
|
|
19217
|
+
scrollBy(delta, unit = "absolute") {
|
|
19218
|
+
if (typeof delta === "number") {
|
|
19219
|
+
this.verticalScrollBar.scrollBy(delta, unit);
|
|
19220
|
+
} else {
|
|
19221
|
+
this.verticalScrollBar.scrollBy(delta.y, unit);
|
|
19222
|
+
this.horizontalScrollBar.scrollBy(delta.x, unit);
|
|
19223
|
+
}
|
|
19224
|
+
}
|
|
19225
|
+
scrollTo(position) {
|
|
19226
|
+
if (typeof position === "number") {
|
|
19227
|
+
this.scrollTop = position;
|
|
19228
|
+
} else {
|
|
19229
|
+
this.scrollTop = position.y;
|
|
19230
|
+
this.scrollLeft = position.x;
|
|
19231
|
+
}
|
|
19232
|
+
}
|
|
19233
|
+
isAtStickyPosition() {
|
|
19234
|
+
if (!this._stickyScroll || !this._stickyStart) {
|
|
19235
|
+
return false;
|
|
19236
|
+
}
|
|
19237
|
+
const maxScrollTop = Math.max(0, this.scrollHeight - this.viewport.height);
|
|
19238
|
+
const maxScrollLeft = Math.max(0, this.scrollWidth - this.viewport.width);
|
|
19239
|
+
switch (this._stickyStart) {
|
|
19240
|
+
case "top":
|
|
19241
|
+
return this.scrollTop === 0;
|
|
19242
|
+
case "bottom":
|
|
19243
|
+
return this.scrollTop >= maxScrollTop;
|
|
19244
|
+
case "left":
|
|
19245
|
+
return this.scrollLeft === 0;
|
|
19246
|
+
case "right":
|
|
19247
|
+
return this.scrollLeft >= maxScrollLeft;
|
|
19248
|
+
default:
|
|
19249
|
+
return false;
|
|
19250
|
+
}
|
|
19251
|
+
}
|
|
19252
|
+
add(obj, index) {
|
|
19253
|
+
return this.content.add(obj, index);
|
|
19254
|
+
}
|
|
19255
|
+
insertBefore(obj, anchor) {
|
|
19256
|
+
return this.content.insertBefore(obj, anchor);
|
|
19257
|
+
}
|
|
19258
|
+
remove(id) {
|
|
19259
|
+
this.content.remove(id);
|
|
19260
|
+
}
|
|
19261
|
+
getChildren() {
|
|
19262
|
+
return this.content.getChildren();
|
|
19263
|
+
}
|
|
19264
|
+
onMouseEvent(event) {
|
|
19265
|
+
if (event.type === "scroll") {
|
|
19266
|
+
let dir = event.scroll?.direction;
|
|
19267
|
+
if (event.modifiers.shift)
|
|
19268
|
+
dir = dir === "up" ? "left" : dir === "down" ? "right" : dir === "right" ? "down" : "up";
|
|
19269
|
+
const baseDelta = event.scroll?.delta ?? 0;
|
|
19270
|
+
const now = Date.now();
|
|
19271
|
+
const multiplier = this.scrollAccel.tick(now);
|
|
19272
|
+
const scrollAmount = baseDelta * multiplier;
|
|
19273
|
+
if (dir === "up") {
|
|
19274
|
+
this.scrollAccumulatorY -= scrollAmount;
|
|
19275
|
+
const integerScroll = Math.trunc(this.scrollAccumulatorY);
|
|
19276
|
+
if (integerScroll !== 0) {
|
|
19277
|
+
this.scrollTop += integerScroll;
|
|
19278
|
+
this.scrollAccumulatorY -= integerScroll;
|
|
19279
|
+
}
|
|
19280
|
+
} else if (dir === "down") {
|
|
19281
|
+
this.scrollAccumulatorY += scrollAmount;
|
|
19282
|
+
const integerScroll = Math.trunc(this.scrollAccumulatorY);
|
|
19283
|
+
if (integerScroll !== 0) {
|
|
19284
|
+
this.scrollTop += integerScroll;
|
|
19285
|
+
this.scrollAccumulatorY -= integerScroll;
|
|
19286
|
+
}
|
|
19287
|
+
} else if (dir === "left") {
|
|
19288
|
+
this.scrollAccumulatorX -= scrollAmount;
|
|
19289
|
+
const integerScroll = Math.trunc(this.scrollAccumulatorX);
|
|
19290
|
+
if (integerScroll !== 0) {
|
|
19291
|
+
this.scrollLeft += integerScroll;
|
|
19292
|
+
this.scrollAccumulatorX -= integerScroll;
|
|
19293
|
+
}
|
|
19294
|
+
} else if (dir === "right") {
|
|
19295
|
+
this.scrollAccumulatorX += scrollAmount;
|
|
19296
|
+
const integerScroll = Math.trunc(this.scrollAccumulatorX);
|
|
19297
|
+
if (integerScroll !== 0) {
|
|
19298
|
+
this.scrollLeft += integerScroll;
|
|
19299
|
+
this.scrollAccumulatorX -= integerScroll;
|
|
19300
|
+
}
|
|
19301
|
+
}
|
|
19302
|
+
const maxScrollTop = Math.max(0, this.scrollHeight - this.viewport.height);
|
|
19303
|
+
const maxScrollLeft = Math.max(0, this.scrollWidth - this.viewport.width);
|
|
19304
|
+
if (maxScrollTop > 1 || maxScrollLeft > 1) {
|
|
19305
|
+
this._hasManualScroll = true;
|
|
19306
|
+
}
|
|
19307
|
+
}
|
|
19308
|
+
if (event.type === "drag" && event.isSelecting) {
|
|
19309
|
+
this.updateAutoScroll(event.x, event.y);
|
|
19310
|
+
} else if (event.type === "up") {
|
|
19311
|
+
this.stopAutoScroll();
|
|
19312
|
+
}
|
|
19313
|
+
}
|
|
19314
|
+
handleKeyPress(key) {
|
|
19315
|
+
if (this.verticalScrollBar.handleKeyPress(key)) {
|
|
19316
|
+
this._hasManualScroll = true;
|
|
19317
|
+
this.scrollAccel.reset();
|
|
19318
|
+
this.resetScrollAccumulators();
|
|
19319
|
+
return true;
|
|
19320
|
+
}
|
|
19321
|
+
if (this.horizontalScrollBar.handleKeyPress(key)) {
|
|
19322
|
+
this._hasManualScroll = true;
|
|
19323
|
+
this.scrollAccel.reset();
|
|
19324
|
+
this.resetScrollAccumulators();
|
|
19325
|
+
return true;
|
|
19326
|
+
}
|
|
19327
|
+
return false;
|
|
19328
|
+
}
|
|
19329
|
+
resetScrollAccumulators() {
|
|
19330
|
+
this.scrollAccumulatorX = 0;
|
|
19331
|
+
this.scrollAccumulatorY = 0;
|
|
19332
|
+
}
|
|
19333
|
+
startAutoScroll(mouseX, mouseY) {
|
|
19334
|
+
this.stopAutoScroll();
|
|
19335
|
+
this.autoScrollMouseX = mouseX;
|
|
19336
|
+
this.autoScrollMouseY = mouseY;
|
|
19337
|
+
this.cachedAutoScrollSpeed = this.getAutoScrollSpeed(mouseX, mouseY);
|
|
19338
|
+
this.isAutoScrolling = true;
|
|
19339
|
+
if (!this.live) {
|
|
19340
|
+
this.live = true;
|
|
19341
|
+
}
|
|
19342
|
+
}
|
|
19343
|
+
updateAutoScroll(mouseX, mouseY) {
|
|
19344
|
+
this.autoScrollMouseX = mouseX;
|
|
19345
|
+
this.autoScrollMouseY = mouseY;
|
|
19346
|
+
this.cachedAutoScrollSpeed = this.getAutoScrollSpeed(mouseX, mouseY);
|
|
19347
|
+
const scrollX = this.getAutoScrollDirectionX(mouseX);
|
|
19348
|
+
const scrollY = this.getAutoScrollDirectionY(mouseY);
|
|
19349
|
+
if (scrollX === 0 && scrollY === 0) {
|
|
19350
|
+
this.stopAutoScroll();
|
|
19351
|
+
} else if (!this.isAutoScrolling) {
|
|
19352
|
+
this.startAutoScroll(mouseX, mouseY);
|
|
19353
|
+
}
|
|
19354
|
+
}
|
|
19355
|
+
stopAutoScroll() {
|
|
19356
|
+
const wasAutoScrolling = this.isAutoScrolling;
|
|
19357
|
+
this.isAutoScrolling = false;
|
|
19358
|
+
this.autoScrollAccumulatorX = 0;
|
|
19359
|
+
this.autoScrollAccumulatorY = 0;
|
|
19360
|
+
if (wasAutoScrolling && !this.hasOtherLiveReasons()) {
|
|
19361
|
+
this.live = false;
|
|
19362
|
+
}
|
|
19363
|
+
}
|
|
19364
|
+
hasOtherLiveReasons() {
|
|
19365
|
+
return false;
|
|
19366
|
+
}
|
|
19367
|
+
handleAutoScroll(deltaTime) {
|
|
19368
|
+
if (!this.isAutoScrolling)
|
|
19369
|
+
return;
|
|
19370
|
+
const scrollX = this.getAutoScrollDirectionX(this.autoScrollMouseX);
|
|
19371
|
+
const scrollY = this.getAutoScrollDirectionY(this.autoScrollMouseY);
|
|
19372
|
+
const scrollAmount = this.cachedAutoScrollSpeed * (deltaTime / 1000);
|
|
19373
|
+
let scrolled = false;
|
|
19374
|
+
if (scrollX !== 0) {
|
|
19375
|
+
this.autoScrollAccumulatorX += scrollX * scrollAmount;
|
|
19376
|
+
const integerScrollX = Math.trunc(this.autoScrollAccumulatorX);
|
|
19377
|
+
if (integerScrollX !== 0) {
|
|
19378
|
+
this.scrollLeft += integerScrollX;
|
|
19379
|
+
this.autoScrollAccumulatorX -= integerScrollX;
|
|
19380
|
+
scrolled = true;
|
|
19381
|
+
}
|
|
19382
|
+
}
|
|
19383
|
+
if (scrollY !== 0) {
|
|
19384
|
+
this.autoScrollAccumulatorY += scrollY * scrollAmount;
|
|
19385
|
+
const integerScrollY = Math.trunc(this.autoScrollAccumulatorY);
|
|
19386
|
+
if (integerScrollY !== 0) {
|
|
19387
|
+
this.scrollTop += integerScrollY;
|
|
19388
|
+
this.autoScrollAccumulatorY -= integerScrollY;
|
|
19389
|
+
scrolled = true;
|
|
19390
|
+
}
|
|
19391
|
+
}
|
|
19392
|
+
if (scrolled) {
|
|
19393
|
+
this._ctx.requestSelectionUpdate();
|
|
19394
|
+
}
|
|
19395
|
+
if (scrollX === 0 && scrollY === 0) {
|
|
19396
|
+
this.stopAutoScroll();
|
|
19397
|
+
}
|
|
19398
|
+
}
|
|
19399
|
+
getAutoScrollDirectionX(mouseX) {
|
|
19400
|
+
const relativeX = mouseX - this.x;
|
|
19401
|
+
const distToLeft = relativeX;
|
|
19402
|
+
const distToRight = this.width - relativeX;
|
|
19403
|
+
if (distToLeft <= this.autoScrollThresholdHorizontal) {
|
|
19404
|
+
return this.scrollLeft > 0 ? -1 : 0;
|
|
19405
|
+
} else if (distToRight <= this.autoScrollThresholdHorizontal) {
|
|
19406
|
+
const maxScrollLeft = this.scrollWidth - this.viewport.width;
|
|
19407
|
+
return this.scrollLeft < maxScrollLeft ? 1 : 0;
|
|
19408
|
+
}
|
|
19409
|
+
return 0;
|
|
19410
|
+
}
|
|
19411
|
+
getAutoScrollDirectionY(mouseY) {
|
|
19412
|
+
const relativeY = mouseY - this.y;
|
|
19413
|
+
const distToTop = relativeY;
|
|
19414
|
+
const distToBottom = this.height - relativeY;
|
|
19415
|
+
if (distToTop <= this.autoScrollThresholdVertical) {
|
|
19416
|
+
return this.scrollTop > 0 ? -1 : 0;
|
|
19417
|
+
} else if (distToBottom <= this.autoScrollThresholdVertical) {
|
|
19418
|
+
const maxScrollTop = this.scrollHeight - this.viewport.height;
|
|
19419
|
+
return this.scrollTop < maxScrollTop ? 1 : 0;
|
|
19420
|
+
}
|
|
19421
|
+
return 0;
|
|
19422
|
+
}
|
|
19423
|
+
getAutoScrollSpeed(mouseX, mouseY) {
|
|
19424
|
+
const relativeX = mouseX - this.x;
|
|
19425
|
+
const relativeY = mouseY - this.y;
|
|
19426
|
+
const distToLeft = relativeX;
|
|
19427
|
+
const distToRight = this.width - relativeX;
|
|
19428
|
+
const distToTop = relativeY;
|
|
19429
|
+
const distToBottom = this.height - relativeY;
|
|
19430
|
+
const minDistance = Math.min(distToLeft, distToRight, distToTop, distToBottom);
|
|
19431
|
+
if (minDistance <= 1) {
|
|
19432
|
+
return this.autoScrollSpeedFast;
|
|
19433
|
+
} else if (minDistance <= 2) {
|
|
19434
|
+
return this.autoScrollSpeedMedium;
|
|
19435
|
+
} else {
|
|
19436
|
+
return this.autoScrollSpeedSlow;
|
|
19437
|
+
}
|
|
19438
|
+
}
|
|
19439
|
+
recalculateBarProps() {
|
|
19440
|
+
const wasApplyingStickyScroll = this._isApplyingStickyScroll;
|
|
19441
|
+
this._isApplyingStickyScroll = true;
|
|
19442
|
+
this.verticalScrollBar.scrollSize = this.content.height;
|
|
19443
|
+
this.verticalScrollBar.viewportSize = this.viewport.height;
|
|
19444
|
+
this.horizontalScrollBar.scrollSize = this.content.width;
|
|
19445
|
+
this.horizontalScrollBar.viewportSize = this.viewport.width;
|
|
19446
|
+
if (this._stickyScroll) {
|
|
19447
|
+
const newMaxScrollTop = Math.max(0, this.scrollHeight - this.viewport.height);
|
|
19448
|
+
const newMaxScrollLeft = Math.max(0, this.scrollWidth - this.viewport.width);
|
|
19449
|
+
if (this._stickyStart && !this._hasManualScroll) {
|
|
19450
|
+
this.applyStickyStart(this._stickyStart);
|
|
19451
|
+
} else {
|
|
19452
|
+
if (this._stickyScrollTop) {
|
|
19453
|
+
this.scrollTop = 0;
|
|
19454
|
+
} else if (this._stickyScrollBottom && newMaxScrollTop > 0) {
|
|
19455
|
+
this.scrollTop = newMaxScrollTop;
|
|
19456
|
+
}
|
|
19457
|
+
if (this._stickyScrollLeft) {
|
|
19458
|
+
this.scrollLeft = 0;
|
|
19459
|
+
} else if (this._stickyScrollRight && newMaxScrollLeft > 0) {
|
|
19460
|
+
this.scrollLeft = newMaxScrollLeft;
|
|
19461
|
+
}
|
|
19462
|
+
}
|
|
19463
|
+
}
|
|
19464
|
+
this._isApplyingStickyScroll = wasApplyingStickyScroll;
|
|
19465
|
+
process.nextTick(() => {
|
|
19466
|
+
this.requestRender();
|
|
19467
|
+
});
|
|
19468
|
+
}
|
|
19469
|
+
set rootOptions(options) {
|
|
19470
|
+
Object.assign(this, options);
|
|
19471
|
+
this.requestRender();
|
|
19472
|
+
}
|
|
19473
|
+
set wrapperOptions(options) {
|
|
19474
|
+
Object.assign(this.wrapper, options);
|
|
19475
|
+
this.requestRender();
|
|
19476
|
+
}
|
|
19477
|
+
set viewportOptions(options) {
|
|
19478
|
+
Object.assign(this.viewport, options);
|
|
19479
|
+
this.requestRender();
|
|
19480
|
+
}
|
|
19481
|
+
set contentOptions(options) {
|
|
19482
|
+
Object.assign(this.content, options);
|
|
19483
|
+
this.requestRender();
|
|
19484
|
+
}
|
|
19485
|
+
set scrollbarOptions(options) {
|
|
19486
|
+
Object.assign(this.verticalScrollBar, options);
|
|
19487
|
+
Object.assign(this.horizontalScrollBar, options);
|
|
19488
|
+
this.requestRender();
|
|
19489
|
+
}
|
|
19490
|
+
set verticalScrollbarOptions(options) {
|
|
19491
|
+
Object.assign(this.verticalScrollBar, options);
|
|
19492
|
+
this.requestRender();
|
|
19493
|
+
}
|
|
19494
|
+
set horizontalScrollbarOptions(options) {
|
|
19495
|
+
Object.assign(this.horizontalScrollBar, options);
|
|
19496
|
+
this.requestRender();
|
|
19497
|
+
}
|
|
19498
|
+
get scrollAcceleration() {
|
|
19499
|
+
return this.scrollAccel;
|
|
19500
|
+
}
|
|
19501
|
+
set scrollAcceleration(value) {
|
|
19502
|
+
this.scrollAccel = value;
|
|
19503
|
+
}
|
|
19504
|
+
get viewportCulling() {
|
|
19505
|
+
return this.content.viewportCulling;
|
|
19506
|
+
}
|
|
19507
|
+
set viewportCulling(value) {
|
|
19508
|
+
this.content.viewportCulling = value;
|
|
19509
|
+
this.requestRender();
|
|
19510
|
+
}
|
|
19511
|
+
destroySelf() {
|
|
19512
|
+
if (this.selectionListener) {
|
|
19513
|
+
this._ctx.off("selection", this.selectionListener);
|
|
19514
|
+
this.selectionListener = undefined;
|
|
19515
|
+
}
|
|
19516
|
+
super.destroySelf();
|
|
19517
|
+
}
|
|
19518
|
+
};
|
|
18372
19519
|
defaultSelectKeybindings = [
|
|
18373
19520
|
{ name: "up", action: "move-up" },
|
|
18374
19521
|
{ name: "k", action: "move-up" },
|
|
@@ -19969,7 +21116,7 @@ var init_config = __esm(() => {
|
|
|
19969
21116
|
provider: "opencode-zen",
|
|
19970
21117
|
openrouterApiKey: "",
|
|
19971
21118
|
opencodeZenApiKey: "",
|
|
19972
|
-
defaultModel: "
|
|
21119
|
+
defaultModel: "gemini-3-flash",
|
|
19973
21120
|
safetyLevel: "moderate",
|
|
19974
21121
|
dryRunByDefault: false,
|
|
19975
21122
|
blockedCommands: [
|
|
@@ -19980,7 +21127,8 @@ var init_config = __esm(() => {
|
|
|
19980
21127
|
"chmod -R 777 /",
|
|
19981
21128
|
"chown -R"
|
|
19982
21129
|
],
|
|
19983
|
-
confirmedDangerousPatterns: []
|
|
21130
|
+
confirmedDangerousPatterns: [],
|
|
21131
|
+
repoContext: false
|
|
19984
21132
|
};
|
|
19985
21133
|
});
|
|
19986
21134
|
|
|
@@ -20325,6 +21473,112 @@ function getPlatformPaths(platform) {
|
|
|
20325
21473
|
}
|
|
20326
21474
|
var init_shell = () => {};
|
|
20327
21475
|
|
|
21476
|
+
// src/lib/repo-context.ts
|
|
21477
|
+
import { existsSync as existsSync6, readFileSync as readFileSync2 } from "fs";
|
|
21478
|
+
import { join as join3 } from "path";
|
|
21479
|
+
function detectRepoContext(cwd) {
|
|
21480
|
+
const context = {
|
|
21481
|
+
type: "unknown"
|
|
21482
|
+
};
|
|
21483
|
+
let detected = false;
|
|
21484
|
+
if (existsSync6(join3(cwd, ".git"))) {
|
|
21485
|
+
context.hasGit = true;
|
|
21486
|
+
detected = true;
|
|
21487
|
+
}
|
|
21488
|
+
if (existsSync6(join3(cwd, "Dockerfile")) || existsSync6(join3(cwd, "docker-compose.yml")) || existsSync6(join3(cwd, "docker-compose.yaml"))) {
|
|
21489
|
+
context.hasDocker = true;
|
|
21490
|
+
detected = true;
|
|
21491
|
+
}
|
|
21492
|
+
const packageJsonPath = join3(cwd, "package.json");
|
|
21493
|
+
if (existsSync6(packageJsonPath)) {
|
|
21494
|
+
detected = true;
|
|
21495
|
+
context.type = "node";
|
|
21496
|
+
try {
|
|
21497
|
+
const packageJson = JSON.parse(readFileSync2(packageJsonPath, "utf-8"));
|
|
21498
|
+
if (existsSync6(join3(cwd, "bun.lockb")) || existsSync6(join3(cwd, "bun.lock"))) {
|
|
21499
|
+
context.packageManager = "bun";
|
|
21500
|
+
} else if (existsSync6(join3(cwd, "pnpm-lock.yaml"))) {
|
|
21501
|
+
context.packageManager = "pnpm";
|
|
21502
|
+
} else if (existsSync6(join3(cwd, "yarn.lock"))) {
|
|
21503
|
+
context.packageManager = "yarn";
|
|
21504
|
+
} else if (existsSync6(join3(cwd, "package-lock.json"))) {
|
|
21505
|
+
context.packageManager = "npm";
|
|
21506
|
+
} else if (packageJson.packageManager) {
|
|
21507
|
+
const pm = packageJson.packageManager.split("@")[0];
|
|
21508
|
+
context.packageManager = pm;
|
|
21509
|
+
}
|
|
21510
|
+
if (packageJson.scripts && typeof packageJson.scripts === "object") {
|
|
21511
|
+
context.scripts = Object.keys(packageJson.scripts);
|
|
21512
|
+
}
|
|
21513
|
+
} catch {}
|
|
21514
|
+
}
|
|
21515
|
+
const makefilePath = join3(cwd, "Makefile");
|
|
21516
|
+
if (existsSync6(makefilePath)) {
|
|
21517
|
+
detected = true;
|
|
21518
|
+
if (context.type === "unknown")
|
|
21519
|
+
context.type = "make";
|
|
21520
|
+
try {
|
|
21521
|
+
const makefile = readFileSync2(makefilePath, "utf-8");
|
|
21522
|
+
const targetRegex = /^([a-zA-Z_][a-zA-Z0-9_-]*)\s*:/gm;
|
|
21523
|
+
const targets = [];
|
|
21524
|
+
let match;
|
|
21525
|
+
while ((match = targetRegex.exec(makefile)) !== null) {
|
|
21526
|
+
if (!match[1].startsWith(".") && !match[1].startsWith("_")) {
|
|
21527
|
+
targets.push(match[1]);
|
|
21528
|
+
}
|
|
21529
|
+
}
|
|
21530
|
+
if (targets.length > 0) {
|
|
21531
|
+
context.makeTargets = [...new Set(targets)];
|
|
21532
|
+
}
|
|
21533
|
+
} catch {}
|
|
21534
|
+
}
|
|
21535
|
+
if (existsSync6(join3(cwd, "Cargo.toml"))) {
|
|
21536
|
+
detected = true;
|
|
21537
|
+
context.type = "rust";
|
|
21538
|
+
context.cargoCommands = ["build", "run", "test", "check", "clippy", "fmt", "doc"];
|
|
21539
|
+
}
|
|
21540
|
+
if (existsSync6(join3(cwd, "pyproject.toml")) || existsSync6(join3(cwd, "setup.py")) || existsSync6(join3(cwd, "requirements.txt"))) {
|
|
21541
|
+
detected = true;
|
|
21542
|
+
if (context.type === "unknown")
|
|
21543
|
+
context.type = "python";
|
|
21544
|
+
}
|
|
21545
|
+
if (existsSync6(join3(cwd, "go.mod"))) {
|
|
21546
|
+
detected = true;
|
|
21547
|
+
if (context.type === "unknown")
|
|
21548
|
+
context.type = "go";
|
|
21549
|
+
}
|
|
21550
|
+
return detected ? context : null;
|
|
21551
|
+
}
|
|
21552
|
+
function formatRepoContext(context) {
|
|
21553
|
+
const lines = [];
|
|
21554
|
+
lines.push(`Project type: ${context.type}`);
|
|
21555
|
+
if (context.packageManager) {
|
|
21556
|
+
lines.push(`Package manager: ${context.packageManager}`);
|
|
21557
|
+
}
|
|
21558
|
+
if (context.scripts && context.scripts.length > 0) {
|
|
21559
|
+
const displayScripts = context.scripts.slice(0, 15);
|
|
21560
|
+
const suffix = context.scripts.length > 15 ? ` (+${context.scripts.length - 15} more)` : "";
|
|
21561
|
+
lines.push(`Available scripts: ${displayScripts.join(", ")}${suffix}`);
|
|
21562
|
+
}
|
|
21563
|
+
if (context.makeTargets && context.makeTargets.length > 0) {
|
|
21564
|
+
const displayTargets = context.makeTargets.slice(0, 15);
|
|
21565
|
+
const suffix = context.makeTargets.length > 15 ? ` (+${context.makeTargets.length - 15} more)` : "";
|
|
21566
|
+
lines.push(`Make targets: ${displayTargets.join(", ")}${suffix}`);
|
|
21567
|
+
}
|
|
21568
|
+
if (context.cargoCommands) {
|
|
21569
|
+
lines.push(`Cargo commands: ${context.cargoCommands.join(", ")}`);
|
|
21570
|
+
}
|
|
21571
|
+
if (context.hasDocker) {
|
|
21572
|
+
lines.push(`Docker: available`);
|
|
21573
|
+
}
|
|
21574
|
+
if (context.hasGit) {
|
|
21575
|
+
lines.push(`Git: initialized`);
|
|
21576
|
+
}
|
|
21577
|
+
return lines.join(`
|
|
21578
|
+
`);
|
|
21579
|
+
}
|
|
21580
|
+
var init_repo_context = () => {};
|
|
21581
|
+
|
|
20328
21582
|
// src/lib/api.ts
|
|
20329
21583
|
function getZenApiType(modelId) {
|
|
20330
21584
|
if (modelId.startsWith("gpt-")) {
|
|
@@ -20338,11 +21592,21 @@ function getZenApiType(modelId) {
|
|
|
20338
21592
|
}
|
|
20339
21593
|
return "openai-compatible";
|
|
20340
21594
|
}
|
|
20341
|
-
function buildSystemPrompt(cwd, history, shellInfo) {
|
|
21595
|
+
function buildSystemPrompt(cwd, history, shellInfo, repoContextEnabled) {
|
|
20342
21596
|
const historyContext = formatHistory(history);
|
|
20343
21597
|
const platformPaths = getPlatformPaths(shellInfo.platform);
|
|
20344
21598
|
const shellHints = getShellSyntaxHints(shellInfo.shell);
|
|
20345
21599
|
const platformName = shellInfo.platform === "macos" ? "macOS" : shellInfo.platform === "windows" ? "Windows" : shellInfo.platform === "linux" ? shellInfo.isWSL ? "Linux (WSL)" : "Linux" : "Unknown";
|
|
21600
|
+
let projectContextSection = "";
|
|
21601
|
+
if (repoContextEnabled) {
|
|
21602
|
+
const repoContext = detectRepoContext(cwd);
|
|
21603
|
+
if (repoContext) {
|
|
21604
|
+
projectContextSection = `
|
|
21605
|
+
Project context:
|
|
21606
|
+
${formatRepoContext(repoContext)}
|
|
21607
|
+
`;
|
|
21608
|
+
}
|
|
21609
|
+
}
|
|
20346
21610
|
return `You are a shell command translator. Convert the user's natural language request into a shell command.
|
|
20347
21611
|
|
|
20348
21612
|
Current environment:
|
|
@@ -20351,7 +21615,7 @@ Current environment:
|
|
|
20351
21615
|
- Working directory: ${cwd}
|
|
20352
21616
|
- Home directory: ${shellInfo.homeDir}
|
|
20353
21617
|
${shellInfo.terminalEmulator ? `- Terminal: ${shellInfo.terminalEmulator}` : ""}
|
|
20354
|
-
|
|
21618
|
+
${projectContextSection}
|
|
20355
21619
|
${shellHints}
|
|
20356
21620
|
|
|
20357
21621
|
Recent command history:
|
|
@@ -20362,7 +21626,8 @@ Rules:
|
|
|
20362
21626
|
- No explanations, no markdown, no backticks, no code blocks
|
|
20363
21627
|
- Use the correct syntax for the detected shell (${shellInfo.shell})
|
|
20364
21628
|
- If the request is unclear, make a reasonable assumption
|
|
20365
|
-
- Prefer simple, common commands over complex one-liners
|
|
21629
|
+
- Prefer simple, common commands over complex one-liners${repoContextEnabled ? `
|
|
21630
|
+
- Use project-specific commands when relevant (e.g., use the detected package manager and available scripts)` : ""}
|
|
20366
21631
|
- Use the command history for context (e.g., "do that again", "undo", "delete the file I just created")
|
|
20367
21632
|
- If the user asks something that can't be done with a shell command, output a command that prints a helpful message
|
|
20368
21633
|
- For file operations, prefer safer alternatives when possible
|
|
@@ -20634,9 +21899,9 @@ function getShellInfo() {
|
|
|
20634
21899
|
}
|
|
20635
21900
|
return cachedShellInfo;
|
|
20636
21901
|
}
|
|
20637
|
-
async function translateToCommand(apiKey, model, userInput, cwd, history = []) {
|
|
21902
|
+
async function translateToCommand(apiKey, model, userInput, cwd, history = [], repoContextEnabled) {
|
|
20638
21903
|
const shellInfo = getShellInfo();
|
|
20639
|
-
const systemPrompt = buildSystemPrompt(cwd, history, shellInfo);
|
|
21904
|
+
const systemPrompt = buildSystemPrompt(cwd, history, shellInfo, repoContextEnabled);
|
|
20640
21905
|
let rawCommand;
|
|
20641
21906
|
if (model.provider === "openrouter") {
|
|
20642
21907
|
rawCommand = await callOpenRouter(apiKey, model.id, systemPrompt, userInput);
|
|
@@ -20662,6 +21927,7 @@ async function translateToCommand(apiKey, model, userInput, cwd, history = []) {
|
|
|
20662
21927
|
var DEBUG_API, cachedShellInfo = null;
|
|
20663
21928
|
var init_api = __esm(() => {
|
|
20664
21929
|
init_shell();
|
|
21930
|
+
init_repo_context();
|
|
20665
21931
|
DEBUG_API = process.env.DEBUG_API === "1";
|
|
20666
21932
|
});
|
|
20667
21933
|
|
|
@@ -20877,21 +22143,21 @@ var config;
|
|
|
20877
22143
|
var history = [];
|
|
20878
22144
|
var currentCwd = getCwd();
|
|
20879
22145
|
var dryRunMode = false;
|
|
22146
|
+
var chatMessages = [];
|
|
22147
|
+
var messageIdCounter = 0;
|
|
20880
22148
|
var mainContainer;
|
|
20881
22149
|
var headerText;
|
|
20882
|
-
var
|
|
20883
|
-
var
|
|
22150
|
+
var statusBarText;
|
|
22151
|
+
var chatScrollBox;
|
|
20884
22152
|
var inputField;
|
|
20885
|
-
var
|
|
20886
|
-
var outputText;
|
|
20887
|
-
var statusText;
|
|
20888
|
-
var commandPreview;
|
|
20889
|
-
var safetyWarning;
|
|
20890
|
-
var confirmPrompt;
|
|
22153
|
+
var helpBarText;
|
|
20891
22154
|
var modelSelector = null;
|
|
20892
22155
|
var providerSelector = null;
|
|
20893
|
-
var
|
|
22156
|
+
var pendingMessageId = null;
|
|
20894
22157
|
var awaitingConfirmation = false;
|
|
22158
|
+
function generateMessageId() {
|
|
22159
|
+
return `msg-${++messageIdCounter}`;
|
|
22160
|
+
}
|
|
20895
22161
|
async function main2() {
|
|
20896
22162
|
config = loadConfig();
|
|
20897
22163
|
history = loadHistory();
|
|
@@ -21069,44 +22335,65 @@ function createMainUI() {
|
|
|
21069
22335
|
id: "header-row",
|
|
21070
22336
|
flexDirection: "row",
|
|
21071
22337
|
width: "100%",
|
|
22338
|
+
alignItems: "center",
|
|
21072
22339
|
marginBottom: 1
|
|
21073
22340
|
});
|
|
21074
22341
|
mainContainer.add(headerRow);
|
|
21075
22342
|
headerText = new TextRenderable(renderer, {
|
|
21076
22343
|
id: "header-text",
|
|
21077
|
-
content: t`${bold(fg(theme.colors.primary)("magic-shell"))}
|
|
22344
|
+
content: t`${bold(fg(theme.colors.primary)("magic-shell"))}`,
|
|
21078
22345
|
flexGrow: 1
|
|
21079
22346
|
});
|
|
21080
22347
|
headerRow.add(headerText);
|
|
21081
|
-
const
|
|
21082
|
-
id: "
|
|
21083
|
-
|
|
21084
|
-
width: "100%",
|
|
21085
|
-
marginBottom: 1
|
|
22348
|
+
const modelBadge = new TextRenderable(renderer, {
|
|
22349
|
+
id: "model-badge",
|
|
22350
|
+
content: getModelDisplay()
|
|
21086
22351
|
});
|
|
21087
|
-
|
|
21088
|
-
|
|
21089
|
-
id: "
|
|
21090
|
-
content:
|
|
21091
|
-
|
|
22352
|
+
headerRow.add(modelBadge);
|
|
22353
|
+
statusBarText = new TextRenderable(renderer, {
|
|
22354
|
+
id: "status-bar-text",
|
|
22355
|
+
content: getStatusBarContent(),
|
|
22356
|
+
marginBottom: 1
|
|
21092
22357
|
});
|
|
21093
|
-
|
|
21094
|
-
|
|
21095
|
-
id: "
|
|
21096
|
-
|
|
22358
|
+
mainContainer.add(statusBarText);
|
|
22359
|
+
chatScrollBox = new ScrollBoxRenderable(renderer, {
|
|
22360
|
+
id: "chat-scroll-box",
|
|
22361
|
+
flexGrow: 1,
|
|
22362
|
+
width: "100%",
|
|
22363
|
+
scrollY: true,
|
|
22364
|
+
scrollX: false,
|
|
22365
|
+
stickyScroll: true,
|
|
22366
|
+
stickyStart: "bottom",
|
|
22367
|
+
rootOptions: {
|
|
22368
|
+
border: true,
|
|
22369
|
+
borderColor: theme.colors.border,
|
|
22370
|
+
borderStyle: "single"
|
|
22371
|
+
},
|
|
22372
|
+
viewportOptions: {
|
|
22373
|
+
backgroundColor: theme.colors.background,
|
|
22374
|
+
paddingLeft: 1,
|
|
22375
|
+
paddingRight: 1,
|
|
22376
|
+
paddingTop: 1
|
|
22377
|
+
},
|
|
22378
|
+
contentOptions: {
|
|
22379
|
+
flexDirection: "column",
|
|
22380
|
+
gap: 1
|
|
22381
|
+
}
|
|
21097
22382
|
});
|
|
21098
|
-
|
|
22383
|
+
mainContainer.add(chatScrollBox);
|
|
22384
|
+
addSystemMessage(getWelcomeMessage());
|
|
21099
22385
|
const inputRow = new BoxRenderable(renderer, {
|
|
21100
22386
|
id: "input-row",
|
|
21101
22387
|
flexDirection: "row",
|
|
21102
22388
|
width: "100%",
|
|
21103
|
-
|
|
22389
|
+
marginTop: 1,
|
|
22390
|
+
alignItems: "center"
|
|
21104
22391
|
});
|
|
21105
22392
|
mainContainer.add(inputRow);
|
|
21106
22393
|
const promptText = new TextRenderable(renderer, {
|
|
21107
22394
|
id: "prompt-text",
|
|
21108
|
-
content: t`${fg(theme.colors.
|
|
21109
|
-
width:
|
|
22395
|
+
content: t`${fg(theme.colors.primary)("~>")} `,
|
|
22396
|
+
width: 3
|
|
21110
22397
|
});
|
|
21111
22398
|
inputRow.add(promptText);
|
|
21112
22399
|
inputField = new InputRenderable(renderer, {
|
|
@@ -21124,58 +22411,269 @@ function createMainUI() {
|
|
|
21124
22411
|
}
|
|
21125
22412
|
});
|
|
21126
22413
|
inputRow.add(inputField);
|
|
21127
|
-
|
|
21128
|
-
id: "
|
|
21129
|
-
content:
|
|
21130
|
-
|
|
21131
|
-
});
|
|
21132
|
-
mainContainer.add(commandPreview);
|
|
21133
|
-
safetyWarning = new TextRenderable(renderer, {
|
|
21134
|
-
id: "safety-warning",
|
|
21135
|
-
content: ""
|
|
22414
|
+
helpBarText = new TextRenderable(renderer, {
|
|
22415
|
+
id: "help-bar-text",
|
|
22416
|
+
content: getHelpBarContent(),
|
|
22417
|
+
marginTop: 1
|
|
21136
22418
|
});
|
|
21137
|
-
mainContainer.add(
|
|
21138
|
-
|
|
21139
|
-
|
|
22419
|
+
mainContainer.add(helpBarText);
|
|
22420
|
+
inputField.on(InputRenderableEvents.ENTER, handleInput);
|
|
22421
|
+
renderer.keyInput.on("keypress", handleKeypress);
|
|
22422
|
+
inputField.focus();
|
|
22423
|
+
}
|
|
22424
|
+
function getStatusBarContent() {
|
|
22425
|
+
const theme = getTheme();
|
|
22426
|
+
const providerName = config.provider === "opencode-zen" ? "OpenCode Zen" : "OpenRouter";
|
|
22427
|
+
const safeModeIndicator = dryRunMode ? fg(theme.colors.warning)("[DRY RUN]") : fg(theme.colors.success)("Safe");
|
|
22428
|
+
const repoContextIndicator = config.repoContext ? fg(theme.colors.info)("[Repo]") : "";
|
|
22429
|
+
return t`${fg(theme.colors.textMuted)("Provider:")} ${fg(theme.colors.text)(providerName)} ${fg(theme.colors.textMuted)("Model:")} ${fg(theme.colors.text)(currentModel.name)} ${safeModeIndicator}${repoContextIndicator ? " " : ""}${repoContextIndicator}`;
|
|
22430
|
+
}
|
|
22431
|
+
function getHelpBarContent() {
|
|
22432
|
+
const theme = getTheme();
|
|
22433
|
+
if (awaitingConfirmation) {
|
|
22434
|
+
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")}`;
|
|
22435
|
+
}
|
|
22436
|
+
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")}`;
|
|
22437
|
+
}
|
|
22438
|
+
function getWelcomeMessage() {
|
|
22439
|
+
const providerName = config.provider === "opencode-zen" ? "OpenCode Zen" : "OpenRouter";
|
|
22440
|
+
const freeNote = config.provider === "opencode-zen" ? `
|
|
22441
|
+
Free models: grok-code, glm-4.7-free` : "";
|
|
22442
|
+
return `Ready. Using ${providerName}.${freeNote}
|
|
22443
|
+
Type what you want to do, or press Ctrl+X P for command palette.`;
|
|
22444
|
+
}
|
|
22445
|
+
function addSystemMessage(content) {
|
|
22446
|
+
const msg = {
|
|
22447
|
+
id: generateMessageId(),
|
|
22448
|
+
type: "system",
|
|
22449
|
+
content,
|
|
22450
|
+
timestamp: Date.now()
|
|
22451
|
+
};
|
|
22452
|
+
chatMessages.push(msg);
|
|
22453
|
+
renderMessage(msg);
|
|
22454
|
+
return msg;
|
|
22455
|
+
}
|
|
22456
|
+
function addUserMessage(content) {
|
|
22457
|
+
const msg = {
|
|
22458
|
+
id: generateMessageId(),
|
|
22459
|
+
type: "user",
|
|
22460
|
+
content,
|
|
22461
|
+
timestamp: Date.now()
|
|
22462
|
+
};
|
|
22463
|
+
chatMessages.push(msg);
|
|
22464
|
+
renderMessage(msg);
|
|
22465
|
+
return msg;
|
|
22466
|
+
}
|
|
22467
|
+
function addAssistantMessage(content, command, safety) {
|
|
22468
|
+
const msg = {
|
|
22469
|
+
id: generateMessageId(),
|
|
22470
|
+
type: "assistant",
|
|
22471
|
+
content,
|
|
22472
|
+
command,
|
|
22473
|
+
safety,
|
|
22474
|
+
timestamp: Date.now(),
|
|
22475
|
+
executed: false
|
|
22476
|
+
};
|
|
22477
|
+
chatMessages.push(msg);
|
|
22478
|
+
renderMessage(msg);
|
|
22479
|
+
return msg;
|
|
22480
|
+
}
|
|
22481
|
+
function addResultMessage(content, exitCode) {
|
|
22482
|
+
const msg = {
|
|
22483
|
+
id: generateMessageId(),
|
|
22484
|
+
type: "result",
|
|
22485
|
+
content,
|
|
22486
|
+
timestamp: Date.now(),
|
|
22487
|
+
exitCode
|
|
22488
|
+
};
|
|
22489
|
+
chatMessages.push(msg);
|
|
22490
|
+
renderMessage(msg);
|
|
22491
|
+
return msg;
|
|
22492
|
+
}
|
|
22493
|
+
function renderMessage(msg) {
|
|
22494
|
+
const theme = getTheme();
|
|
22495
|
+
const msgBox = createMessageRenderable(msg, theme);
|
|
22496
|
+
chatScrollBox.add(msgBox);
|
|
22497
|
+
}
|
|
22498
|
+
function createMessageRenderable(msg, theme) {
|
|
22499
|
+
switch (msg.type) {
|
|
22500
|
+
case "user":
|
|
22501
|
+
return createUserMessageRenderable(msg, theme);
|
|
22502
|
+
case "assistant":
|
|
22503
|
+
return createAssistantMessageRenderable(msg, theme);
|
|
22504
|
+
case "result":
|
|
22505
|
+
return createResultMessageRenderable(msg, theme);
|
|
22506
|
+
case "system":
|
|
22507
|
+
default:
|
|
22508
|
+
return createSystemMessageRenderable(msg, theme);
|
|
22509
|
+
}
|
|
22510
|
+
}
|
|
22511
|
+
function createUserMessageRenderable(msg, theme) {
|
|
22512
|
+
const box = new BoxRenderable(renderer, {
|
|
22513
|
+
id: `msg-${msg.id}`,
|
|
21140
22514
|
flexDirection: "row",
|
|
21141
|
-
|
|
21142
|
-
marginBottom: 1
|
|
22515
|
+
width: "100%"
|
|
21143
22516
|
});
|
|
21144
|
-
|
|
21145
|
-
|
|
21146
|
-
|
|
21147
|
-
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")}`
|
|
22517
|
+
const text = new TextRenderable(renderer, {
|
|
22518
|
+
id: `msg-${msg.id}-text`,
|
|
22519
|
+
content: t`${fg(theme.colors.success)(">")} ${fg(theme.colors.text)(msg.content)}`
|
|
21148
22520
|
});
|
|
21149
|
-
|
|
21150
|
-
|
|
21151
|
-
|
|
21152
|
-
|
|
22521
|
+
box.add(text);
|
|
22522
|
+
return box;
|
|
22523
|
+
}
|
|
22524
|
+
function createAssistantMessageRenderable(msg, theme) {
|
|
22525
|
+
const isSelected = pendingMessageId === msg.id;
|
|
22526
|
+
const card = new BoxRenderable(renderer, {
|
|
22527
|
+
id: `msg-${msg.id}`,
|
|
22528
|
+
flexDirection: "column",
|
|
22529
|
+
width: "100%",
|
|
21153
22530
|
border: true,
|
|
21154
|
-
borderColor: theme.colors.border,
|
|
22531
|
+
borderColor: isSelected ? theme.colors.primary : theme.colors.border,
|
|
21155
22532
|
borderStyle: "single",
|
|
21156
|
-
|
|
21157
|
-
|
|
22533
|
+
paddingLeft: 1,
|
|
22534
|
+
paddingRight: 1,
|
|
22535
|
+
paddingTop: 0,
|
|
22536
|
+
paddingBottom: 0,
|
|
22537
|
+
backgroundColor: theme.colors.backgroundPanel
|
|
21158
22538
|
});
|
|
21159
|
-
|
|
21160
|
-
|
|
21161
|
-
|
|
21162
|
-
${fg(theme.colors.success)("Free models available!")} Try: grok-code, glm-4.7-free` : "";
|
|
21163
|
-
outputText = new TextRenderable(renderer, {
|
|
21164
|
-
id: "output-text",
|
|
21165
|
-
content: t`${fg(theme.colors.textMuted)(`Ready. Using ${providerName}.`)}${freeModelsNote}
|
|
21166
|
-
|
|
21167
|
-
${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.")}`
|
|
22539
|
+
const commandText = new TextRenderable(renderer, {
|
|
22540
|
+
id: `msg-${msg.id}-cmd`,
|
|
22541
|
+
content: t`${fg(theme.colors.textMuted)("Command:")} ${fg(theme.colors.text)(msg.command || "")}`
|
|
21168
22542
|
});
|
|
21169
|
-
|
|
21170
|
-
|
|
21171
|
-
|
|
21172
|
-
|
|
21173
|
-
|
|
22543
|
+
card.add(commandText);
|
|
22544
|
+
if (msg.safety) {
|
|
22545
|
+
const severityColor = getSeverityColor(msg.safety.severity);
|
|
22546
|
+
const severityText = msg.safety.isDangerous ? `${msg.safety.severity.toUpperCase()} risk${msg.safety.reason ? ` - ${msg.safety.reason}` : ""}` : "Low risk";
|
|
22547
|
+
const safetyText = new TextRenderable(renderer, {
|
|
22548
|
+
id: `msg-${msg.id}-safety`,
|
|
22549
|
+
content: t`${fg(severityColor)("●")} ${fg(theme.colors.textMuted)(severityText)}`
|
|
22550
|
+
});
|
|
22551
|
+
card.add(safetyText);
|
|
22552
|
+
}
|
|
22553
|
+
if (isSelected && !msg.executed) {
|
|
22554
|
+
const actionsText = new TextRenderable(renderer, {
|
|
22555
|
+
id: `msg-${msg.id}-actions`,
|
|
22556
|
+
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")}`
|
|
22557
|
+
});
|
|
22558
|
+
card.add(actionsText);
|
|
22559
|
+
}
|
|
22560
|
+
if (msg.executed) {
|
|
22561
|
+
const execText = new TextRenderable(renderer, {
|
|
22562
|
+
id: `msg-${msg.id}-exec`,
|
|
22563
|
+
content: t`${fg(theme.colors.success)("Executed")}`
|
|
22564
|
+
});
|
|
22565
|
+
card.add(execText);
|
|
22566
|
+
}
|
|
22567
|
+
return card;
|
|
22568
|
+
}
|
|
22569
|
+
function createResultMessageRenderable(msg, theme) {
|
|
22570
|
+
const isSuccess = msg.exitCode === undefined || msg.exitCode === 0;
|
|
22571
|
+
const isExpanded = msg.expanded ?? false;
|
|
22572
|
+
const hasOutput = msg.content && msg.content.trim().length > 0;
|
|
22573
|
+
const outputLines = hasOutput ? msg.content.trim().split(`
|
|
22574
|
+
`) : [];
|
|
22575
|
+
const isLongOutput = outputLines.length > 5;
|
|
22576
|
+
const PREVIEW_LINES = 3;
|
|
22577
|
+
const card = new BoxRenderable(renderer, {
|
|
22578
|
+
id: `msg-${msg.id}`,
|
|
22579
|
+
flexDirection: "column",
|
|
22580
|
+
width: "100%",
|
|
22581
|
+
border: true,
|
|
22582
|
+
borderColor: isSuccess ? theme.colors.success : theme.colors.error,
|
|
22583
|
+
borderStyle: "single",
|
|
22584
|
+
paddingLeft: 1,
|
|
22585
|
+
paddingRight: 1,
|
|
22586
|
+
backgroundColor: theme.colors.backgroundPanel,
|
|
22587
|
+
onMouseDown: isLongOutput ? () => {
|
|
22588
|
+
toggleResultExpand(msg.id);
|
|
22589
|
+
} : undefined
|
|
21174
22590
|
});
|
|
21175
|
-
|
|
21176
|
-
|
|
21177
|
-
|
|
21178
|
-
|
|
22591
|
+
const statusIcon = isSuccess ? "✓" : "✗";
|
|
22592
|
+
const statusColor = isSuccess ? theme.colors.success : theme.colors.error;
|
|
22593
|
+
const statusLabel = isSuccess ? "Executed successfully" : `Exit code: ${msg.exitCode}`;
|
|
22594
|
+
const expandIcon = isLongOutput ? isExpanded ? "▼" : "▶" : "";
|
|
22595
|
+
const lineCount = isLongOutput ? ` (${outputLines.length} lines)` : "";
|
|
22596
|
+
const statusText = new TextRenderable(renderer, {
|
|
22597
|
+
id: `msg-${msg.id}-status`,
|
|
22598
|
+
content: t`${fg(statusColor)(statusIcon)} ${fg(theme.colors.text)(statusLabel)}${fg(theme.colors.textMuted)(lineCount)} ${fg(theme.colors.primary)(expandIcon)}`
|
|
22599
|
+
});
|
|
22600
|
+
card.add(statusText);
|
|
22601
|
+
if (hasOutput) {
|
|
22602
|
+
let displayContent;
|
|
22603
|
+
if (isExpanded || !isLongOutput) {
|
|
22604
|
+
displayContent = msg.content.trim();
|
|
22605
|
+
} else {
|
|
22606
|
+
const previewLines = outputLines.slice(0, PREVIEW_LINES);
|
|
22607
|
+
displayContent = previewLines.join(`
|
|
22608
|
+
`) + `
|
|
22609
|
+
... ${outputLines.length - PREVIEW_LINES} more lines`;
|
|
22610
|
+
}
|
|
22611
|
+
const outputText = new TextRenderable(renderer, {
|
|
22612
|
+
id: `msg-${msg.id}-output`,
|
|
22613
|
+
content: t`${fg(theme.colors.textMuted)(displayContent)}`
|
|
22614
|
+
});
|
|
22615
|
+
card.add(outputText);
|
|
22616
|
+
if (isLongOutput) {
|
|
22617
|
+
const hintText = new TextRenderable(renderer, {
|
|
22618
|
+
id: `msg-${msg.id}-hint`,
|
|
22619
|
+
content: t`${fg(theme.colors.primary)("[o]")} ${fg(theme.colors.textMuted)(isExpanded ? "Collapse" : "Expand output")}`
|
|
22620
|
+
});
|
|
22621
|
+
card.add(hintText);
|
|
22622
|
+
}
|
|
22623
|
+
}
|
|
22624
|
+
return card;
|
|
22625
|
+
}
|
|
22626
|
+
function createSystemMessageRenderable(msg, theme) {
|
|
22627
|
+
const box = new BoxRenderable(renderer, {
|
|
22628
|
+
id: `msg-${msg.id}`,
|
|
22629
|
+
flexDirection: "column",
|
|
22630
|
+
width: "100%"
|
|
22631
|
+
});
|
|
22632
|
+
const text = new TextRenderable(renderer, {
|
|
22633
|
+
id: `msg-${msg.id}-text`,
|
|
22634
|
+
content: t`${fg(theme.colors.textMuted)(msg.content)}`
|
|
22635
|
+
});
|
|
22636
|
+
box.add(text);
|
|
22637
|
+
return box;
|
|
22638
|
+
}
|
|
22639
|
+
function updateAssistantMessage(msgId, updates) {
|
|
22640
|
+
const msgIndex = chatMessages.findIndex((m) => m.id === msgId);
|
|
22641
|
+
if (msgIndex === -1)
|
|
22642
|
+
return;
|
|
22643
|
+
const msg = chatMessages[msgIndex];
|
|
22644
|
+
Object.assign(msg, updates);
|
|
22645
|
+
chatScrollBox.remove(`msg-${msgId}`);
|
|
22646
|
+
const theme = getTheme();
|
|
22647
|
+
const newBox = createMessageRenderable(msg, theme);
|
|
22648
|
+
chatScrollBox.add(newBox);
|
|
22649
|
+
}
|
|
22650
|
+
function updateResultMessage(msgId, updates) {
|
|
22651
|
+
const msgIndex = chatMessages.findIndex((m) => m.id === msgId);
|
|
22652
|
+
if (msgIndex === -1)
|
|
22653
|
+
return;
|
|
22654
|
+
const msg = chatMessages[msgIndex];
|
|
22655
|
+
Object.assign(msg, updates);
|
|
22656
|
+
chatScrollBox.remove(`msg-${msgId}`);
|
|
22657
|
+
const theme = getTheme();
|
|
22658
|
+
const newBox = createMessageRenderable(msg, theme);
|
|
22659
|
+
chatScrollBox.add(newBox);
|
|
22660
|
+
}
|
|
22661
|
+
function toggleResultExpand(msgId) {
|
|
22662
|
+
const msg = chatMessages.find((m) => m.id === msgId);
|
|
22663
|
+
if (!msg || msg.type !== "result")
|
|
22664
|
+
return;
|
|
22665
|
+
const outputLines = msg.content?.trim().split(`
|
|
22666
|
+
`) || [];
|
|
22667
|
+
if (outputLines.length <= 5)
|
|
22668
|
+
return;
|
|
22669
|
+
updateResultMessage(msgId, { expanded: !msg.expanded });
|
|
22670
|
+
}
|
|
22671
|
+
function toggleLastResultExpand() {
|
|
22672
|
+
const resultMessages = chatMessages.filter((m) => m.type === "result");
|
|
22673
|
+
if (resultMessages.length === 0)
|
|
22674
|
+
return;
|
|
22675
|
+
const lastResult = resultMessages[resultMessages.length - 1];
|
|
22676
|
+
toggleResultExpand(lastResult.id);
|
|
21179
22677
|
}
|
|
21180
22678
|
function getModelDisplay() {
|
|
21181
22679
|
const theme = getTheme();
|
|
@@ -21184,30 +22682,22 @@ function getModelDisplay() {
|
|
|
21184
22682
|
const freeBadge = currentModel.free ? fg(theme.colors.success)(" FREE") : "";
|
|
21185
22683
|
return t`${providerBadge} ${fg(categoryColor)(currentModel.name)}${freeBadge}`;
|
|
21186
22684
|
}
|
|
21187
|
-
function getDryRunStatus() {
|
|
21188
|
-
const theme = getTheme();
|
|
21189
|
-
if (dryRunMode) {
|
|
21190
|
-
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")}`;
|
|
21191
|
-
}
|
|
21192
|
-
return t`${fg(theme.colors.textMuted)("Ctrl+X P palette | Ctrl+X M model | Ctrl+X ? help")}`;
|
|
21193
|
-
}
|
|
21194
22685
|
function refreshThemeColors() {
|
|
21195
22686
|
const theme = getTheme();
|
|
21196
22687
|
renderer.setBackgroundColor(theme.colors.background);
|
|
21197
22688
|
if (headerText) {
|
|
21198
|
-
headerText.content = t`${bold(fg(theme.colors.primary)("magic-shell"))}
|
|
21199
|
-
}
|
|
21200
|
-
if (cwdText) {
|
|
21201
|
-
cwdText.content = t`${fg(theme.colors.textMuted)("cwd:")} ${fg(theme.colors.success)(currentCwd)}`;
|
|
22689
|
+
headerText.content = t`${bold(fg(theme.colors.primary)("magic-shell"))}`;
|
|
21202
22690
|
}
|
|
21203
|
-
if (
|
|
21204
|
-
|
|
22691
|
+
if (statusBarText) {
|
|
22692
|
+
statusBarText.content = getStatusBarContent();
|
|
21205
22693
|
}
|
|
21206
|
-
if (
|
|
21207
|
-
|
|
22694
|
+
if (helpBarText) {
|
|
22695
|
+
helpBarText.content = getHelpBarContent();
|
|
21208
22696
|
}
|
|
21209
|
-
if (
|
|
21210
|
-
|
|
22697
|
+
if (chatScrollBox) {
|
|
22698
|
+
chatScrollBox.rootOptions = {
|
|
22699
|
+
borderColor: theme.colors.border
|
|
22700
|
+
};
|
|
21211
22701
|
}
|
|
21212
22702
|
if (inputField) {
|
|
21213
22703
|
inputField.focusedBackgroundColor = theme.colors.backgroundPanel;
|
|
@@ -21215,14 +22705,6 @@ function refreshThemeColors() {
|
|
|
21215
22705
|
inputField.placeholderColor = theme.colors.textMuted;
|
|
21216
22706
|
inputField.cursorColor = theme.colors.primary;
|
|
21217
22707
|
}
|
|
21218
|
-
const providerName = config.provider === "opencode-zen" ? "OpenCode Zen" : "OpenRouter";
|
|
21219
|
-
const freeModelsNote = config.provider === "opencode-zen" ? `
|
|
21220
|
-
${fg(theme.colors.success)("Free models available!")} Try: grok-code, glm-4.7-free` : "";
|
|
21221
|
-
if (outputText) {
|
|
21222
|
-
outputText.content = t`${fg(theme.colors.textMuted)(`Ready. Using ${providerName}.`)}${freeModelsNote}
|
|
21223
|
-
|
|
21224
|
-
${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.")}`;
|
|
21225
|
-
}
|
|
21226
22708
|
}
|
|
21227
22709
|
async function handleInput(value) {
|
|
21228
22710
|
const input = value.trim();
|
|
@@ -21233,8 +22715,9 @@ async function handleInput(value) {
|
|
|
21233
22715
|
await handleSpecialCommand(input);
|
|
21234
22716
|
return;
|
|
21235
22717
|
}
|
|
22718
|
+
addUserMessage(input);
|
|
21236
22719
|
if (isDirectCommand(input)) {
|
|
21237
|
-
await
|
|
22720
|
+
await processDirectCommand(input, input);
|
|
21238
22721
|
return;
|
|
21239
22722
|
}
|
|
21240
22723
|
await translateAndProcess(input);
|
|
@@ -21268,38 +22751,50 @@ function isDirectCommand(input) {
|
|
|
21268
22751
|
async function translateAndProcess(input) {
|
|
21269
22752
|
const apiKey = await getApiKey(config.provider);
|
|
21270
22753
|
if (!apiKey) {
|
|
21271
|
-
|
|
22754
|
+
addSystemMessage("Error: No API key configured. Run !provider to set up.");
|
|
21272
22755
|
return;
|
|
21273
22756
|
}
|
|
21274
|
-
|
|
22757
|
+
const loadingMsg = addSystemMessage("Translating...");
|
|
21275
22758
|
try {
|
|
21276
|
-
const command = await translateToCommand(apiKey, currentModel, input, currentCwd, history);
|
|
21277
|
-
|
|
22759
|
+
const command = await translateToCommand(apiKey, currentModel, input, currentCwd, history, config.repoContext);
|
|
22760
|
+
chatScrollBox.remove(`msg-${loadingMsg.id}`);
|
|
22761
|
+
chatMessages = chatMessages.filter((m) => m.id !== loadingMsg.id);
|
|
21278
22762
|
const safety = analyzeCommand(command, config);
|
|
22763
|
+
const assistantMsg = addAssistantMessage(input, command, safety);
|
|
21279
22764
|
if (safety.isDangerous) {
|
|
21280
|
-
|
|
21281
|
-
pendingCommand = command;
|
|
22765
|
+
pendingMessageId = assistantMsg.id;
|
|
21282
22766
|
awaitingConfirmation = true;
|
|
21283
|
-
|
|
21284
|
-
setOutput(t`${fg("#fbbf24")("Command requires confirmation. Press Enter to execute or Esc to cancel.")}`);
|
|
22767
|
+
helpBarText.content = getHelpBarContent();
|
|
21285
22768
|
} else {
|
|
21286
|
-
|
|
21287
|
-
await processCommand(input, command);
|
|
22769
|
+
await executeAndShowResult(input, command, assistantMsg.id);
|
|
21288
22770
|
}
|
|
21289
22771
|
} catch (error) {
|
|
22772
|
+
chatScrollBox.remove(`msg-${loadingMsg.id}`);
|
|
22773
|
+
chatMessages = chatMessages.filter((m) => m.id !== loadingMsg.id);
|
|
21290
22774
|
const message = error instanceof Error ? error.message : String(error);
|
|
21291
|
-
|
|
22775
|
+
addSystemMessage(`Error: ${message}`);
|
|
21292
22776
|
}
|
|
21293
22777
|
}
|
|
21294
|
-
async function
|
|
22778
|
+
async function processDirectCommand(input, command) {
|
|
22779
|
+
const safety = analyzeCommand(command, config);
|
|
22780
|
+
const assistantMsg = addAssistantMessage(input, command, safety);
|
|
22781
|
+
if (safety.isDangerous) {
|
|
22782
|
+
pendingMessageId = assistantMsg.id;
|
|
22783
|
+
awaitingConfirmation = true;
|
|
22784
|
+
helpBarText.content = getHelpBarContent();
|
|
22785
|
+
} else {
|
|
22786
|
+
await executeAndShowResult(input, command, assistantMsg.id);
|
|
22787
|
+
}
|
|
22788
|
+
}
|
|
22789
|
+
async function executeAndShowResult(input, command, assistantMsgId) {
|
|
21295
22790
|
if (command.startsWith("cd ")) {
|
|
21296
22791
|
const path2 = command.slice(3).trim().replace(/^["']|["']$/g, "");
|
|
21297
22792
|
try {
|
|
21298
22793
|
const expandedPath = path2.startsWith("~") ? path2.replace("~", process.env.HOME || "") : path2;
|
|
21299
22794
|
process.chdir(expandedPath);
|
|
21300
22795
|
currentCwd = getCwd();
|
|
21301
|
-
|
|
21302
|
-
|
|
22796
|
+
statusBarText.content = getStatusBarContent();
|
|
22797
|
+
addResultMessage(`Changed directory to ${currentCwd}`, 0);
|
|
21303
22798
|
addToHistory({
|
|
21304
22799
|
input,
|
|
21305
22800
|
command,
|
|
@@ -21307,35 +22802,37 @@ async function processCommand(input, command) {
|
|
|
21307
22802
|
timestamp: Date.now()
|
|
21308
22803
|
});
|
|
21309
22804
|
history = loadHistory();
|
|
22805
|
+
updateAssistantMessage(assistantMsgId, { executed: true });
|
|
21310
22806
|
} catch (err) {
|
|
21311
|
-
|
|
22807
|
+
addResultMessage(`cd: ${err instanceof Error ? err.message : String(err)}`, 1);
|
|
21312
22808
|
}
|
|
21313
22809
|
clearCommandState();
|
|
21314
22810
|
return;
|
|
21315
22811
|
}
|
|
21316
22812
|
if (dryRunMode) {
|
|
21317
|
-
|
|
22813
|
+
addResultMessage(`[DRY RUN] Would execute: ${command}`, 0);
|
|
22814
|
+
updateAssistantMessage(assistantMsgId, { executed: true });
|
|
21318
22815
|
clearCommandState();
|
|
21319
22816
|
return;
|
|
21320
22817
|
}
|
|
21321
|
-
setOutput(t`${fg("#64748b")("Executing...")}`);
|
|
21322
22818
|
try {
|
|
21323
|
-
const
|
|
21324
|
-
|
|
22819
|
+
const { output, exitCode } = await executeCommandWithCode(command);
|
|
22820
|
+
addResultMessage(output || "Command completed successfully", exitCode);
|
|
21325
22821
|
addToHistory({
|
|
21326
22822
|
input,
|
|
21327
22823
|
command,
|
|
21328
|
-
output:
|
|
22824
|
+
output: output.slice(0, 500),
|
|
21329
22825
|
timestamp: Date.now()
|
|
21330
22826
|
});
|
|
21331
22827
|
history = loadHistory();
|
|
22828
|
+
updateAssistantMessage(assistantMsgId, { executed: true });
|
|
21332
22829
|
} catch (error) {
|
|
21333
22830
|
const message = error instanceof Error ? error.message : String(error);
|
|
21334
|
-
|
|
22831
|
+
addResultMessage(`Error: ${message}`, 1);
|
|
21335
22832
|
}
|
|
21336
22833
|
clearCommandState();
|
|
21337
22834
|
}
|
|
21338
|
-
function
|
|
22835
|
+
function executeCommandWithCode(command) {
|
|
21339
22836
|
return new Promise((resolve3, reject) => {
|
|
21340
22837
|
const child = spawn(command, {
|
|
21341
22838
|
shell: true,
|
|
@@ -21354,23 +22851,16 @@ function executeCommand(command) {
|
|
|
21354
22851
|
reject(error);
|
|
21355
22852
|
});
|
|
21356
22853
|
child.on("close", (code) => {
|
|
21357
|
-
|
|
21358
|
-
|
|
21359
|
-
|
|
21360
|
-
resolve3(stderr || stdout || `Command exited with code ${code}`);
|
|
21361
|
-
}
|
|
22854
|
+
const exitCode = code ?? 0;
|
|
22855
|
+
const output = stdout || stderr || (exitCode === 0 ? "" : `Command exited with code ${exitCode}`);
|
|
22856
|
+
resolve3({ output, exitCode });
|
|
21362
22857
|
});
|
|
21363
22858
|
});
|
|
21364
22859
|
}
|
|
21365
22860
|
function clearCommandState() {
|
|
21366
|
-
|
|
22861
|
+
pendingMessageId = null;
|
|
21367
22862
|
awaitingConfirmation = false;
|
|
21368
|
-
|
|
21369
|
-
commandPreview.content = "";
|
|
21370
|
-
safetyWarning.content = "";
|
|
21371
|
-
}
|
|
21372
|
-
function setOutput(content) {
|
|
21373
|
-
outputText.content = content;
|
|
22863
|
+
helpBarText.content = getHelpBarContent();
|
|
21374
22864
|
}
|
|
21375
22865
|
async function handleSpecialCommand(input) {
|
|
21376
22866
|
const cmd = input.slice(1).toLowerCase().trim();
|
|
@@ -21386,8 +22876,8 @@ async function handleSpecialCommand(input) {
|
|
|
21386
22876
|
break;
|
|
21387
22877
|
case "dry":
|
|
21388
22878
|
dryRunMode = !dryRunMode;
|
|
21389
|
-
|
|
21390
|
-
|
|
22879
|
+
statusBarText.content = getStatusBarContent();
|
|
22880
|
+
addSystemMessage(`Dry-run mode: ${dryRunMode ? "ON" : "OFF"}`);
|
|
21391
22881
|
break;
|
|
21392
22882
|
case "config":
|
|
21393
22883
|
await showConfig();
|
|
@@ -21396,65 +22886,74 @@ async function handleSpecialCommand(input) {
|
|
|
21396
22886
|
showHistory();
|
|
21397
22887
|
break;
|
|
21398
22888
|
case "clear":
|
|
21399
|
-
|
|
22889
|
+
clearChat();
|
|
21400
22890
|
break;
|
|
21401
22891
|
default:
|
|
21402
22892
|
if (cmd) {
|
|
21403
|
-
|
|
22893
|
+
addUserMessage(cmd);
|
|
22894
|
+
await processDirectCommand(input, cmd);
|
|
21404
22895
|
}
|
|
21405
22896
|
}
|
|
21406
22897
|
}
|
|
22898
|
+
function clearChat() {
|
|
22899
|
+
for (const msg of chatMessages) {
|
|
22900
|
+
chatScrollBox.remove(`msg-${msg.id}`);
|
|
22901
|
+
}
|
|
22902
|
+
chatMessages = [];
|
|
22903
|
+
addSystemMessage(getWelcomeMessage());
|
|
22904
|
+
}
|
|
21407
22905
|
function showHelp() {
|
|
21408
|
-
const
|
|
21409
|
-
|
|
21410
|
-
|
|
21411
|
-
|
|
21412
|
-
|
|
21413
|
-
|
|
21414
|
-
|
|
21415
|
-
${fg(theme.colors.primary)("C")} ${fg(theme.colors.textMuted)("Show config")} ${fg(theme.colors.primary)("L")} ${fg(theme.colors.textMuted)("Clear output")}
|
|
21416
|
-
${fg(theme.colors.primary)("?")} ${fg(theme.colors.textMuted)("This help")} ${fg(theme.colors.primary)("Q")} ${fg(theme.colors.textMuted)("Exit")}
|
|
22906
|
+
const helpText = `Keyboard Shortcuts (Ctrl+X then...):
|
|
22907
|
+
P Command palette M Change model
|
|
22908
|
+
S Switch provider D Toggle dry-run
|
|
22909
|
+
T Change theme R Toggle repo context
|
|
22910
|
+
H Show history L Clear chat
|
|
22911
|
+
C Show config ? This help
|
|
22912
|
+
Q Exit
|
|
21417
22913
|
|
|
21418
|
-
|
|
21419
|
-
|
|
22914
|
+
Other:
|
|
22915
|
+
Ctrl+C Exit / Cancel Esc Close palette
|
|
21420
22916
|
|
|
21421
|
-
|
|
22917
|
+
Tips:
|
|
21422
22918
|
- Type naturally: "list all files" -> ls -la
|
|
21423
22919
|
- Reference history: "do that again", "undo"
|
|
21424
|
-
-
|
|
22920
|
+
- Enable repo context to use project scripts (Ctrl+X R)`;
|
|
22921
|
+
addSystemMessage(helpText);
|
|
21425
22922
|
}
|
|
21426
22923
|
async function showConfig() {
|
|
21427
22924
|
const theme = getTheme();
|
|
21428
22925
|
const providerName = config.provider === "opencode-zen" ? "OpenCode Zen" : "OpenRouter";
|
|
21429
22926
|
const apiKey = await getApiKey(config.provider);
|
|
21430
|
-
const apiKeyStatus = apiKey ?
|
|
21431
|
-
const freeBadge = currentModel.free ?
|
|
22927
|
+
const apiKeyStatus = apiKey ? "configured" : "not set";
|
|
22928
|
+
const freeBadge = currentModel.free ? " (FREE)" : "";
|
|
21432
22929
|
const shellInfo = getShellInfo();
|
|
21433
|
-
|
|
22930
|
+
const configText = `Current Configuration
|
|
21434
22931
|
|
|
21435
|
-
|
|
21436
|
-
|
|
21437
|
-
|
|
21438
|
-
|
|
21439
|
-
|
|
21440
|
-
|
|
21441
|
-
|
|
21442
|
-
|
|
21443
|
-
|
|
21444
|
-
${
|
|
21445
|
-
|
|
22932
|
+
Provider: ${providerName}
|
|
22933
|
+
Model: ${currentModel.name}${freeBadge}
|
|
22934
|
+
Model ID: ${currentModel.id}
|
|
22935
|
+
Category: ${currentModel.category}
|
|
22936
|
+
Theme: ${theme.name}
|
|
22937
|
+
Shell: ${shellInfo.shell} (${shellInfo.shellPath})
|
|
22938
|
+
Platform: ${shellInfo.platform}${shellInfo.isWSL ? " (WSL)" : ""}
|
|
22939
|
+
Safety: ${config.safetyLevel}
|
|
22940
|
+
Dry-run: ${dryRunMode ? "ON" : "OFF"}
|
|
22941
|
+
Repo context: ${config.repoContext ? "ON" : "OFF"}
|
|
22942
|
+
API Key: ${apiKeyStatus}
|
|
22943
|
+
History: ${history.length} commands`;
|
|
22944
|
+
addSystemMessage(configText);
|
|
21446
22945
|
}
|
|
21447
22946
|
function showHistory() {
|
|
21448
22947
|
if (history.length === 0) {
|
|
21449
|
-
|
|
22948
|
+
addSystemMessage("No command history yet.");
|
|
21450
22949
|
return;
|
|
21451
22950
|
}
|
|
21452
22951
|
const recent = history.slice(-10);
|
|
21453
22952
|
const lines = recent.map((entry, i) => {
|
|
21454
22953
|
const date = new Date(entry.timestamp).toLocaleTimeString();
|
|
21455
|
-
return
|
|
22954
|
+
return `${i + 1}. [${date}] ${entry.command}`;
|
|
21456
22955
|
});
|
|
21457
|
-
|
|
22956
|
+
addSystemMessage(`Recent Command History
|
|
21458
22957
|
|
|
21459
22958
|
${lines.join(`
|
|
21460
22959
|
`)}`);
|
|
@@ -21520,10 +23019,10 @@ async function switchProvider() {
|
|
|
21520
23019
|
currentModel = models.find((m) => m.id === config.defaultModel) || models[0];
|
|
21521
23020
|
config.defaultModel = currentModel.id;
|
|
21522
23021
|
saveConfig(config);
|
|
21523
|
-
|
|
23022
|
+
statusBarText.content = getStatusBarContent();
|
|
21524
23023
|
closeSelector();
|
|
21525
23024
|
const providerName = newProvider === "opencode-zen" ? "OpenCode Zen" : "OpenRouter";
|
|
21526
|
-
|
|
23025
|
+
addSystemMessage(`Switched to ${providerName}. Model: ${currentModel.name}`);
|
|
21527
23026
|
} else {
|
|
21528
23027
|
closeSelector();
|
|
21529
23028
|
renderer.root.remove("main-container");
|
|
@@ -21589,12 +23088,12 @@ function showModelSelector() {
|
|
|
21589
23088
|
currentModel = option.value;
|
|
21590
23089
|
config.defaultModel = currentModel.id;
|
|
21591
23090
|
saveConfig(config);
|
|
21592
|
-
|
|
23091
|
+
statusBarText.content = getStatusBarContent();
|
|
21593
23092
|
renderer.root.remove("model-selector-container");
|
|
21594
23093
|
modelSelector = null;
|
|
21595
23094
|
inputField.focus();
|
|
21596
23095
|
const freeBadge = currentModel.free ? " (FREE)" : "";
|
|
21597
|
-
|
|
23096
|
+
addSystemMessage(`Model changed to ${currentModel.name}${freeBadge}`);
|
|
21598
23097
|
});
|
|
21599
23098
|
modelSelector.focus();
|
|
21600
23099
|
}
|
|
@@ -21652,8 +23151,7 @@ function showThemeSelector() {
|
|
|
21652
23151
|
renderer.root.remove("theme-selector-container");
|
|
21653
23152
|
themeSelector = null;
|
|
21654
23153
|
refreshThemeColors();
|
|
21655
|
-
|
|
21656
|
-
setOutput(t`${fg(newTheme.colors.success)(`Theme changed to ${themeName}`)}`);
|
|
23154
|
+
addSystemMessage(`Theme changed to ${themeName}`);
|
|
21657
23155
|
inputField.focus();
|
|
21658
23156
|
});
|
|
21659
23157
|
const escHandler = (key) => {
|
|
@@ -21699,8 +23197,20 @@ function getCommandPaletteOptions() {
|
|
|
21699
23197
|
chord: "d",
|
|
21700
23198
|
action: () => {
|
|
21701
23199
|
dryRunMode = !dryRunMode;
|
|
21702
|
-
|
|
21703
|
-
|
|
23200
|
+
statusBarText.content = getStatusBarContent();
|
|
23201
|
+
addSystemMessage(`Dry-run mode: ${dryRunMode ? "ON" : "OFF"}`);
|
|
23202
|
+
}
|
|
23203
|
+
},
|
|
23204
|
+
{
|
|
23205
|
+
name: "Toggle Project Context",
|
|
23206
|
+
description: config.repoContext ? "Currently ON (sends script names to AI)" : "Currently OFF",
|
|
23207
|
+
key: "r",
|
|
23208
|
+
chord: "r",
|
|
23209
|
+
action: () => {
|
|
23210
|
+
config.repoContext = !config.repoContext;
|
|
23211
|
+
saveConfig(config);
|
|
23212
|
+
statusBarText.content = getStatusBarContent();
|
|
23213
|
+
addSystemMessage(`Project context: ${config.repoContext ? "ON - AI can see your package.json scripts, Makefile targets, etc." : "OFF"}`);
|
|
21704
23214
|
}
|
|
21705
23215
|
},
|
|
21706
23216
|
{
|
|
@@ -21725,11 +23235,11 @@ function getCommandPaletteOptions() {
|
|
|
21725
23235
|
action: () => showThemeSelector()
|
|
21726
23236
|
},
|
|
21727
23237
|
{
|
|
21728
|
-
name: "Clear
|
|
21729
|
-
description: "Clear the
|
|
23238
|
+
name: "Clear Chat",
|
|
23239
|
+
description: "Clear the chat history",
|
|
21730
23240
|
key: "l",
|
|
21731
23241
|
chord: "l",
|
|
21732
|
-
action: () =>
|
|
23242
|
+
action: () => clearChat()
|
|
21733
23243
|
},
|
|
21734
23244
|
{
|
|
21735
23245
|
name: "Show Help",
|
|
@@ -21870,21 +23380,41 @@ function handleKeypress(key) {
|
|
|
21870
23380
|
inputField.focus();
|
|
21871
23381
|
return;
|
|
21872
23382
|
}
|
|
21873
|
-
if (awaitingConfirmation) {
|
|
23383
|
+
if (awaitingConfirmation && pendingMessageId) {
|
|
21874
23384
|
clearCommandState();
|
|
21875
|
-
|
|
23385
|
+
addSystemMessage("Command cancelled.");
|
|
21876
23386
|
inputField.focus();
|
|
21877
23387
|
}
|
|
21878
23388
|
}
|
|
21879
|
-
if (key.name === "return" && awaitingConfirmation &&
|
|
21880
|
-
const
|
|
21881
|
-
|
|
21882
|
-
|
|
23389
|
+
if (key.name === "return" && awaitingConfirmation && pendingMessageId) {
|
|
23390
|
+
const msg = chatMessages.find((m) => m.id === pendingMessageId);
|
|
23391
|
+
if (msg && msg.command) {
|
|
23392
|
+
const command = msg.command;
|
|
23393
|
+
const msgId = pendingMessageId;
|
|
23394
|
+
clearCommandState();
|
|
23395
|
+
executeAndShowResult(msg.content, command, msgId);
|
|
23396
|
+
}
|
|
21883
23397
|
}
|
|
21884
|
-
if (key.name === "e" && awaitingConfirmation &&
|
|
21885
|
-
|
|
21886
|
-
|
|
21887
|
-
|
|
23398
|
+
if (key.name === "e" && awaitingConfirmation && pendingMessageId) {
|
|
23399
|
+
const msg = chatMessages.find((m) => m.id === pendingMessageId);
|
|
23400
|
+
if (msg && msg.command) {
|
|
23401
|
+
inputField.value = msg.command;
|
|
23402
|
+
clearCommandState();
|
|
23403
|
+
inputField.focus();
|
|
23404
|
+
}
|
|
23405
|
+
}
|
|
23406
|
+
if (key.name === "c" && awaitingConfirmation && pendingMessageId) {
|
|
23407
|
+
const msg = chatMessages.find((m) => m.id === pendingMessageId);
|
|
23408
|
+
if (msg && msg.command) {
|
|
23409
|
+
const copyCmd = process.platform === "darwin" ? "pbcopy" : "xclip -selection clipboard";
|
|
23410
|
+
const child = spawn(copyCmd, { shell: true });
|
|
23411
|
+
child.stdin?.write(msg.command);
|
|
23412
|
+
child.stdin?.end();
|
|
23413
|
+
addSystemMessage(`Copied to clipboard: ${msg.command}`);
|
|
23414
|
+
}
|
|
23415
|
+
}
|
|
23416
|
+
if (key.name === "o" && !awaitingConfirmation && !commandPalette && !modelSelector) {
|
|
23417
|
+
toggleLastResultExpand();
|
|
21888
23418
|
}
|
|
21889
23419
|
}
|
|
21890
23420
|
if (__require.main == __require.module) {
|