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