pi-extension-utils 0.5.1 → 0.6.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/src/client/index.js +4 -3
- package/dist/src/pane/overlay.d.ts +6 -1
- package/dist/src/pane/overlay.js +14 -4
- package/dist/src/ui/client.d.ts +12 -1
- package/dist/src/ui/client.js +30 -2
- package/dist/src/widgets/client.d.ts +3 -0
- package/dist/src/widgets/client.js +13 -2
- package/package.json +1 -1
package/dist/src/client/index.js
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { createRemindersClient } from "../reminders/client.js";
|
|
2
|
-
import { createUiClient } from "../ui/client.js";
|
|
2
|
+
import { createTuiModeCapture, createUiClient } from "../ui/client.js";
|
|
3
3
|
import { connectWidgetCoordinator } from "../widgets/client.js";
|
|
4
4
|
let nextClientId = 1;
|
|
5
5
|
export function connect(pi, opts) {
|
|
6
6
|
const clientId = opts.clientId ?? `client-${process.pid}-${Date.now()}-${nextClientId++}`;
|
|
7
7
|
let disposed = false;
|
|
8
|
-
const
|
|
8
|
+
const tuiCapture = createTuiModeCapture();
|
|
9
|
+
const coordinator = connectWidgetCoordinator(pi, { ctx: opts.ctx, clientId, tui: tuiCapture });
|
|
9
10
|
return {
|
|
10
11
|
clientId,
|
|
11
12
|
get mode() {
|
|
@@ -13,7 +14,7 @@ export function connect(pi, opts) {
|
|
|
13
14
|
},
|
|
14
15
|
widgets: coordinator.widgets,
|
|
15
16
|
fullscreen: coordinator.fullscreen,
|
|
16
|
-
ui: createUiClient(coordinator.fullscreen, opts.ctx),
|
|
17
|
+
ui: createUiClient(coordinator.fullscreen, opts.ctx, tuiCapture),
|
|
17
18
|
reminders: createRemindersClient(pi, () => disposed),
|
|
18
19
|
dispose() {
|
|
19
20
|
if (disposed)
|
|
@@ -72,7 +72,12 @@ export interface PaneOverlaySplitOptions {
|
|
|
72
72
|
export interface PaneCollapseOptions {
|
|
73
73
|
key?: string;
|
|
74
74
|
collapsedWidth?: number;
|
|
75
|
-
|
|
75
|
+
/**
|
|
76
|
+
* Legend/divider label for the collapse toggle. A function receives the
|
|
77
|
+
* current collapsed state so the hint can read e.g. "hide sidebar" when open
|
|
78
|
+
* and "open sidebar" when collapsed.
|
|
79
|
+
*/
|
|
80
|
+
label?: string | ((collapsed: boolean) => string);
|
|
76
81
|
}
|
|
77
82
|
export interface PaneOverlayOptions<T = unknown, Row = unknown> {
|
|
78
83
|
primary: PrimaryPaneOptions<Row>;
|
package/dist/src/pane/overlay.js
CHANGED
|
@@ -89,6 +89,10 @@ export function paneOverlay(options) {
|
|
|
89
89
|
let finished = false;
|
|
90
90
|
let focus = "primary";
|
|
91
91
|
let collapsed = false;
|
|
92
|
+
const collapseLabel = (fallback) => {
|
|
93
|
+
const label = collapse?.label;
|
|
94
|
+
return typeof label === "function" ? label(collapsed) : (label ?? fallback);
|
|
95
|
+
};
|
|
92
96
|
let leftFraction = split.initialFraction ?? 0.5;
|
|
93
97
|
let lastRenderWidth = 80;
|
|
94
98
|
let lastSelectedKey;
|
|
@@ -339,7 +343,7 @@ export function paneOverlay(options) {
|
|
|
339
343
|
if (!collapsed)
|
|
340
344
|
entries.push({ key: "[/]", label: "resize" });
|
|
341
345
|
if (collapse)
|
|
342
|
-
entries.push({ key: collapseKey, label:
|
|
346
|
+
entries.push({ key: collapseKey, label: collapseLabel("collapse") });
|
|
343
347
|
for (const action of customActions) {
|
|
344
348
|
if (action.showInLegend === false)
|
|
345
349
|
continue;
|
|
@@ -453,10 +457,16 @@ export function paneOverlay(options) {
|
|
|
453
457
|
: primaryRows.length > primaryHeight
|
|
454
458
|
? formatScrollInfo(primaryState.scrollOffset, Math.max(0, primaryRows.length - primaryHeight), { style: "position" })
|
|
455
459
|
: "");
|
|
456
|
-
const
|
|
457
|
-
|
|
460
|
+
const detailFooterBase = resolveValue(options.detail.footer, ctx) ??
|
|
461
|
+
(detailRows.length > detailHeight
|
|
458
462
|
? formatScrollInfo(detailState.scrollOffset, Math.max(0, detailRows.length - detailHeight), { style: "position" })
|
|
459
463
|
: "");
|
|
464
|
+
// When the primary pane is collapsed its legend (and the collapse hint with
|
|
465
|
+
// it) disappears, so the user can't see how to reopen it. Surface the
|
|
466
|
+
// reopen hint in the always-visible detail footer instead.
|
|
467
|
+
const detailFooterText = collapsed && collapse
|
|
468
|
+
? [`${collapseKey} ${collapseLabel("expand")}`, detailFooterBase].filter(Boolean).join(" \u00b7 ")
|
|
469
|
+
: detailFooterBase;
|
|
460
470
|
const bottomPrimary = titledBottomSegment(chromeTheme, primaryWidth, primaryFooterText, focus === "primary");
|
|
461
471
|
const bottomDetail = titledBottomSegment(chromeTheme, detailWidth, detailFooterText, focus === "detail");
|
|
462
472
|
const bottomBorder = primaryWidth === 0
|
|
@@ -474,7 +484,7 @@ export function paneOverlay(options) {
|
|
|
474
484
|
primaryCell = infoLines[row - infoStart - 1] ?? "";
|
|
475
485
|
}
|
|
476
486
|
else if (legendPlacement === "primary" && row === legendStart && legendLines.length > 0) {
|
|
477
|
-
primaryCell = flatRule(chromeTheme, collapse
|
|
487
|
+
primaryCell = flatRule(chromeTheme, collapse ? collapseLabel("actions") : "actions", primaryWidth);
|
|
478
488
|
}
|
|
479
489
|
if (legendPlacement === "primary" && row > legendStart) {
|
|
480
490
|
const legendIndex = row - legendStart - 1;
|
package/dist/src/ui/client.d.ts
CHANGED
|
@@ -4,6 +4,17 @@ export interface FullscreenLeaseLike {
|
|
|
4
4
|
export interface FullscreenController {
|
|
5
5
|
acquire(): FullscreenLeaseLike;
|
|
6
6
|
}
|
|
7
|
+
/**
|
|
8
|
+
* Lazily-captured TUI reference used to detect the fullscreen TUI mode.
|
|
9
|
+
* Populated whenever pi invokes any wrapped UI factory (widget, footer,
|
|
10
|
+
* editor, custom component). Structural on purpose: the helper must not
|
|
11
|
+
* depend on pi-tui types directly.
|
|
12
|
+
*/
|
|
13
|
+
export interface TuiModeCapture {
|
|
14
|
+
noteTui(tui: unknown): void;
|
|
15
|
+
isFullscreenTui(): boolean;
|
|
16
|
+
}
|
|
17
|
+
export declare function createTuiModeCapture(): TuiModeCapture;
|
|
7
18
|
/**
|
|
8
19
|
* Factory signature accepted by `ctx.ui.custom`. Typed structurally so the
|
|
9
20
|
* helper does not depend on pi-tui types directly.
|
|
@@ -19,4 +30,4 @@ export interface UiClient {
|
|
|
19
30
|
}
|
|
20
31
|
export declare function createUiClient(fullscreen: FullscreenController, ctx: {
|
|
21
32
|
ui: unknown;
|
|
22
|
-
}): UiClient;
|
|
33
|
+
}, capture?: TuiModeCapture): UiClient;
|
package/dist/src/ui/client.js
CHANGED
|
@@ -1,4 +1,17 @@
|
|
|
1
|
-
export function
|
|
1
|
+
export function createTuiModeCapture() {
|
|
2
|
+
let mode;
|
|
3
|
+
return {
|
|
4
|
+
noteTui(tui) {
|
|
5
|
+
const value = tui?.mode;
|
|
6
|
+
if (typeof value === "string")
|
|
7
|
+
mode = value;
|
|
8
|
+
},
|
|
9
|
+
isFullscreenTui() {
|
|
10
|
+
return mode === "fullscreen";
|
|
11
|
+
},
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
export function createUiClient(fullscreen, ctx, capture = createTuiModeCapture()) {
|
|
2
15
|
return {
|
|
3
16
|
async fullscreen(factory) {
|
|
4
17
|
const ui = ctx.ui;
|
|
@@ -7,7 +20,22 @@ export function createUiClient(fullscreen, ctx) {
|
|
|
7
20
|
}
|
|
8
21
|
const lease = fullscreen.acquire();
|
|
9
22
|
try {
|
|
10
|
-
|
|
23
|
+
const wrapped = (tui, theme, keybindings, done) => {
|
|
24
|
+
capture.noteTui(tui);
|
|
25
|
+
return factory(tui, theme, keybindings, done);
|
|
26
|
+
};
|
|
27
|
+
// In the fullscreen TUI a plain custom component mounts inside the
|
|
28
|
+
// bottom dock (the editor slot), where the dock layout shrinks and
|
|
29
|
+
// clips it while the transcript tail renders above it. Mount it as a
|
|
30
|
+
// full-screen overlay instead so the component owns the whole screen,
|
|
31
|
+
// matching the regular-TUI behavior it was designed for.
|
|
32
|
+
const options = capture.isFullscreenTui()
|
|
33
|
+
? {
|
|
34
|
+
overlay: true,
|
|
35
|
+
overlayOptions: { anchor: "top-left", width: "100%", maxHeight: "100%" },
|
|
36
|
+
}
|
|
37
|
+
: undefined;
|
|
38
|
+
return await ui.custom(wrapped, options);
|
|
11
39
|
}
|
|
12
40
|
finally {
|
|
13
41
|
lease.release();
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { ExtensionAPI, ExtensionContext } from "@earendil-works/pi-coding-agent";
|
|
2
|
+
import { type TuiModeCapture } from "../ui/client.ts";
|
|
2
3
|
import { type WidgetFactory, type WidgetPlacement } from "./protocol.ts";
|
|
3
4
|
export interface WidgetSetOptions {
|
|
4
5
|
order?: number;
|
|
@@ -22,6 +23,8 @@ export interface WidgetCoordinatorClient {
|
|
|
22
23
|
interface WidgetCoordinatorClientOptions {
|
|
23
24
|
ctx: ExtensionContext;
|
|
24
25
|
clientId: string;
|
|
26
|
+
/** Optional shared TUI-mode capture fed by every widget factory invocation. */
|
|
27
|
+
tui?: TuiModeCapture;
|
|
25
28
|
}
|
|
26
29
|
export declare function connectWidgetCoordinator(pi: ExtensionAPI, opts: WidgetCoordinatorClientOptions): WidgetCoordinatorClient;
|
|
27
30
|
export {};
|
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
import { basePayload, EVENTS, PROTOCOL_VERSION } from "./protocol.js";
|
|
2
2
|
let nextLeaseId = 1;
|
|
3
|
+
function withTuiCapture(factory, capture) {
|
|
4
|
+
return (tui, theme) => {
|
|
5
|
+
capture.noteTui(tui);
|
|
6
|
+
return factory(tui, theme);
|
|
7
|
+
};
|
|
8
|
+
}
|
|
3
9
|
export function connectWidgetCoordinator(pi, opts) {
|
|
4
10
|
const { clientId } = opts;
|
|
5
11
|
const widgets = new Map();
|
|
@@ -78,13 +84,18 @@ export function connectWidgetCoordinator(pi, opts) {
|
|
|
78
84
|
set(placement, key, factory, setOpts = {}) {
|
|
79
85
|
if (disposed)
|
|
80
86
|
return;
|
|
81
|
-
const record = {
|
|
87
|
+
const record = {
|
|
88
|
+
placement,
|
|
89
|
+
key,
|
|
90
|
+
factory: opts.tui ? withTuiCapture(factory, opts.tui) : factory,
|
|
91
|
+
order: setOpts.order ?? 0,
|
|
92
|
+
};
|
|
82
93
|
widgets.set(widgetId(placement, key), record);
|
|
83
94
|
if (coordinated) {
|
|
84
95
|
emitRegister(record);
|
|
85
96
|
}
|
|
86
97
|
else if (leases.size === 0) {
|
|
87
|
-
opts.ctx.ui.setWidget(key, factory, { placement });
|
|
98
|
+
opts.ctx.ui.setWidget(key, record.factory, { placement });
|
|
88
99
|
}
|
|
89
100
|
},
|
|
90
101
|
remove(placement, key) {
|