pi-btw 0.4.0 → 0.4.1
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/extensions/btw.ts +59 -15
- package/package.json +4 -4
package/extensions/btw.ts
CHANGED
|
@@ -1001,6 +1001,9 @@ function notify(ctx: ExtensionContext | ExtensionCommandContext, message: string
|
|
|
1001
1001
|
}
|
|
1002
1002
|
}
|
|
1003
1003
|
|
|
1004
|
+
/** Fixed overlay rows outside the transcript viewport (must match render() structure). */
|
|
1005
|
+
const BTW_OVERLAY_CHROME_LINES = 9;
|
|
1006
|
+
|
|
1004
1007
|
function getOverlayTitle(mode: BtwThreadMode): string {
|
|
1005
1008
|
return mode === "tangent" ? "BTW tangent" : "BTW";
|
|
1006
1009
|
}
|
|
@@ -1085,6 +1088,9 @@ class BtwOverlayComponent extends Container implements Focusable {
|
|
|
1085
1088
|
|
|
1086
1089
|
this.hintsText = new Text("", 1, 0);
|
|
1087
1090
|
|
|
1091
|
+
// Enable SGR mouse reporting so wheel/touchpad events reach handleInput().
|
|
1092
|
+
this.tui.terminal?.write?.("\x1b[?1000h\x1b[?1006h");
|
|
1093
|
+
|
|
1088
1094
|
const originalHandleInput = this.input.handleInput.bind(this.input);
|
|
1089
1095
|
this.input.handleInput = (data: string) => {
|
|
1090
1096
|
if (keybindings.matches(data, "app.clear")) {
|
|
@@ -1111,17 +1117,17 @@ class BtwOverlayComponent extends Container implements Focusable {
|
|
|
1111
1117
|
private frameLine(content: string, innerWidth: number): string {
|
|
1112
1118
|
const truncated = truncateToWidth(content, innerWidth, "");
|
|
1113
1119
|
const padding = Math.max(0, innerWidth - visibleWidth(truncated));
|
|
1114
|
-
return `${this.theme.fg("
|
|
1120
|
+
return `${this.theme.fg("border", "│")}${truncated}${" ".repeat(padding)}${this.theme.fg("border", "│")}`;
|
|
1115
1121
|
}
|
|
1116
1122
|
|
|
1117
1123
|
private ruleLine(innerWidth: number): string {
|
|
1118
|
-
return this.theme.fg("
|
|
1124
|
+
return this.theme.fg("border", `├${"─".repeat(innerWidth)}┤`);
|
|
1119
1125
|
}
|
|
1120
1126
|
|
|
1121
1127
|
private borderLine(innerWidth: number, edge: "top" | "bottom"): string {
|
|
1122
1128
|
const left = edge === "top" ? "┌" : "└";
|
|
1123
1129
|
const right = edge === "top" ? "┐" : "┘";
|
|
1124
|
-
return this.theme.fg("
|
|
1130
|
+
return this.theme.fg("border", `${left}${"─".repeat(innerWidth)}${right}`);
|
|
1125
1131
|
}
|
|
1126
1132
|
|
|
1127
1133
|
private wrapTranscript(innerWidth: number): string[] {
|
|
@@ -1141,22 +1147,53 @@ class BtwOverlayComponent extends Container implements Focusable {
|
|
|
1141
1147
|
return Math.max(18, Math.min(32, Math.floor(terminalRows * 0.78)));
|
|
1142
1148
|
}
|
|
1143
1149
|
|
|
1150
|
+
private scrollTranscript(delta: number): void {
|
|
1151
|
+
if (delta < 0) {
|
|
1152
|
+
this.followTranscript = false;
|
|
1153
|
+
}
|
|
1154
|
+
this.transcriptScrollOffset = Math.max(0, this.transcriptScrollOffset + delta);
|
|
1155
|
+
this.tui.requestRender();
|
|
1156
|
+
}
|
|
1157
|
+
|
|
1158
|
+
dispose(): void {
|
|
1159
|
+
this.tui.terminal?.write?.("\x1b[?1000l\x1b[?1006l");
|
|
1160
|
+
}
|
|
1161
|
+
|
|
1162
|
+
private getMouseScrollDelta(data: string): number | null {
|
|
1163
|
+
const match = data.match(/^\x1b\[<(\d+);\d+;\d+[Mm]$/);
|
|
1164
|
+
if (!match) {
|
|
1165
|
+
return null;
|
|
1166
|
+
}
|
|
1167
|
+
|
|
1168
|
+
const button = Number(match[1]);
|
|
1169
|
+
if ((button & 64) !== 64) {
|
|
1170
|
+
return null;
|
|
1171
|
+
}
|
|
1172
|
+
|
|
1173
|
+
return (button & 1) === 0 ? -3 : 3;
|
|
1174
|
+
}
|
|
1175
|
+
|
|
1144
1176
|
handleInput(data: string): void {
|
|
1145
1177
|
if (matchesBtwFocusShortcut(data)) {
|
|
1146
1178
|
this.onUnfocusCallback();
|
|
1147
1179
|
return;
|
|
1148
1180
|
}
|
|
1149
1181
|
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
this.
|
|
1153
|
-
|
|
1182
|
+
const mouseScrollDelta = this.getMouseScrollDelta(data);
|
|
1183
|
+
if (mouseScrollDelta !== null) {
|
|
1184
|
+
this.scrollTranscript(mouseScrollDelta);
|
|
1185
|
+
return;
|
|
1186
|
+
}
|
|
1187
|
+
|
|
1188
|
+
if (matchesKey(data, Key.pageUp) || matchesKey(data, Key.up)) {
|
|
1189
|
+
const step = matchesKey(data, Key.pageUp) ? Math.max(1, this.transcriptViewportHeight - 1) : 1;
|
|
1190
|
+
this.scrollTranscript(-step);
|
|
1154
1191
|
return;
|
|
1155
1192
|
}
|
|
1156
1193
|
|
|
1157
|
-
if (matchesKey(data, Key.pageDown)) {
|
|
1158
|
-
|
|
1159
|
-
this.
|
|
1194
|
+
if (matchesKey(data, Key.pageDown) || matchesKey(data, Key.down)) {
|
|
1195
|
+
const step = matchesKey(data, Key.pageDown) ? Math.max(1, this.transcriptViewportHeight - 1) : 1;
|
|
1196
|
+
this.scrollTranscript(step);
|
|
1160
1197
|
return;
|
|
1161
1198
|
}
|
|
1162
1199
|
|
|
@@ -1172,19 +1209,25 @@ class BtwOverlayComponent extends Container implements Focusable {
|
|
|
1172
1209
|
// the row stays geometrically stable while the overlay still owns keyboard input.
|
|
1173
1210
|
this.input.focused = false;
|
|
1174
1211
|
try {
|
|
1175
|
-
const
|
|
1176
|
-
|
|
1212
|
+
const renderedInputLine = this.input.render(targetWidth)[0] ?? "";
|
|
1213
|
+
const inputLine = truncateToWidth(renderedInputLine, targetWidth, "");
|
|
1214
|
+
const padding = Math.max(0, targetWidth - visibleWidth(inputLine));
|
|
1215
|
+
return `${this.theme.fg("border", "│")}${inputLine}${" ".repeat(padding)}${this.theme.fg("border", "│")}`;
|
|
1177
1216
|
} finally {
|
|
1178
1217
|
this.input.focused = previousFocused;
|
|
1179
1218
|
}
|
|
1180
1219
|
}
|
|
1181
1220
|
|
|
1221
|
+
private fitRenderedLine(line: string, width: number): string {
|
|
1222
|
+
return visibleWidth(line) > width ? truncateToWidth(line, width, "") : line;
|
|
1223
|
+
}
|
|
1224
|
+
|
|
1182
1225
|
override render(width: number): string[] {
|
|
1183
1226
|
const dialogWidth = Math.max(24, width);
|
|
1184
1227
|
const innerWidth = Math.max(22, dialogWidth - 2);
|
|
1185
1228
|
const transcriptLines = this.wrapTranscript(innerWidth);
|
|
1186
1229
|
const dialogHeight = this.getDialogHeight();
|
|
1187
|
-
const chromeHeight =
|
|
1230
|
+
const chromeHeight = BTW_OVERLAY_CHROME_LINES;
|
|
1188
1231
|
const transcriptHeight = Math.max(6, dialogHeight - chromeHeight);
|
|
1189
1232
|
this.transcriptViewportHeight = transcriptHeight;
|
|
1190
1233
|
|
|
@@ -1229,7 +1272,7 @@ class BtwOverlayComponent extends Container implements Focusable {
|
|
|
1229
1272
|
lines.push(this.frameLine(this.theme.fg("dim", this.hintsTextValue.trim()), innerWidth));
|
|
1230
1273
|
lines.push(this.borderLine(innerWidth, "bottom"));
|
|
1231
1274
|
|
|
1232
|
-
return lines;
|
|
1275
|
+
return lines.map((line) => this.fitRenderedLine(line, width));
|
|
1233
1276
|
}
|
|
1234
1277
|
|
|
1235
1278
|
setDraft(value: string): void {
|
|
@@ -1263,7 +1306,7 @@ class BtwOverlayComponent extends Container implements Focusable {
|
|
|
1263
1306
|
const status = this.getStatus() ?? "Ready. Enter submits; Escape dismisses without clearing.";
|
|
1264
1307
|
this.statusTextValue = status;
|
|
1265
1308
|
this.statusText.setText(this.statusTextValue);
|
|
1266
|
-
this.hintsTextValue = "
|
|
1309
|
+
this.hintsTextValue = "Scroll wheel ↑↓ PgUp/PgDn · Enter · Alt+/ focus · Esc";
|
|
1267
1310
|
this.hintsText.setText(this.hintsTextValue);
|
|
1268
1311
|
this.tui.requestRender();
|
|
1269
1312
|
}
|
|
@@ -1655,6 +1698,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
1655
1698
|
};
|
|
1656
1699
|
runtime.close = () => {
|
|
1657
1700
|
overlayDraft = overlay.getDraft();
|
|
1701
|
+
overlay.dispose();
|
|
1658
1702
|
closeRuntime();
|
|
1659
1703
|
};
|
|
1660
1704
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-btw",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.1",
|
|
4
4
|
"description": "A pi extension for parallel side conversations with /btw",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -42,9 +42,9 @@
|
|
|
42
42
|
"image": "https://raw.githubusercontent.com/dbachelder/pi-btw/main/docs/btw-overlay.png"
|
|
43
43
|
},
|
|
44
44
|
"peerDependencies": {
|
|
45
|
-
"@earendil-works/pi-ai": "
|
|
46
|
-
"@earendil-works/pi-coding-agent": "
|
|
47
|
-
"@earendil-works/pi-tui": "
|
|
45
|
+
"@earendil-works/pi-ai": ">=0.74.0 <1",
|
|
46
|
+
"@earendil-works/pi-coding-agent": ">=0.74.0 <1",
|
|
47
|
+
"@earendil-works/pi-tui": ">=0.74.0 <1"
|
|
48
48
|
},
|
|
49
49
|
"devDependencies": {
|
|
50
50
|
"typescript": "^6.0.2",
|