pi-extension-utils 0.3.0 → 0.3.2
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/README.md +90 -27
- package/dist/index.js +9 -140
- package/dist/src/client/index.d.ts +7 -0
- package/dist/src/client/index.js +25 -0
- package/dist/src/client/types.d.ts +17 -0
- package/dist/src/client/types.js +1 -0
- package/dist/src/config/index.d.ts +28 -0
- package/dist/src/config/index.js +222 -0
- package/dist/src/index.d.ts +10 -6
- package/dist/src/index.js +10 -6
- package/dist/src/logger/config.d.ts +11 -0
- package/dist/src/logger/config.js +27 -0
- package/dist/src/logger/index.d.ts +20 -0
- package/dist/src/logger/index.js +76 -0
- package/dist/src/{tui-chrome.js → pane/chrome.js} +22 -13
- package/dist/src/pane/overlay.d.ts +75 -0
- package/dist/src/pane/overlay.js +492 -0
- package/dist/src/reminders/client.d.ts +10 -0
- package/dist/src/reminders/client.js +49 -0
- package/dist/src/reminders/config.d.ts +5 -6
- package/dist/src/reminders/config.js +9 -23
- package/dist/src/reminders/debug.js +2 -2
- package/dist/src/reminders/host.d.ts +5 -0
- package/dist/src/reminders/host.js +298 -0
- package/dist/src/reminders/index.d.ts +3 -5
- package/dist/src/reminders/index.js +3 -308
- package/dist/src/reminders/types.d.ts +3 -0
- package/dist/src/reminders/types.js +3 -0
- package/dist/src/ui/client.d.ts +22 -0
- package/dist/src/ui/client.js +17 -0
- package/dist/src/utils-config.d.ts +24 -0
- package/dist/src/utils-config.js +38 -0
- package/dist/src/widgets/client.d.ts +27 -0
- package/dist/src/widgets/client.js +130 -0
- package/dist/src/widgets/host.d.ts +2 -0
- package/dist/src/widgets/host.js +143 -0
- package/docs/README.md +11 -0
- package/docs/client.md +91 -0
- package/docs/config.md +86 -0
- package/docs/pane-overlay.md +88 -0
- package/docs/reference/reminders-spec.md +351 -0
- package/docs/reminders.md +68 -0
- package/docs/widgets.md +57 -0
- package/examples/README.md +23 -0
- package/examples/config.ts +31 -0
- package/examples/logger.ts +20 -0
- package/examples/pane-overlay.ts +47 -0
- package/examples/reminders.ts +25 -0
- package/examples/widget-coordinator.ts +18 -0
- package/package.json +12 -1
- package/dist/src/client.d.ts +0 -47
- package/dist/src/client.js +0 -194
- package/dist/src/logger.d.ts +0 -12
- package/dist/src/logger.js +0 -45
- /package/dist/src/{tui-chrome.d.ts → pane/chrome.d.ts} +0 -0
- /package/dist/src/{key-dispatch.d.ts → pane/key-dispatch.d.ts} +0 -0
- /package/dist/src/{key-dispatch.js → pane/key-dispatch.js} +0 -0
- /package/dist/src/{pane-state.d.ts → pane/state.d.ts} +0 -0
- /package/dist/src/{pane-state.js → pane/state.js} +0 -0
- /package/dist/src/{protocol.d.ts → widgets/protocol.d.ts} +0 -0
- /package/dist/src/{protocol.js → widgets/protocol.js} +0 -0
package/dist/src/client.d.ts
DELETED
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
import type { ExtensionAPI, ExtensionContext } from "@earendil-works/pi-coding-agent";
|
|
2
|
-
import type { WidgetFactory, WidgetPlacement } from "./protocol.ts";
|
|
3
|
-
import { type ReminderAnnounceNowRequest, type ReminderIntent, type ReminderSnapshot } from "./reminders/types.ts";
|
|
4
|
-
export interface UtilsClientOptions {
|
|
5
|
-
ctx: ExtensionContext;
|
|
6
|
-
clientId?: string;
|
|
7
|
-
}
|
|
8
|
-
export interface WidgetSetOptions {
|
|
9
|
-
order?: number;
|
|
10
|
-
}
|
|
11
|
-
export interface FullscreenLease {
|
|
12
|
-
release(): void;
|
|
13
|
-
}
|
|
14
|
-
/**
|
|
15
|
-
* Factory signature accepted by `ctx.ui.custom`. Typed structurally so the
|
|
16
|
-
* client does not depend on pi-tui types directly.
|
|
17
|
-
*/
|
|
18
|
-
export type FullscreenComponentFactory<T> = (tui: unknown, theme: unknown, keybindings: unknown, done: (result: T) => void) => unknown;
|
|
19
|
-
export interface RemindersClient {
|
|
20
|
-
upsert(intent: ReminderIntent): void;
|
|
21
|
-
remove(source: string, key: string): void;
|
|
22
|
-
clearSource(source: string): void;
|
|
23
|
-
announceNow(payload?: ReminderAnnounceNowRequest): void;
|
|
24
|
-
list(source?: string): Promise<ReminderSnapshot>;
|
|
25
|
-
}
|
|
26
|
-
export interface UtilsClient {
|
|
27
|
-
readonly clientId: string;
|
|
28
|
-
readonly mode: "fallback" | "coordinated";
|
|
29
|
-
widgets: {
|
|
30
|
-
set(placement: WidgetPlacement, key: string, factory: WidgetFactory, opts?: WidgetSetOptions): void;
|
|
31
|
-
remove(placement: WidgetPlacement, key: string): void;
|
|
32
|
-
};
|
|
33
|
-
fullscreen: {
|
|
34
|
-
acquire(): FullscreenLease;
|
|
35
|
-
};
|
|
36
|
-
ui: {
|
|
37
|
-
/**
|
|
38
|
-
* Run a full-screen custom UI: acquires a fullscreen lease (blanking
|
|
39
|
-
* coordinated widgets), shows the component via `ctx.ui.custom`, and
|
|
40
|
-
* releases the lease in a finally — even if the component throws.
|
|
41
|
-
*/
|
|
42
|
-
fullscreen<T>(factory: FullscreenComponentFactory<T>): Promise<T>;
|
|
43
|
-
};
|
|
44
|
-
reminders: RemindersClient;
|
|
45
|
-
dispose(): void;
|
|
46
|
-
}
|
|
47
|
-
export declare function connect(pi: ExtensionAPI, opts: UtilsClientOptions): UtilsClient;
|
package/dist/src/client.js
DELETED
|
@@ -1,194 +0,0 @@
|
|
|
1
|
-
import { REMINDER_ANNOUNCE_NOW_EVENT, REMINDER_CLEAR_SOURCE_EVENT, REMINDER_LIST_EVENT, REMINDER_REMOVE_EVENT, REMINDER_UPSERT_EVENT, } from "./reminders/types.js";
|
|
2
|
-
import { basePayload, EVENTS, PROTOCOL_VERSION, } from "./protocol.js";
|
|
3
|
-
let nextClientId = 1;
|
|
4
|
-
let nextLeaseId = 1;
|
|
5
|
-
export function connect(pi, opts) {
|
|
6
|
-
const clientId = opts.clientId ?? `client-${process.pid}-${Date.now()}-${nextClientId++}`;
|
|
7
|
-
const widgets = new Map();
|
|
8
|
-
const leases = new Set();
|
|
9
|
-
let disposed = false;
|
|
10
|
-
let coordinated = false;
|
|
11
|
-
const widgetId = (placement, key) => `${placement}:${key}`;
|
|
12
|
-
function emitRegister(record) {
|
|
13
|
-
pi.events.emit(EVENTS.registerWidget, {
|
|
14
|
-
...basePayload(clientId),
|
|
15
|
-
placement: record.placement,
|
|
16
|
-
key: record.key,
|
|
17
|
-
order: record.order,
|
|
18
|
-
factory: record.factory,
|
|
19
|
-
});
|
|
20
|
-
}
|
|
21
|
-
function clearFallback(record) {
|
|
22
|
-
opts.ctx.ui.setWidget(record.key, undefined, { placement: record.placement });
|
|
23
|
-
}
|
|
24
|
-
function restoreFallback(record) {
|
|
25
|
-
opts.ctx.ui.setWidget(record.key, record.factory, { placement: record.placement });
|
|
26
|
-
}
|
|
27
|
-
function attach() {
|
|
28
|
-
if (disposed || coordinated)
|
|
29
|
-
return;
|
|
30
|
-
coordinated = true;
|
|
31
|
-
for (const record of widgets.values()) {
|
|
32
|
-
clearFallback(record);
|
|
33
|
-
emitRegister(record);
|
|
34
|
-
}
|
|
35
|
-
for (const token of leases) {
|
|
36
|
-
pi.events.emit(EVENTS.fullscreenAcquire, { ...basePayload(clientId), token });
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
const offReady = pi.events.on(EVENTS.ready, (data) => {
|
|
40
|
-
if (!isReadyPayload(data))
|
|
41
|
-
return;
|
|
42
|
-
attach();
|
|
43
|
-
});
|
|
44
|
-
pi.events.emit(EVENTS.hello, basePayload(clientId));
|
|
45
|
-
const client = {
|
|
46
|
-
clientId,
|
|
47
|
-
get mode() {
|
|
48
|
-
return coordinated ? "coordinated" : "fallback";
|
|
49
|
-
},
|
|
50
|
-
widgets: {
|
|
51
|
-
set(placement, key, factory, setOpts = {}) {
|
|
52
|
-
if (disposed)
|
|
53
|
-
return;
|
|
54
|
-
const record = { placement, key, factory, order: setOpts.order ?? 0 };
|
|
55
|
-
widgets.set(widgetId(placement, key), record);
|
|
56
|
-
if (coordinated) {
|
|
57
|
-
emitRegister(record);
|
|
58
|
-
}
|
|
59
|
-
else if (leases.size === 0) {
|
|
60
|
-
opts.ctx.ui.setWidget(key, factory, { placement });
|
|
61
|
-
}
|
|
62
|
-
},
|
|
63
|
-
remove(placement, key) {
|
|
64
|
-
const id = widgetId(placement, key);
|
|
65
|
-
const record = widgets.get(id);
|
|
66
|
-
if (!record)
|
|
67
|
-
return;
|
|
68
|
-
widgets.delete(id);
|
|
69
|
-
if (coordinated) {
|
|
70
|
-
pi.events.emit(EVENTS.unregisterWidget, { ...basePayload(clientId), placement, key });
|
|
71
|
-
}
|
|
72
|
-
else {
|
|
73
|
-
clearFallback(record);
|
|
74
|
-
}
|
|
75
|
-
},
|
|
76
|
-
},
|
|
77
|
-
fullscreen: {
|
|
78
|
-
acquire() {
|
|
79
|
-
const token = `${clientId}-lease-${nextLeaseId++}`;
|
|
80
|
-
let released = false;
|
|
81
|
-
const wasFallbackVisible = !coordinated && leases.size === 0;
|
|
82
|
-
leases.add(token);
|
|
83
|
-
if (coordinated) {
|
|
84
|
-
pi.events.emit(EVENTS.fullscreenAcquire, { ...basePayload(clientId), token });
|
|
85
|
-
}
|
|
86
|
-
else if (wasFallbackVisible) {
|
|
87
|
-
for (const record of widgets.values())
|
|
88
|
-
clearFallback(record);
|
|
89
|
-
}
|
|
90
|
-
return {
|
|
91
|
-
release() {
|
|
92
|
-
if (released)
|
|
93
|
-
return;
|
|
94
|
-
released = true;
|
|
95
|
-
leases.delete(token);
|
|
96
|
-
if (coordinated && !disposed) {
|
|
97
|
-
pi.events.emit(EVENTS.fullscreenRelease, { ...basePayload(clientId), token });
|
|
98
|
-
}
|
|
99
|
-
else if (!disposed && leases.size === 0) {
|
|
100
|
-
for (const record of widgets.values())
|
|
101
|
-
restoreFallback(record);
|
|
102
|
-
}
|
|
103
|
-
},
|
|
104
|
-
};
|
|
105
|
-
},
|
|
106
|
-
},
|
|
107
|
-
ui: {
|
|
108
|
-
async fullscreen(factory) {
|
|
109
|
-
const ui = opts.ctx.ui;
|
|
110
|
-
if (typeof ui.custom !== "function") {
|
|
111
|
-
throw new Error("pi-extension-utils: ui.fullscreen requires an interactive UI (ctx.ui.custom is unavailable)");
|
|
112
|
-
}
|
|
113
|
-
const lease = client.fullscreen.acquire();
|
|
114
|
-
try {
|
|
115
|
-
return await ui.custom(factory);
|
|
116
|
-
}
|
|
117
|
-
finally {
|
|
118
|
-
lease.release();
|
|
119
|
-
}
|
|
120
|
-
},
|
|
121
|
-
},
|
|
122
|
-
reminders: {
|
|
123
|
-
upsert(intent) {
|
|
124
|
-
if (disposed)
|
|
125
|
-
return;
|
|
126
|
-
pi.events.emit(REMINDER_UPSERT_EVENT, intent);
|
|
127
|
-
},
|
|
128
|
-
remove(source, key) {
|
|
129
|
-
if (disposed)
|
|
130
|
-
return;
|
|
131
|
-
pi.events.emit(REMINDER_REMOVE_EVENT, { source, id: key });
|
|
132
|
-
},
|
|
133
|
-
clearSource(source) {
|
|
134
|
-
if (disposed)
|
|
135
|
-
return;
|
|
136
|
-
pi.events.emit(REMINDER_CLEAR_SOURCE_EVENT, { source });
|
|
137
|
-
},
|
|
138
|
-
announceNow(payload = {}) {
|
|
139
|
-
if (disposed)
|
|
140
|
-
return;
|
|
141
|
-
pi.events.emit(REMINDER_ANNOUNCE_NOW_EVENT, payload);
|
|
142
|
-
},
|
|
143
|
-
list(source) {
|
|
144
|
-
if (disposed)
|
|
145
|
-
return Promise.resolve(emptyReminderSnapshot());
|
|
146
|
-
let settled = false;
|
|
147
|
-
let resolveSnapshot;
|
|
148
|
-
let rejectSnapshot;
|
|
149
|
-
const promise = new Promise((resolve, reject) => {
|
|
150
|
-
resolveSnapshot = (snapshot) => {
|
|
151
|
-
settled = true;
|
|
152
|
-
resolve(snapshot);
|
|
153
|
-
};
|
|
154
|
-
rejectSnapshot = (error) => {
|
|
155
|
-
settled = true;
|
|
156
|
-
reject(error);
|
|
157
|
-
};
|
|
158
|
-
});
|
|
159
|
-
pi.events.emit(REMINDER_LIST_EVENT, { source, resolve: resolveSnapshot, reject: rejectSnapshot });
|
|
160
|
-
if (!settled)
|
|
161
|
-
resolveSnapshot(emptyReminderSnapshot());
|
|
162
|
-
return promise;
|
|
163
|
-
},
|
|
164
|
-
},
|
|
165
|
-
dispose() {
|
|
166
|
-
if (disposed)
|
|
167
|
-
return;
|
|
168
|
-
disposed = true;
|
|
169
|
-
offReady();
|
|
170
|
-
if (coordinated) {
|
|
171
|
-
pi.events.emit(EVENTS.unregisterWidget, { ...basePayload(clientId), all: true });
|
|
172
|
-
for (const token of leases) {
|
|
173
|
-
pi.events.emit(EVENTS.fullscreenRelease, { ...basePayload(clientId), token });
|
|
174
|
-
}
|
|
175
|
-
}
|
|
176
|
-
else {
|
|
177
|
-
for (const record of widgets.values())
|
|
178
|
-
clearFallback(record);
|
|
179
|
-
}
|
|
180
|
-
leases.clear();
|
|
181
|
-
widgets.clear();
|
|
182
|
-
},
|
|
183
|
-
};
|
|
184
|
-
return client;
|
|
185
|
-
}
|
|
186
|
-
function isReadyPayload(data) {
|
|
187
|
-
if (!data || typeof data !== "object")
|
|
188
|
-
return false;
|
|
189
|
-
const payload = data;
|
|
190
|
-
return typeof payload.protocolVersion === "number" && payload.protocolVersion <= PROTOCOL_VERSION && typeof payload.clientId === "string";
|
|
191
|
-
}
|
|
192
|
-
function emptyReminderSnapshot() {
|
|
193
|
-
return { reminders: [], count: 0 };
|
|
194
|
-
}
|
package/dist/src/logger.d.ts
DELETED
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
export interface LoggerOptions {
|
|
2
|
-
dir?: string;
|
|
3
|
-
maxBytes?: number;
|
|
4
|
-
}
|
|
5
|
-
export interface Logger {
|
|
6
|
-
debug(message: string): void;
|
|
7
|
-
info(message: string): void;
|
|
8
|
-
warn(message: string): void;
|
|
9
|
-
error(message: string): void;
|
|
10
|
-
log(level: string, message: string): void;
|
|
11
|
-
}
|
|
12
|
-
export declare function createLogger(ns: string, opts?: LoggerOptions): Logger;
|
package/dist/src/logger.js
DELETED
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
import { appendFileSync, existsSync, mkdirSync, renameSync, rmSync, statSync } from "node:fs";
|
|
2
|
-
import { homedir } from "node:os";
|
|
3
|
-
import { dirname, join } from "node:path";
|
|
4
|
-
const DEFAULT_MAX_BYTES = 1024 * 1024;
|
|
5
|
-
export function createLogger(ns, opts = {}) {
|
|
6
|
-
if (!ns || ns.includes("/") || ns.includes("\\")) {
|
|
7
|
-
throw new Error("Logger namespace must not contain path separators");
|
|
8
|
-
}
|
|
9
|
-
const dir = opts.dir ?? join(homedir(), ".pi", "logs");
|
|
10
|
-
const maxBytes = opts.maxBytes ?? DEFAULT_MAX_BYTES;
|
|
11
|
-
const file = join(dir, `${ns}.log`);
|
|
12
|
-
mkdirSync(dirname(file), { recursive: true });
|
|
13
|
-
function rotateIfNeeded(nextBytes) {
|
|
14
|
-
if (maxBytes <= 0 || !existsSync(file))
|
|
15
|
-
return;
|
|
16
|
-
const size = statSync(file).size;
|
|
17
|
-
if (size + nextBytes <= maxBytes)
|
|
18
|
-
return;
|
|
19
|
-
const rotated = `${file}.1`;
|
|
20
|
-
if (existsSync(rotated))
|
|
21
|
-
rmSync(rotated);
|
|
22
|
-
renameSync(file, rotated);
|
|
23
|
-
}
|
|
24
|
-
function log(level, message) {
|
|
25
|
-
const line = `${new Date().toISOString()} ${level} ${message}\n`;
|
|
26
|
-
mkdirSync(dirname(file), { recursive: true });
|
|
27
|
-
rotateIfNeeded(Buffer.byteLength(line));
|
|
28
|
-
appendFileSync(file, line);
|
|
29
|
-
}
|
|
30
|
-
return {
|
|
31
|
-
debug(message) {
|
|
32
|
-
log("debug", message);
|
|
33
|
-
},
|
|
34
|
-
info(message) {
|
|
35
|
-
log("info", message);
|
|
36
|
-
},
|
|
37
|
-
warn(message) {
|
|
38
|
-
log("warn", message);
|
|
39
|
-
},
|
|
40
|
-
error(message) {
|
|
41
|
-
log("error", message);
|
|
42
|
-
},
|
|
43
|
-
log,
|
|
44
|
-
};
|
|
45
|
-
}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|