@pi-unipi/image 2.6.0 → 2.6.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/package.json +8 -4
- package/src/register-providers.ts +0 -11
- package/src/tools.ts +0 -3
- package/src/tui/model-selector.ts +36 -71
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pi-unipi/image",
|
|
3
|
-
"version": "2.6.
|
|
3
|
+
"version": "2.6.2",
|
|
4
4
|
"description": "Image generation and image recognition tools for the Pi coding agent",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/index.ts",
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
"access": "public"
|
|
35
35
|
},
|
|
36
36
|
"dependencies": {
|
|
37
|
-
"@pi-unipi/core": "2.6.
|
|
37
|
+
"@pi-unipi/core": "2.6.1"
|
|
38
38
|
},
|
|
39
39
|
"peerDependencies": {
|
|
40
40
|
"@earendil-works/pi-ai": "^0.80.0",
|
|
@@ -49,8 +49,12 @@
|
|
|
49
49
|
"test": "npx tsx --test tests/**/*.test.ts"
|
|
50
50
|
},
|
|
51
51
|
"pi": {
|
|
52
|
-
"extensions": [
|
|
53
|
-
|
|
52
|
+
"extensions": [
|
|
53
|
+
"./src/index.ts"
|
|
54
|
+
],
|
|
55
|
+
"skills": [
|
|
56
|
+
"./skills"
|
|
57
|
+
],
|
|
54
58
|
"prompts": [],
|
|
55
59
|
"themes": []
|
|
56
60
|
}
|
|
@@ -207,14 +207,3 @@ async function resolveProviderKey(
|
|
|
207
207
|
const envName = `${provider.toUpperCase().replace(/[^A-Z0-9]/g, "_")}_API_KEY`;
|
|
208
208
|
return process.env[envName] || undefined;
|
|
209
209
|
}
|
|
210
|
-
|
|
211
|
-
/** Provider ids pi-ai can currently generate with, after registration. */
|
|
212
|
-
export function registeredProviderIds(images: {
|
|
213
|
-
getProviders?: () => ReadonlyArray<{ id: string }>;
|
|
214
|
-
}): string[] {
|
|
215
|
-
try {
|
|
216
|
-
return (images.getProviders?.() ?? []).map((p) => p.id);
|
|
217
|
-
} catch {
|
|
218
|
-
return [];
|
|
219
|
-
}
|
|
220
|
-
}
|
package/src/tools.ts
CHANGED
|
@@ -19,7 +19,6 @@ import {
|
|
|
19
19
|
listAllImageGenModels,
|
|
20
20
|
resolveImageGenModel,
|
|
21
21
|
resolveVisionModel,
|
|
22
|
-
splitModelRef,
|
|
23
22
|
type ChatModelRegistry,
|
|
24
23
|
type VisionModel,
|
|
25
24
|
} from "./models.js";
|
|
@@ -325,5 +324,3 @@ function currentSessionModel(ctx: ExtensionContext): string {
|
|
|
325
324
|
}
|
|
326
325
|
return "";
|
|
327
326
|
}
|
|
328
|
-
|
|
329
|
-
export { splitModelRef };
|
|
@@ -6,9 +6,9 @@
|
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
import type { Component } from "@earendil-works/pi-tui";
|
|
9
|
-
import { matchesKey
|
|
9
|
+
import { matchesKey } from "@earendil-works/pi-tui";
|
|
10
10
|
import type { Theme } from "@earendil-works/pi-coding-agent";
|
|
11
|
-
import { boxInnerWidth,
|
|
11
|
+
import { boxInnerWidth, OverlayTheme } from "@pi-unipi/core";
|
|
12
12
|
|
|
13
13
|
export interface SelectableModel {
|
|
14
14
|
provider: string;
|
|
@@ -29,7 +29,7 @@ export class ImageModelSelectorOverlay implements Component {
|
|
|
29
29
|
private filterMode = false;
|
|
30
30
|
private saved = false;
|
|
31
31
|
private error: string | null = null;
|
|
32
|
-
private
|
|
32
|
+
private overlay = new OverlayTheme();
|
|
33
33
|
/** Free-text entry, for models the catalog does not know about. */
|
|
34
34
|
private customMode = false;
|
|
35
35
|
private custom = "";
|
|
@@ -55,7 +55,7 @@ export class ImageModelSelectorOverlay implements Component {
|
|
|
55
55
|
}
|
|
56
56
|
|
|
57
57
|
setTheme(theme: Theme): void {
|
|
58
|
-
this.theme
|
|
58
|
+
this.overlay.setTheme(theme);
|
|
59
59
|
}
|
|
60
60
|
|
|
61
61
|
invalidate(): void {}
|
|
@@ -229,41 +229,6 @@ export class ImageModelSelectorOverlay implements Component {
|
|
|
229
229
|
this.onClose?.();
|
|
230
230
|
}
|
|
231
231
|
|
|
232
|
-
// ─── Theme helpers ───────────────────────────────────────────────────
|
|
233
|
-
|
|
234
|
-
private fg(color: string, text: string): string {
|
|
235
|
-
if (this.theme) return this.theme.fg(color as never, text);
|
|
236
|
-
const codes: Record<string, string> = {
|
|
237
|
-
accent: "\x1b[36m",
|
|
238
|
-
success: "\x1b[32m",
|
|
239
|
-
warning: "\x1b[33m",
|
|
240
|
-
error: "\x1b[31m",
|
|
241
|
-
dim: "\x1b[2m",
|
|
242
|
-
borderMuted: "\x1b[90m",
|
|
243
|
-
};
|
|
244
|
-
return `${codes[color] ?? ""}${text}\x1b[0m`;
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
private bold(text: string): string {
|
|
248
|
-
return this.theme ? this.theme.bold(text) : `\x1b[1m${text}\x1b[0m`;
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
private frameLine(content: string, innerWidth: number): string {
|
|
252
|
-
const truncated = truncateToWidth(content, innerWidth, "");
|
|
253
|
-
const padding = safeRepeat(" ", innerWidth - visibleWidth(truncated));
|
|
254
|
-
return `${this.fg("borderMuted", "│")}${truncated}${padding}${this.fg("borderMuted", "│")}`;
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
private ruleLine(innerWidth: number): string {
|
|
258
|
-
return this.fg("borderMuted", `├${safeRepeat("─", innerWidth)}┤`);
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
private borderLine(innerWidth: number, edge: "top" | "bottom"): string {
|
|
262
|
-
const left = edge === "top" ? "┌" : "└";
|
|
263
|
-
const right = edge === "top" ? "┐" : "┘";
|
|
264
|
-
return this.fg("borderMuted", `${left}${safeRepeat("─", innerWidth)}${right}`);
|
|
265
|
-
}
|
|
266
|
-
|
|
267
232
|
render(width: number): string[] {
|
|
268
233
|
const innerWidth = boxInnerWidth(width);
|
|
269
234
|
const lines: string[] = [];
|
|
@@ -277,48 +242,48 @@ export class ImageModelSelectorOverlay implements Component {
|
|
|
277
242
|
? "Model used by image_generate"
|
|
278
243
|
: "Vision model used by image_recognize";
|
|
279
244
|
|
|
280
|
-
lines.push(this.borderLine(innerWidth, "top"));
|
|
281
|
-
lines.push(this.frameLine(this.fg("accent", this.bold(title)), innerWidth));
|
|
282
|
-
lines.push(this.frameLine(this.fg("dim", subtitle), innerWidth));
|
|
283
|
-
lines.push(this.ruleLine(innerWidth));
|
|
245
|
+
lines.push(this.overlay.borderLine(innerWidth, "top"));
|
|
246
|
+
lines.push(this.overlay.frameLine(this.overlay.fg("accent", this.overlay.bold(title)), innerWidth));
|
|
247
|
+
lines.push(this.overlay.frameLine(this.overlay.fg("dim", subtitle), innerWidth));
|
|
248
|
+
lines.push(this.overlay.ruleLine(innerWidth));
|
|
284
249
|
|
|
285
250
|
// Filter bar
|
|
286
251
|
if (this.customMode) {
|
|
287
252
|
lines.push(
|
|
288
|
-
this.frameLine(
|
|
289
|
-
` ${this.fg("accent", "Model:")} ${this.custom}${this.fg("accent", "█")}`,
|
|
253
|
+
this.overlay.frameLine(
|
|
254
|
+
` ${this.overlay.fg("accent", "Model:")} ${this.custom}${this.overlay.fg("accent", "█")}`,
|
|
290
255
|
innerWidth,
|
|
291
256
|
),
|
|
292
257
|
);
|
|
293
258
|
lines.push(
|
|
294
|
-
this.frameLine(
|
|
295
|
-
` ${this.fg("dim", "e.g. omniroute/fal/fal-ai/flux-2-pro")}`,
|
|
259
|
+
this.overlay.frameLine(
|
|
260
|
+
` ${this.overlay.fg("dim", "e.g. omniroute/fal/fal-ai/flux-2-pro")}`,
|
|
296
261
|
innerWidth,
|
|
297
262
|
),
|
|
298
263
|
);
|
|
299
264
|
} else if (this.filterMode) {
|
|
300
265
|
lines.push(
|
|
301
|
-
this.frameLine(
|
|
302
|
-
` ${this.fg("accent", "Filter:")} ${this.filter}${this.fg("accent", "█")}`,
|
|
266
|
+
this.overlay.frameLine(
|
|
267
|
+
` ${this.overlay.fg("accent", "Filter:")} ${this.filter}${this.overlay.fg("accent", "█")}`,
|
|
303
268
|
innerWidth,
|
|
304
269
|
),
|
|
305
270
|
);
|
|
306
271
|
} else if (this.filter) {
|
|
307
272
|
lines.push(
|
|
308
|
-
this.frameLine(
|
|
309
|
-
` ${this.fg("dim", "Filter:")} ${this.filter} ${this.fg("dim", "(press / to edit)")}`,
|
|
273
|
+
this.overlay.frameLine(
|
|
274
|
+
` ${this.overlay.fg("dim", "Filter:")} ${this.filter} ${this.overlay.fg("dim", "(press / to edit)")}`,
|
|
310
275
|
innerWidth,
|
|
311
276
|
),
|
|
312
277
|
);
|
|
313
278
|
} else {
|
|
314
279
|
lines.push(
|
|
315
|
-
this.frameLine(
|
|
316
|
-
` ${this.fg("dim", `${this.models.length} models · / filter · c custom`)}`,
|
|
280
|
+
this.overlay.frameLine(
|
|
281
|
+
` ${this.overlay.fg("dim", `${this.models.length} models · / filter · c custom`)}`,
|
|
317
282
|
innerWidth,
|
|
318
283
|
),
|
|
319
284
|
);
|
|
320
285
|
}
|
|
321
|
-
lines.push(this.ruleLine(innerWidth));
|
|
286
|
+
lines.push(this.overlay.ruleLine(innerWidth));
|
|
322
287
|
|
|
323
288
|
// Model list
|
|
324
289
|
const terminalRows = process.stdout.rows ?? 30;
|
|
@@ -335,47 +300,47 @@ export class ImageModelSelectorOverlay implements Component {
|
|
|
335
300
|
? "No image models available (needs OpenRouter)"
|
|
336
301
|
: "No vision-capable models configured"
|
|
337
302
|
: "No models match the filter";
|
|
338
|
-
lines.push(this.frameLine(` ${this.fg("dim", empty)}`, innerWidth));
|
|
303
|
+
lines.push(this.overlay.frameLine(` ${this.overlay.fg("dim", empty)}`, innerWidth));
|
|
339
304
|
} else {
|
|
340
305
|
for (let i = start; i < end; i++) {
|
|
341
306
|
const model = this.filtered[i];
|
|
342
307
|
const isSelected = i === this.selectedIndex;
|
|
343
|
-
const marker = isSelected ? this.fg("accent", "▸") : " ";
|
|
308
|
+
const marker = isSelected ? this.overlay.fg("accent", "▸") : " ";
|
|
344
309
|
const label = model.name || model.id;
|
|
345
|
-
const providerTag = this.fg("dim", `[${model.provider}]`);
|
|
310
|
+
const providerTag = this.overlay.fg("dim", `[${model.provider}]`);
|
|
346
311
|
const base = isSelected
|
|
347
|
-
? `${providerTag} ${this.bold(label)}`
|
|
348
|
-
: `${providerTag} ${this.fg("dim", label)}`;
|
|
312
|
+
? `${providerTag} ${this.overlay.bold(label)}`
|
|
313
|
+
: `${providerTag} ${this.overlay.fg("dim", label)}`;
|
|
349
314
|
const display = model.unavailable
|
|
350
|
-
? `${base} ${this.fg("warning", `(${model.unavailable})`)}`
|
|
315
|
+
? `${base} ${this.overlay.fg("warning", `(${model.unavailable})`)}`
|
|
351
316
|
: base;
|
|
352
|
-
lines.push(this.frameLine(` ${marker} ${display}`, innerWidth));
|
|
317
|
+
lines.push(this.overlay.frameLine(` ${marker} ${display}`, innerWidth));
|
|
353
318
|
}
|
|
354
319
|
}
|
|
355
320
|
|
|
356
321
|
if (!this.customMode && this.filtered.length > maxVisible) {
|
|
357
322
|
const pct = Math.round(((this.selectedIndex + 1) / this.filtered.length) * 100);
|
|
358
323
|
lines.push(
|
|
359
|
-
this.frameLine(
|
|
360
|
-
this.fg("dim", ` ${pct}% (${this.selectedIndex + 1}/${this.filtered.length})`),
|
|
324
|
+
this.overlay.frameLine(
|
|
325
|
+
this.overlay.fg("dim", ` ${pct}% (${this.selectedIndex + 1}/${this.filtered.length})`),
|
|
361
326
|
innerWidth,
|
|
362
327
|
),
|
|
363
328
|
);
|
|
364
329
|
}
|
|
365
330
|
|
|
366
331
|
if (this.error) {
|
|
367
|
-
lines.push(this.ruleLine(innerWidth));
|
|
368
|
-
lines.push(this.frameLine(` ${this.fg("error", `⚠ ${this.error}`)}`, innerWidth));
|
|
332
|
+
lines.push(this.overlay.ruleLine(innerWidth));
|
|
333
|
+
lines.push(this.overlay.frameLine(` ${this.overlay.fg("error", `⚠ ${this.error}`)}`, innerWidth));
|
|
369
334
|
}
|
|
370
335
|
if (this.saved) {
|
|
371
|
-
lines.push(this.ruleLine(innerWidth));
|
|
372
|
-
lines.push(this.frameLine(` ${this.fg("success", "✓ Model saved")}`, innerWidth));
|
|
336
|
+
lines.push(this.overlay.ruleLine(innerWidth));
|
|
337
|
+
lines.push(this.overlay.frameLine(` ${this.overlay.fg("success", "✓ Model saved")}`, innerWidth));
|
|
373
338
|
}
|
|
374
339
|
|
|
375
|
-
lines.push(this.ruleLine(innerWidth));
|
|
340
|
+
lines.push(this.overlay.ruleLine(innerWidth));
|
|
376
341
|
lines.push(
|
|
377
|
-
this.frameLine(
|
|
378
|
-
this.fg(
|
|
342
|
+
this.overlay.frameLine(
|
|
343
|
+
this.overlay.fg(
|
|
379
344
|
"dim",
|
|
380
345
|
this.customMode
|
|
381
346
|
? "Enter save · Esc back to list"
|
|
@@ -384,7 +349,7 @@ export class ImageModelSelectorOverlay implements Component {
|
|
|
384
349
|
innerWidth,
|
|
385
350
|
),
|
|
386
351
|
);
|
|
387
|
-
lines.push(this.borderLine(innerWidth, "bottom"));
|
|
352
|
+
lines.push(this.overlay.borderLine(innerWidth, "bottom"));
|
|
388
353
|
|
|
389
354
|
return lines;
|
|
390
355
|
}
|