@hheei/pi-ext-ui 0.8.4 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/tool-group-view.js +48 -33
- package/package.json +1 -1
package/dist/tool-group-view.js
CHANGED
|
@@ -144,6 +144,12 @@ function thinkingPreview(text) {
|
|
|
144
144
|
}
|
|
145
145
|
return { head, more: content !== head };
|
|
146
146
|
}
|
|
147
|
+
function isLiveMember(member) {
|
|
148
|
+
return member.status === "running" || member.status === "streaming";
|
|
149
|
+
}
|
|
150
|
+
function isFailedMember(member) {
|
|
151
|
+
return member.status === "error";
|
|
152
|
+
}
|
|
147
153
|
/**
|
|
148
154
|
* The collapsed frame of one activity block.
|
|
149
155
|
*
|
|
@@ -171,16 +177,21 @@ export class ToolGroupView extends Container {
|
|
|
171
177
|
#category;
|
|
172
178
|
#item;
|
|
173
179
|
#renderedExpanded = false;
|
|
174
|
-
#shell
|
|
180
|
+
#shell;
|
|
175
181
|
/** When the block last became quiet; the surface hold counts from here. */
|
|
176
182
|
#settledAt;
|
|
177
183
|
#holdTimer;
|
|
178
184
|
#hits = [];
|
|
179
185
|
#heading;
|
|
180
186
|
#body;
|
|
181
|
-
// One extra colored row above and below: the block needs breathing
|
|
182
|
-
|
|
187
|
+
// One extra colored row above and below while the surface is filled: the block needs breathing
|
|
188
|
+
// room inside its background, and a transparent block has nothing to breathe in. `Box` has no
|
|
189
|
+
// vertical setter, so the box is rebuilt when the fill flips.
|
|
190
|
+
#box = new Box(0, 0);
|
|
191
|
+
#filled;
|
|
183
192
|
#spacer = new Spacer(1);
|
|
193
|
+
/** The blank row between the expanded title and the first member. */
|
|
194
|
+
#gap = new Spacer(1);
|
|
184
195
|
constructor(ui, redraw, background, outputPad) {
|
|
185
196
|
super();
|
|
186
197
|
this.#ui = ui;
|
|
@@ -198,11 +209,7 @@ export class ToolGroupView extends Container {
|
|
|
198
209
|
handleMouse: (event) => this.#hit(event),
|
|
199
210
|
invalidate() { },
|
|
200
211
|
};
|
|
201
|
-
|
|
202
|
-
// at the width they were laid out for; a tool's own outputPad must not be counted twice.
|
|
203
|
-
this.#box.addChild(this.#heading);
|
|
204
|
-
this.#box.addChild(this.#body);
|
|
205
|
-
this.children = [this.#spacer, this.#box];
|
|
212
|
+
this.#syncShell();
|
|
206
213
|
}
|
|
207
214
|
#toggleRaw() {
|
|
208
215
|
this.#raw = this.#ui.getToolsExpanded() ? false : !this.#raw;
|
|
@@ -242,35 +249,23 @@ export class ToolGroupView extends Container {
|
|
|
242
249
|
return this.#tail === undefined ? [...this.#members] : [...this.#members, this.#tail];
|
|
243
250
|
}
|
|
244
251
|
/** The block's own last fact: the newest member that states a status, tail included. */
|
|
245
|
-
#lastStatus() {
|
|
246
|
-
const block = this.#block();
|
|
247
|
-
for (let index = block.length - 1; index >= 0; index -= 1) {
|
|
248
|
-
const status = block[index]?.status;
|
|
249
|
-
if (status !== undefined)
|
|
250
|
-
return status;
|
|
251
|
-
}
|
|
252
|
-
return undefined;
|
|
253
|
-
}
|
|
254
252
|
/**
|
|
255
|
-
* The surface states the block
|
|
256
|
-
*
|
|
257
|
-
*
|
|
258
|
-
* of colored panels.
|
|
253
|
+
* The surface states the block, decided once: pending until a call settles, then the settled color
|
|
254
|
+
* for five seconds, then the neutral surface. Reading the last call's status instead re-tinted the
|
|
255
|
+
* block on every call, which read as flashing while one request ran several tools.
|
|
259
256
|
*/
|
|
260
257
|
#surfaceToken() {
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
258
|
+
if (this.#settledAt === undefined) {
|
|
259
|
+
const stated = this.#block().filter((member) => member.status !== undefined);
|
|
260
|
+
if (stated.length === 0 || stated.some(isLiveMember))
|
|
261
|
+
return "toolPendingBg";
|
|
262
|
+
this.#settledAt = performance.now();
|
|
266
263
|
}
|
|
267
|
-
const
|
|
268
|
-
|
|
269
|
-
const held = now - this.#settledAt;
|
|
270
|
-
if (last === undefined || held >= SURFACE_HOLD_MS)
|
|
264
|
+
const held = performance.now() - this.#settledAt;
|
|
265
|
+
if (held >= SURFACE_HOLD_MS)
|
|
271
266
|
return "toolPendingBg";
|
|
272
267
|
this.#hold(SURFACE_HOLD_MS - held);
|
|
273
|
-
return
|
|
268
|
+
return this.#block().some(isFailedMember) ? "toolErrorBg" : "toolSuccessBg";
|
|
274
269
|
}
|
|
275
270
|
/** One repaint when the hold expires; nothing keeps ticking after that. */
|
|
276
271
|
#hold(delay) {
|
|
@@ -695,8 +690,16 @@ export class ToolGroupView extends Container {
|
|
|
695
690
|
}
|
|
696
691
|
this.#renderedExpanded = this.#raw || globalExpanded;
|
|
697
692
|
const theme = this.#ui.theme;
|
|
693
|
+
// The fill and its breathing rows belong to one surface: a transparent block keeps neither, so
|
|
694
|
+
// turning tool backgrounds off really removes them instead of leaving blank rows behind.
|
|
695
|
+
const filled = this.#background();
|
|
696
|
+
if (this.#filled !== filled) {
|
|
697
|
+
this.#filled = filled;
|
|
698
|
+
this.#box = new Box(0, filled ? 1 : 0);
|
|
699
|
+
this.#shell = undefined;
|
|
700
|
+
}
|
|
698
701
|
this.#syncShell();
|
|
699
|
-
this.#box.setBgFn(backgroundFill(theme, this.#surfaceToken()));
|
|
702
|
+
this.#box.setBgFn(filled ? backgroundFill(theme, this.#surfaceToken()) : undefined);
|
|
700
703
|
const calls = this.#calls();
|
|
701
704
|
const elapsed = this.#clock === undefined
|
|
702
705
|
? "—"
|
|
@@ -705,6 +708,9 @@ export class ToolGroupView extends Container {
|
|
|
705
708
|
theme.fg("text", "collapse views"),
|
|
706
709
|
theme.fg("dim", elapsed),
|
|
707
710
|
theme.fg("dim", `${calls} ${calls === 1 ? "call" : "calls"}`),
|
|
711
|
+
// The expanded view has no frame to hold a footer, so its totals join the title instead of
|
|
712
|
+
// adding a row after the last member.
|
|
713
|
+
...(this.#renderedExpanded ? [this.#footerRow()] : []),
|
|
708
714
|
].join(theme.fg("dim", " · "));
|
|
709
715
|
const pad = Math.max(0, this.#outputPad());
|
|
710
716
|
this.#header.setText(fitRow(`${" ".repeat(pad)}${theme.fg("dim", this.#renderedExpanded ? "▾" : "▸")} ${details}`, Math.max(0, width - pad * 2), "…"));
|
|
@@ -719,7 +725,16 @@ export class ToolGroupView extends Container {
|
|
|
719
725
|
if (this.#shell === shell)
|
|
720
726
|
return;
|
|
721
727
|
this.#shell = shell;
|
|
728
|
+
// The title owns the surface in both shells. A collapsed frame keeps its body inside it; the
|
|
729
|
+
// expanded one leaves every member its own styling and separates the title from them by one
|
|
730
|
+
// blank row, so the block never tints the whole conversation.
|
|
731
|
+
this.#box.clear();
|
|
732
|
+
this.#box.addChild(this.#heading);
|
|
733
|
+
if (shell === "collapsed")
|
|
734
|
+
this.#box.addChild(this.#body);
|
|
722
735
|
this.children =
|
|
723
|
-
shell === "raw"
|
|
736
|
+
shell === "raw"
|
|
737
|
+
? [this.#spacer, this.#box, this.#gap, this.#body]
|
|
738
|
+
: [this.#spacer, this.#box];
|
|
724
739
|
}
|
|
725
740
|
}
|