pi-banana 2.0.0 → 2.0.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 +5 -1
- package/extensions/index.ts +80 -4
- package/package.json +8 -3
package/README.md
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# pi-banana
|
|
2
2
|
|
|
3
|
+

|
|
4
|
+
|
|
3
5
|
Generate and edit images directly inside [pi](https://github.com/badlogic/pi-mono) using Google's **Nano Banana 2** (`gemini-3.1-flash-image-preview`) and **Nano Banana Pro** (`gemini-3-pro-image-preview`).
|
|
4
6
|
|
|
5
7
|
- **Inline preview** in Kitty / iTerm2 / WezTerm — image shows up right under the tool call, no copy-pasting paths.
|
|
@@ -41,7 +43,9 @@ Just ask pi for what you want:
|
|
|
41
43
|
|
|
42
44
|
The model calls `generate_image` automatically. The PNG is shown inline and written to disk.
|
|
43
45
|
|
|
44
|
-
## Tool: `
|
|
46
|
+
## Tool: `banana_image`
|
|
47
|
+
|
|
48
|
+
> Renamed from `generate_image` in v2.0.1 to avoid colliding with `@benvargas/pi-antigravity-image-gen`. Both extensions can now coexist in the same pi install.
|
|
45
49
|
|
|
46
50
|
| Param | Type | Default | Description |
|
|
47
51
|
|---|---|---|---|
|
package/extensions/index.ts
CHANGED
|
@@ -16,6 +16,13 @@
|
|
|
16
16
|
|
|
17
17
|
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
18
18
|
import { StringEnum } from "@earendil-works/pi-ai";
|
|
19
|
+
import {
|
|
20
|
+
Box,
|
|
21
|
+
Container,
|
|
22
|
+
Image,
|
|
23
|
+
Spacer,
|
|
24
|
+
Text,
|
|
25
|
+
} from "@earendil-works/pi-tui";
|
|
19
26
|
import { GoogleGenAI } from "@google/genai";
|
|
20
27
|
import { existsSync } from "node:fs";
|
|
21
28
|
import { mkdir, readFile, writeFile } from "node:fs/promises";
|
|
@@ -178,8 +185,8 @@ async function resolveOutputPath(
|
|
|
178
185
|
|
|
179
186
|
export default function (pi: ExtensionAPI) {
|
|
180
187
|
pi.registerTool({
|
|
181
|
-
name: "
|
|
182
|
-
label: "Image
|
|
188
|
+
name: "banana_image",
|
|
189
|
+
label: "Banana Image",
|
|
183
190
|
description:
|
|
184
191
|
"Generate or edit a PNG/JPEG image with Google's Nano Banana 2 " +
|
|
185
192
|
"(Gemini 3.1 Flash Image) or Nano Banana Pro. The image is shown " +
|
|
@@ -188,8 +195,8 @@ export default function (pi: ExtensionAPI) {
|
|
|
188
195
|
promptSnippet:
|
|
189
196
|
"Generate or edit images with Google Nano Banana via the GOOGLE_API_KEY env var.",
|
|
190
197
|
promptGuidelines: [
|
|
191
|
-
"Call
|
|
192
|
-
"For tweaks like 'make the sky purple', pass the previous file path as referenceImage to
|
|
198
|
+
"Call banana_image when the user asks to create, draw, illustrate, or edit a picture.",
|
|
199
|
+
"For tweaks like 'make the sky purple', pass the previous file path as referenceImage to banana_image.",
|
|
193
200
|
"Default quality 'fast' is right for most asks; switch to 'high' only when the user explicitly wants top quality.",
|
|
194
201
|
],
|
|
195
202
|
parameters: Type.Object({
|
|
@@ -370,6 +377,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
370
377
|
{ type: "image", data: base64, mimeType },
|
|
371
378
|
],
|
|
372
379
|
details: {
|
|
380
|
+
prompt: params.prompt,
|
|
373
381
|
model,
|
|
374
382
|
quality,
|
|
375
383
|
aspectRatio,
|
|
@@ -381,5 +389,73 @@ export default function (pi: ExtensionAPI) {
|
|
|
381
389
|
},
|
|
382
390
|
};
|
|
383
391
|
},
|
|
392
|
+
|
|
393
|
+
renderResult(result, _options, theme) {
|
|
394
|
+
const { details, content } = result;
|
|
395
|
+
const container = new Container();
|
|
396
|
+
|
|
397
|
+
// 1. Summary
|
|
398
|
+
const summaryPart = content.find((c: any) => c.type === "text");
|
|
399
|
+
const summaryText = (summaryPart && summaryPart.type === "text") ? summaryPart.text : "";
|
|
400
|
+
container.addChild(new Text(theme.fg("success", "✔ " + summaryText), 1, 0));
|
|
401
|
+
|
|
402
|
+
// 2. Image
|
|
403
|
+
const imagePart = content.find((c: any) => c.type === "image");
|
|
404
|
+
if (imagePart && imagePart.type === "image") {
|
|
405
|
+
container.addChild(
|
|
406
|
+
new Image(imagePart.data, imagePart.mimeType, {
|
|
407
|
+
...theme,
|
|
408
|
+
fallbackColor: (s: string) => theme.fg("muted", s),
|
|
409
|
+
}, {
|
|
410
|
+
maxWidthCells: 80,
|
|
411
|
+
maxHeightCells: 24,
|
|
412
|
+
})
|
|
413
|
+
);
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
if (!details) return container;
|
|
417
|
+
|
|
418
|
+
const {
|
|
419
|
+
prompt,
|
|
420
|
+
model,
|
|
421
|
+
quality,
|
|
422
|
+
aspectRatio,
|
|
423
|
+
imageSize,
|
|
424
|
+
outputPath,
|
|
425
|
+
} = details as any;
|
|
426
|
+
|
|
427
|
+
container.addChild(new Spacer(1));
|
|
428
|
+
|
|
429
|
+
// 3. Settings Card
|
|
430
|
+
const settingsBox = new Box(1, 1, (s) => theme.bg("customMessageBg", s));
|
|
431
|
+
const settingsContainer = new Container();
|
|
432
|
+
|
|
433
|
+
settingsContainer.addChild(
|
|
434
|
+
new Text(theme.fg("accent", theme.bold("🛠️ GENERATION SETTINGS")), 0, 0)
|
|
435
|
+
);
|
|
436
|
+
settingsContainer.addChild(new Spacer(1));
|
|
437
|
+
|
|
438
|
+
const addSetting = (label: string, value: string) => {
|
|
439
|
+
settingsContainer.addChild(
|
|
440
|
+
new Text(
|
|
441
|
+
theme.fg("muted", label.padEnd(10)) + theme.fg("text", String(value)),
|
|
442
|
+
0,
|
|
443
|
+
0
|
|
444
|
+
)
|
|
445
|
+
);
|
|
446
|
+
};
|
|
447
|
+
|
|
448
|
+
if (prompt) addSetting("Prompt", prompt);
|
|
449
|
+
if (model) addSetting("Model", model);
|
|
450
|
+
if (quality) addSetting("Quality", quality);
|
|
451
|
+
if (aspectRatio) addSetting("Aspect", aspectRatio);
|
|
452
|
+
if (imageSize) addSetting("Size", imageSize);
|
|
453
|
+
if (outputPath) addSetting("Path", outputPath);
|
|
454
|
+
|
|
455
|
+
settingsBox.addChild(settingsContainer);
|
|
456
|
+
container.addChild(settingsBox);
|
|
457
|
+
|
|
458
|
+
return container;
|
|
459
|
+
},
|
|
384
460
|
});
|
|
385
461
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-banana",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.2",
|
|
4
4
|
"description": "Generate and edit images in pi using Google Nano Banana 2 (gemini-3.1-flash-image) and Nano Banana Pro. Inline terminal preview, reference-image editing, auto-save.",
|
|
5
5
|
"author": "Francesco Frapporti <effedue@gmail.com>",
|
|
6
6
|
"license": "MIT",
|
|
@@ -20,12 +20,15 @@
|
|
|
20
20
|
"pi-coding-agent",
|
|
21
21
|
"google",
|
|
22
22
|
"gemini",
|
|
23
|
+
"gemini-api",
|
|
23
24
|
"nano-banana",
|
|
24
25
|
"nano-banana-2",
|
|
25
26
|
"nano-banana-pro",
|
|
27
|
+
"imagen",
|
|
26
28
|
"image-generation",
|
|
27
|
-
"image-
|
|
28
|
-
"
|
|
29
|
+
"image-editing",
|
|
30
|
+
"ai-image",
|
|
31
|
+
"terminal"
|
|
29
32
|
],
|
|
30
33
|
"files": [
|
|
31
34
|
"extensions",
|
|
@@ -47,11 +50,13 @@
|
|
|
47
50
|
"peerDependencies": {
|
|
48
51
|
"@earendil-works/pi-ai": "*",
|
|
49
52
|
"@earendil-works/pi-coding-agent": "*",
|
|
53
|
+
"@earendil-works/pi-tui": "*",
|
|
50
54
|
"typebox": "^1.1.24"
|
|
51
55
|
},
|
|
52
56
|
"devDependencies": {
|
|
53
57
|
"@earendil-works/pi-ai": "*",
|
|
54
58
|
"@earendil-works/pi-coding-agent": "*",
|
|
59
|
+
"@earendil-works/pi-tui": "*",
|
|
55
60
|
"@types/node": "^22.0.0",
|
|
56
61
|
"jiti": "^2.7.0",
|
|
57
62
|
"typebox": "^1.1.24",
|