@quatcode/agent 0.2.5 → 0.2.6
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/input.d.ts +6 -2
- package/dist/input.d.ts.map +1 -1
- package/dist/input.js +87 -31
- package/dist/input.js.map +1 -1
- package/dist/keyboard.d.ts.map +1 -1
- package/dist/keyboard.js +20 -2
- package/dist/keyboard.js.map +1 -1
- package/dist/theme.d.ts +1 -0
- package/dist/theme.d.ts.map +1 -1
- package/dist/theme.js +2 -0
- package/dist/theme.js.map +1 -1
- package/dist/tui.d.ts.map +1 -1
- package/dist/tui.js +81 -20
- package/dist/tui.js.map +1 -1
- package/package.json +2 -2
package/dist/tui.js
CHANGED
|
@@ -10,7 +10,7 @@ import { Picker } from "./picker.js";
|
|
|
10
10
|
import { Screen } from "./screen.js";
|
|
11
11
|
import { TurnController, fmtCost, fmtDuration, fmtTokens, toolArg, toolResultBlock } from "./turn.js";
|
|
12
12
|
import { renderBanner } from "./logo.js";
|
|
13
|
-
import { renderMarkdown } from "./markdown.js";
|
|
13
|
+
import { renderMarkdown, wrapSegments } from "./markdown.js";
|
|
14
14
|
import { ADD_PROVIDER, allProviders, buildProvider, defaultModelFor, effortsForModel, hasKey, keyEnvFor, probeProviderModels, providerEnvKey, providerHasModel, providerMeta, providerSlug, registerCustomProvider, removeCustomProvider, resolveProviderForModel, } from "./models.js";
|
|
15
15
|
import { activeThemeId, bg, bold, buildColorRemap, clipAnsi, fg, getThemes, glyphs, padEndTo, palette, remapColors, setTheme, stringWidth, stripAnsi, tone, toolGlyph, } from "./theme.js";
|
|
16
16
|
import { listSessionsForDir, loadConfig, makeSessionId, saveConfig, saveSession, deleteSession } from "./store.js";
|
|
@@ -136,6 +136,9 @@ class TuiApp {
|
|
|
136
136
|
modeBadgeWidth = 0;
|
|
137
137
|
modeBadgeHover = false;
|
|
138
138
|
versionNotice = null;
|
|
139
|
+
bottomHintStart = -1;
|
|
140
|
+
bottomHintWidth = 0;
|
|
141
|
+
bottomHintRow = -1;
|
|
139
142
|
constructor(options, input, output) {
|
|
140
143
|
this.opts = options;
|
|
141
144
|
this.dir = path.resolve(options.dir);
|
|
@@ -433,6 +436,11 @@ class TuiApp {
|
|
|
433
436
|
this.selection = null;
|
|
434
437
|
this.scheduleRender();
|
|
435
438
|
}
|
|
439
|
+
if (key.type === "nav" && key.dir === "down" && key.ctrl) {
|
|
440
|
+
this.scrollOffset = 0;
|
|
441
|
+
this.scheduleRender();
|
|
442
|
+
continue;
|
|
443
|
+
}
|
|
436
444
|
if (this.mode === "editing")
|
|
437
445
|
this.handleEditKey(key);
|
|
438
446
|
else if (this.mode === "running")
|
|
@@ -735,16 +743,38 @@ class TuiApp {
|
|
|
735
743
|
if (!rendered)
|
|
736
744
|
return false;
|
|
737
745
|
const top = this.lastHeaderRows + this.lastViewport + this.lastInputOffset;
|
|
738
|
-
const
|
|
739
|
-
if (
|
|
746
|
+
const rowIndex = row - top;
|
|
747
|
+
if (rowIndex < 0 || rowIndex >= rendered.lines.length)
|
|
748
|
+
return false;
|
|
749
|
+
const visual = rendered.rows[rowIndex];
|
|
750
|
+
if (!visual)
|
|
740
751
|
return false;
|
|
741
|
-
const
|
|
742
|
-
|
|
743
|
-
const start = rendered.lineStarts[lineIndex] ?? 0;
|
|
744
|
-
this.buffer.setCursorFromDisplay(lineIndex, Math.max(0, textCol), start);
|
|
752
|
+
const textCol = col - this.padX - visual.prefixWidth;
|
|
753
|
+
this.buffer.setCursorFromDisplay(visual.line, Math.max(0, textCol), visual.start);
|
|
745
754
|
this.scheduleRender();
|
|
746
755
|
return true;
|
|
747
756
|
}
|
|
757
|
+
bottomHintHit(row, col) {
|
|
758
|
+
if (this.bottomHintRow < 0 || this.bottomHintStart < 0)
|
|
759
|
+
return false;
|
|
760
|
+
if (row !== this.bottomHintRow)
|
|
761
|
+
return false;
|
|
762
|
+
const start = this.padX + this.bottomHintStart;
|
|
763
|
+
return col >= start && col < start + this.bottomHintWidth;
|
|
764
|
+
}
|
|
765
|
+
bottomRuleLine() {
|
|
766
|
+
const width = this.contentWidth();
|
|
767
|
+
const label = `${glyphs.arrowDown} bottom · ctrl+${glyphs.arrowDown}`;
|
|
768
|
+
this.bottomHintStart = -1;
|
|
769
|
+
this.bottomHintWidth = 0;
|
|
770
|
+
if (this.scrollOffset > 0 && this.mode !== "picker" && width > 36) {
|
|
771
|
+
const dashes = Math.max(8, width - 2 - stringWidth(label) - 2);
|
|
772
|
+
this.bottomHintStart = 2 + dashes + 2;
|
|
773
|
+
this.bottomHintWidth = stringWidth(label);
|
|
774
|
+
return ` ${tone.mist("─".repeat(dashes))} ${tone.silver(label)}`;
|
|
775
|
+
}
|
|
776
|
+
return ` ${tone.mist("─".repeat(Math.max(8, width - 2)))}`;
|
|
777
|
+
}
|
|
748
778
|
handleHover(row, col) {
|
|
749
779
|
if (row === 1) {
|
|
750
780
|
const hover = this.badgeHit(col);
|
|
@@ -791,6 +821,11 @@ class TuiApp {
|
|
|
791
821
|
this.toggleMode();
|
|
792
822
|
return;
|
|
793
823
|
}
|
|
824
|
+
if (this.bottomHintHit(row, col)) {
|
|
825
|
+
this.scrollOffset = 0;
|
|
826
|
+
this.scheduleRender();
|
|
827
|
+
return;
|
|
828
|
+
}
|
|
794
829
|
if (this.mode === "permission") {
|
|
795
830
|
const index = this.permissionOptionAt(row, col);
|
|
796
831
|
if (index !== null) {
|
|
@@ -1036,6 +1071,8 @@ class TuiApp {
|
|
|
1036
1071
|
const viewport = Math.max(3, rows - header.length - footer.length - this.padTop - this.padBottom);
|
|
1037
1072
|
this.lastHeaderRows = header.length + this.padTop;
|
|
1038
1073
|
this.lastViewport = viewport;
|
|
1074
|
+
this.bottomHintRow =
|
|
1075
|
+
this.bottomHintStart >= 0 ? this.lastHeaderRows + viewport + footer.length - 1 : -1;
|
|
1039
1076
|
let content;
|
|
1040
1077
|
if (this.mode === "picker" && this.picker) {
|
|
1041
1078
|
content = this.picker.render(this.contentWidth() - 2, viewport);
|
|
@@ -1116,6 +1153,7 @@ class TuiApp {
|
|
|
1116
1153
|
footerLines() {
|
|
1117
1154
|
const width = this.contentWidth();
|
|
1118
1155
|
const rule = ` ${tone.mist("─".repeat(Math.max(8, width - 2)))}`;
|
|
1156
|
+
const bottom = this.bottomRuleLine();
|
|
1119
1157
|
if (this.mode === "editing") {
|
|
1120
1158
|
const rendered = this.buffer.render({
|
|
1121
1159
|
width,
|
|
@@ -1124,23 +1162,35 @@ class TuiApp {
|
|
|
1124
1162
|
mask: this.inputMask,
|
|
1125
1163
|
style: styleInputLine,
|
|
1126
1164
|
});
|
|
1127
|
-
this.
|
|
1165
|
+
const maxRows = Math.max(3, Math.floor(this.screen.rows / 2));
|
|
1166
|
+
if (rendered.lines.length > maxRows) {
|
|
1167
|
+
const start = Math.max(0, Math.min(rendered.cursorRow - maxRows + 1, rendered.lines.length - maxRows));
|
|
1168
|
+
this.lastInputRender = {
|
|
1169
|
+
lines: rendered.lines.slice(start, start + maxRows),
|
|
1170
|
+
rows: rendered.rows.slice(start, start + maxRows),
|
|
1171
|
+
cursorRow: rendered.cursorRow - start,
|
|
1172
|
+
cursorCol: rendered.cursorCol,
|
|
1173
|
+
};
|
|
1174
|
+
}
|
|
1175
|
+
else {
|
|
1176
|
+
this.lastInputRender = rendered;
|
|
1177
|
+
}
|
|
1128
1178
|
const suggestions = this.suggestions();
|
|
1129
1179
|
this.lastInputOffset = 1 + suggestions.length;
|
|
1130
|
-
return [rule, ...suggestions, ...
|
|
1180
|
+
return [rule, ...suggestions, ...this.lastInputRender.lines, bottom];
|
|
1131
1181
|
}
|
|
1132
1182
|
this.lastInputRender = null;
|
|
1133
1183
|
if (this.status)
|
|
1134
|
-
return [rule, this.statusLine(),
|
|
1184
|
+
return [rule, this.statusLine(), bottom];
|
|
1135
1185
|
if (this.mode === "running") {
|
|
1136
|
-
return [rule, ` ${tone.mist("// esc interrupts")}`,
|
|
1186
|
+
return [rule, ` ${tone.mist("// esc interrupts")}`, bottom];
|
|
1137
1187
|
}
|
|
1138
1188
|
if (this.mode === "permission")
|
|
1139
|
-
return [rule, ` ${this.permissionHint}`,
|
|
1189
|
+
return [rule, ` ${this.permissionHint}`, bottom];
|
|
1140
1190
|
if (this.mode === "picker" && this.picker) {
|
|
1141
|
-
return [rule, this.picker.footer(this.picker.itemCount()),
|
|
1191
|
+
return [rule, this.picker.footer(this.picker.itemCount()), bottom];
|
|
1142
1192
|
}
|
|
1143
|
-
return [rule, "",
|
|
1193
|
+
return [rule, "", bottom];
|
|
1144
1194
|
}
|
|
1145
1195
|
statusLine() {
|
|
1146
1196
|
const spin = tone.accent(glyphs.spinner[this.spinnerFrame % glyphs.spinner.length] ?? "-");
|
|
@@ -2687,6 +2737,7 @@ class TuiApp {
|
|
|
2687
2737
|
["sessions picker", "ctrl+r rename · ctrl+p pin · ctrl+d delete"],
|
|
2688
2738
|
["↑ ↓", "input history — previous / next message"],
|
|
2689
2739
|
["wheel", "scroll the conversation"],
|
|
2740
|
+
["ctrl+↓", "jump to the bottom of the chat (or click the ↓ button)"],
|
|
2690
2741
|
["ctrl+p/ctrl+n", "input history (same as ↑ ↓)"],
|
|
2691
2742
|
["tab / badge", "switch plan/build · click the header badge · complete commands"],
|
|
2692
2743
|
["ctrl+z", "undo the last message (works while thinking)"],
|
|
@@ -2705,13 +2756,23 @@ class TuiApp {
|
|
|
2705
2756
|
this.append(lines);
|
|
2706
2757
|
}
|
|
2707
2758
|
userBlockLines(input, imageCount = 0) {
|
|
2759
|
+
const width = Math.max(20, this.contentWidth() - 2);
|
|
2708
2760
|
const lines = input.split("\n");
|
|
2709
|
-
const
|
|
2710
|
-
|
|
2711
|
-
|
|
2712
|
-
|
|
2713
|
-
|
|
2714
|
-
|
|
2761
|
+
const out = [];
|
|
2762
|
+
for (let index = 0; index < lines.length; index++) {
|
|
2763
|
+
const line = lines[index];
|
|
2764
|
+
if (line === "") {
|
|
2765
|
+
out.push("");
|
|
2766
|
+
continue;
|
|
2767
|
+
}
|
|
2768
|
+
const prefix = index === 0 ? ` ${tone.accent(glyphs.prompt)} ` : " ";
|
|
2769
|
+
const kind = index === 0 ? "bold" : "plain";
|
|
2770
|
+
out.push(...wrapSegments([{ text: line, kind }], prefix, " ", width));
|
|
2771
|
+
}
|
|
2772
|
+
if (imageCount > 0) {
|
|
2773
|
+
out.push(` ${tone.mist(`[${imageCount} image${imageCount === 1 ? "" : "s"} attached]`)}`);
|
|
2774
|
+
}
|
|
2775
|
+
return out;
|
|
2715
2776
|
}
|
|
2716
2777
|
async saveTurn(input, result, durationMs) {
|
|
2717
2778
|
const cost = estimateCost(result.usage, this.model, this.providerId);
|