pi-long-task 0.3.6 → 0.3.7
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/package.json +1 -1
- package/src/index.ts +61 -52
package/package.json
CHANGED
package/src/index.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { ExtensionAPI, ExtensionContext, Theme } from "@earendil-works/pi-coding-agent";
|
|
2
2
|
import type { AssistantMessage } from "@earendil-works/pi-ai";
|
|
3
|
-
import { truncateToWidth, type Component, type
|
|
3
|
+
import { truncateToWidth, type Component, type TUI } from "@earendil-works/pi-tui";
|
|
4
4
|
|
|
5
5
|
import { runCoordinator, type CoordinatorProgressUpdate, type CoordinatorResult } from "./coordinator.ts";
|
|
6
6
|
import { longTaskInputTransform } from "./input_router.ts";
|
|
@@ -76,6 +76,9 @@ function toolDetails(result: CoordinatorResult) {
|
|
|
76
76
|
}
|
|
77
77
|
|
|
78
78
|
const LONG_TASK_WIDGET_KEY = "pi-long-task-sidebar";
|
|
79
|
+
const SIDEBAR_WIDGET_RESERVED_INPUT_ROWS = 8;
|
|
80
|
+
const SIDEBAR_WIDGET_MIN_ROWS = 4;
|
|
81
|
+
const SIDEBAR_WIDGET_MAX_ROWS = 24;
|
|
79
82
|
|
|
80
83
|
type UiContext = ExtensionContext;
|
|
81
84
|
|
|
@@ -86,10 +89,12 @@ export interface LongTaskSidebarController {
|
|
|
86
89
|
|
|
87
90
|
class PiLongTaskSidebarComponent implements Component {
|
|
88
91
|
private readonly theme: Theme;
|
|
92
|
+
private readonly maxRows: (() => number | undefined) | undefined;
|
|
89
93
|
private update: CoordinatorProgressUpdate | undefined;
|
|
90
94
|
|
|
91
|
-
constructor(theme: Theme) {
|
|
95
|
+
constructor(theme: Theme, maxRows?: () => number | undefined) {
|
|
92
96
|
this.theme = theme;
|
|
97
|
+
this.maxRows = maxRows;
|
|
93
98
|
}
|
|
94
99
|
|
|
95
100
|
setUpdate(update: CoordinatorProgressUpdate): void {
|
|
@@ -98,7 +103,8 @@ class PiLongTaskSidebarComponent implements Component {
|
|
|
98
103
|
}
|
|
99
104
|
|
|
100
105
|
render(width: number): string[] {
|
|
101
|
-
|
|
106
|
+
const lines = renderSidebarOverlayLines(this.update, this.theme, width);
|
|
107
|
+
return limitSidebarPanelLines(lines, this.theme, width, this.maxRows?.());
|
|
102
108
|
}
|
|
103
109
|
|
|
104
110
|
invalidate(): void {
|
|
@@ -112,48 +118,25 @@ export function createLongTaskSidebarController(ctx: UiContext | undefined): Lon
|
|
|
112
118
|
}
|
|
113
119
|
|
|
114
120
|
let latestUpdate: CoordinatorProgressUpdate | undefined;
|
|
115
|
-
let
|
|
116
|
-
let
|
|
117
|
-
let overlayDone: ((result: undefined) => void) | undefined;
|
|
118
|
-
let overlayHandle: OverlayHandle | undefined;
|
|
121
|
+
let sidebarComponent: PiLongTaskSidebarComponent | undefined;
|
|
122
|
+
let sidebarTui: TUI | undefined;
|
|
119
123
|
let closed = false;
|
|
120
124
|
|
|
121
|
-
ctx
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
overlayDone = done;
|
|
128
|
-
overlayComponent = new PiLongTaskSidebarComponent(theme);
|
|
125
|
+
if (supportsTuiWidget(ctx)) {
|
|
126
|
+
ctx.ui.setWidget(
|
|
127
|
+
LONG_TASK_WIDGET_KEY,
|
|
128
|
+
(tui, theme) => {
|
|
129
|
+
sidebarTui = tui;
|
|
130
|
+
sidebarComponent = new PiLongTaskSidebarComponent(theme, () => sidebarWidgetLineLimit(tui));
|
|
129
131
|
if (latestUpdate) {
|
|
130
|
-
|
|
131
|
-
}
|
|
132
|
-
if (closed) {
|
|
133
|
-
done(undefined);
|
|
132
|
+
sidebarComponent.setUpdate(latestUpdate);
|
|
134
133
|
}
|
|
135
|
-
return
|
|
136
|
-
},
|
|
137
|
-
{
|
|
138
|
-
overlay: true,
|
|
139
|
-
overlayOptions: {
|
|
140
|
-
anchor: "right-center",
|
|
141
|
-
width: "34%",
|
|
142
|
-
minWidth: 36,
|
|
143
|
-
maxHeight: "100%",
|
|
144
|
-
margin: 0,
|
|
145
|
-
nonCapturing: true,
|
|
146
|
-
visible: (termWidth, termHeight) => termWidth >= 96 && termHeight >= 16,
|
|
147
|
-
},
|
|
148
|
-
onHandle: (handle) => {
|
|
149
|
-
overlayHandle = handle;
|
|
150
|
-
handle.unfocus();
|
|
151
|
-
},
|
|
134
|
+
return sidebarComponent;
|
|
152
135
|
},
|
|
136
|
+
{ placement: "aboveEditor" },
|
|
153
137
|
);
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
});
|
|
138
|
+
} else {
|
|
139
|
+
ctx.ui.setWidget(LONG_TASK_WIDGET_KEY, ["Pi Long Task: preparing sidebar..."], { placement: "aboveEditor" });
|
|
157
140
|
}
|
|
158
141
|
|
|
159
142
|
return {
|
|
@@ -162,9 +145,11 @@ export function createLongTaskSidebarController(ctx: UiContext | undefined): Lon
|
|
|
162
145
|
return;
|
|
163
146
|
}
|
|
164
147
|
latestUpdate = update;
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
148
|
+
sidebarComponent?.setUpdate(update);
|
|
149
|
+
sidebarTui?.requestRender();
|
|
150
|
+
if (!sidebarComponent) {
|
|
151
|
+
ctx.ui.setWidget(LONG_TASK_WIDGET_KEY, renderSidebarWidgetLines(update), { placement: "aboveEditor" });
|
|
152
|
+
}
|
|
168
153
|
},
|
|
169
154
|
close(): void {
|
|
170
155
|
if (closed) {
|
|
@@ -172,24 +157,24 @@ export function createLongTaskSidebarController(ctx: UiContext | undefined): Lon
|
|
|
172
157
|
}
|
|
173
158
|
closed = true;
|
|
174
159
|
ctx.ui.setWidget(LONG_TASK_WIDGET_KEY, undefined);
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
} else {
|
|
178
|
-
overlayHandle?.hide();
|
|
179
|
-
}
|
|
180
|
-
overlayComponent = undefined;
|
|
181
|
-
overlayTui = undefined;
|
|
182
|
-
overlayDone = undefined;
|
|
183
|
-
overlayHandle = undefined;
|
|
160
|
+
sidebarComponent = undefined;
|
|
161
|
+
sidebarTui = undefined;
|
|
184
162
|
},
|
|
185
163
|
};
|
|
186
164
|
}
|
|
187
165
|
|
|
188
|
-
function
|
|
166
|
+
function supportsTuiWidget(ctx: UiContext): boolean {
|
|
189
167
|
const mode = (ctx as UiContext & { mode?: string }).mode;
|
|
190
168
|
return mode === "tui" || mode === undefined;
|
|
191
169
|
}
|
|
192
170
|
|
|
171
|
+
function sidebarWidgetLineLimit(tui: TUI): number {
|
|
172
|
+
const terminalRows = tui.terminal.rows;
|
|
173
|
+
const rows = Number.isFinite(terminalRows) ? Math.max(0, Math.floor(terminalRows)) : 24;
|
|
174
|
+
const availableRows = rows - SIDEBAR_WIDGET_RESERVED_INPUT_ROWS;
|
|
175
|
+
return Math.max(SIDEBAR_WIDGET_MIN_ROWS, Math.min(SIDEBAR_WIDGET_MAX_ROWS, availableRows));
|
|
176
|
+
}
|
|
177
|
+
|
|
193
178
|
function renderSidebarWidgetLines(update: CoordinatorProgressUpdate): string[] {
|
|
194
179
|
const progress = update.taskProgress;
|
|
195
180
|
const summary = progress?.summary;
|
|
@@ -229,6 +214,30 @@ function renderSidebarOverlayLines(
|
|
|
229
214
|
return rows.map((row) => sidebarPanelRow(row, contentWidth, theme));
|
|
230
215
|
}
|
|
231
216
|
|
|
217
|
+
function limitSidebarPanelLines(lines: string[], theme: Theme, width: number, maxRows: number | undefined): string[] {
|
|
218
|
+
if (maxRows === undefined || !Number.isFinite(maxRows)) {
|
|
219
|
+
return lines;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
const limit = Math.max(0, Math.floor(maxRows));
|
|
223
|
+
if (lines.length <= limit) {
|
|
224
|
+
return lines;
|
|
225
|
+
}
|
|
226
|
+
if (limit === 0) {
|
|
227
|
+
return [];
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
const contentWidth = Math.max(8, Math.max(28, width) - 4);
|
|
231
|
+
if (limit === 1) {
|
|
232
|
+
return [sidebarPanelRow(theme.fg("dim", "…"), contentWidth, theme)];
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
const visibleLines = lines.slice(0, limit - 1);
|
|
236
|
+
const omitted = lines.length - visibleLines.length;
|
|
237
|
+
visibleLines.push(sidebarPanelRow(theme.fg("dim", `… ${omitted} more`), contentWidth, theme));
|
|
238
|
+
return visibleLines;
|
|
239
|
+
}
|
|
240
|
+
|
|
232
241
|
function renderSidebarRows(update: CoordinatorProgressUpdate | undefined, theme: Theme, width: number): string[] {
|
|
233
242
|
if (!update) {
|
|
234
243
|
return ["", sidebarHeading("Pi Long Task", theme), "", theme.fg("muted", "Preparing long-task sidebar...")];
|