pi-paster 0.1.1 → 0.1.3
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/index.mjs +4 -3
- package/docs/preview.png +0 -0
- package/package.json +10 -3
- package/src/preview.ts +3 -5
- package/spec.md +0 -349
package/dist/index.mjs
CHANGED
|
@@ -2,7 +2,7 @@ import { spawnSync } from "node:child_process";
|
|
|
2
2
|
import { randomUUID } from "node:crypto";
|
|
3
3
|
import { existsSync, readFileSync, statSync, unlinkSync } from "node:fs";
|
|
4
4
|
import { homedir, tmpdir } from "node:os";
|
|
5
|
-
import {
|
|
5
|
+
import { isAbsolute, join, resolve } from "node:path";
|
|
6
6
|
import { Image, getCellDimensions, getImageDimensions, truncateToWidth } from "@earendil-works/pi-tui";
|
|
7
7
|
import { CustomEditor } from "@earendil-works/pi-coding-agent";
|
|
8
8
|
//#region src/types.ts
|
|
@@ -422,7 +422,8 @@ var ImagePreviewMessage = class {
|
|
|
422
422
|
render(width) {
|
|
423
423
|
const lines = [];
|
|
424
424
|
for (let index = 0; index < this.attachments.length; index++) {
|
|
425
|
-
|
|
425
|
+
const attachment = this.attachments[index];
|
|
426
|
+
lines.push(this.theme.fallbackColor(`Attached ${attachment.placeholder} ${attachment.originalPath}`));
|
|
426
427
|
lines.push(...this.images[index].render(width));
|
|
427
428
|
}
|
|
428
429
|
return lines;
|
|
@@ -447,7 +448,7 @@ var CursorImagePreviewWidget = class {
|
|
|
447
448
|
this.image.invalidate();
|
|
448
449
|
}
|
|
449
450
|
headerLine(width) {
|
|
450
|
-
const title =
|
|
451
|
+
const title = `Attached ${this.attachment.placeholder} ${this.attachment.originalPath}`;
|
|
451
452
|
return this.theme.title(truncateToWidth(title, Math.max(1, width), ""));
|
|
452
453
|
}
|
|
453
454
|
createImage(attachment, maxWidthCells = 60) {
|
package/docs/preview.png
CHANGED
|
Binary file
|
package/package.json
CHANGED
|
@@ -1,18 +1,25 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-paster",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"description": "Pi extension that turns pasted image paths into first-class image attachments.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"image-attachments",
|
|
7
7
|
"pi-extension",
|
|
8
8
|
"pi-package"
|
|
9
9
|
],
|
|
10
|
+
"homepage": "https://github.com/beowulf11/pi-paster#readme",
|
|
11
|
+
"bugs": {
|
|
12
|
+
"url": "https://github.com/beowulf11/pi-paster/issues"
|
|
13
|
+
},
|
|
10
14
|
"license": "MIT",
|
|
15
|
+
"repository": {
|
|
16
|
+
"type": "git",
|
|
17
|
+
"url": "git+https://github.com/beowulf11/pi-paster.git"
|
|
18
|
+
},
|
|
11
19
|
"files": [
|
|
12
20
|
"dist",
|
|
13
21
|
"src",
|
|
14
22
|
"docs",
|
|
15
|
-
"spec.md",
|
|
16
23
|
"README.md"
|
|
17
24
|
],
|
|
18
25
|
"type": "module",
|
|
@@ -48,6 +55,6 @@
|
|
|
48
55
|
"extensions": [
|
|
49
56
|
"./src/index.ts"
|
|
50
57
|
],
|
|
51
|
-
"image": "https://unpkg.com/pi-paster@0.1.
|
|
58
|
+
"image": "https://unpkg.com/pi-paster@0.1.3/docs/preview.png"
|
|
52
59
|
}
|
|
53
60
|
}
|
package/src/preview.ts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { basename } from "node:path";
|
|
2
1
|
import {
|
|
3
2
|
getCellDimensions,
|
|
4
3
|
Image,
|
|
@@ -28,10 +27,9 @@ export class ImagePreviewMessage implements Component {
|
|
|
28
27
|
render(width: number): string[] {
|
|
29
28
|
const lines: string[] = [];
|
|
30
29
|
for (let index = 0; index < this.attachments.length; index++) {
|
|
30
|
+
const attachment = this.attachments[index]!;
|
|
31
31
|
lines.push(
|
|
32
|
-
this.theme.fallbackColor(
|
|
33
|
-
`Attached ${this.attachments[index]!.placeholder} (${this.attachments[index]!.mimeType})`,
|
|
34
|
-
),
|
|
32
|
+
this.theme.fallbackColor(`Attached ${attachment.placeholder} ${attachment.originalPath}`),
|
|
35
33
|
);
|
|
36
34
|
lines.push(...this.images[index]!.render(width));
|
|
37
35
|
}
|
|
@@ -70,7 +68,7 @@ export class CursorImagePreviewWidget implements Component {
|
|
|
70
68
|
}
|
|
71
69
|
|
|
72
70
|
private headerLine(width: number): string {
|
|
73
|
-
const title =
|
|
71
|
+
const title = `Attached ${this.attachment.placeholder} ${this.attachment.originalPath}`;
|
|
74
72
|
return this.theme.title(truncateToWidth(title, Math.max(1, width), ""));
|
|
75
73
|
}
|
|
76
74
|
|
package/spec.md
DELETED
|
@@ -1,349 +0,0 @@
|
|
|
1
|
-
# Pi Image Paste Attachments Extension
|
|
2
|
-
|
|
3
|
-
## Goal
|
|
4
|
-
|
|
5
|
-
Create a pi extension that makes pasted or drag-dropped image paths behave like first-class image attachments in interactive mode.
|
|
6
|
-
|
|
7
|
-
Today, pi can read image files when the assistant/tool explicitly calls `read`, and `Ctrl+V` on an image clipboard saves the clipboard image to a temp file and inserts that temp path into the editor. The desired UX is more direct:
|
|
8
|
-
|
|
9
|
-
1. User pastes or drag-drops an image path, or uses pi's image clipboard paste flow.
|
|
10
|
-
2. The editor replaces the raw file path with a readable placeholder such as `[#image1]`.
|
|
11
|
-
3. The extension stores the corresponding image data immediately.
|
|
12
|
-
4. On submit, the LLM receives the user's text first, with placeholders preserved, followed by the attached image blocks. The assistant should not need to call `read` just to see the image.
|
|
13
|
-
|
|
14
|
-
## Non-goals for MVP
|
|
15
|
-
|
|
16
|
-
- No image description/captioning model yet.
|
|
17
|
-
- No file browser or attachment picker.
|
|
18
|
-
- No editing UI beyond visible placeholder text in the normal editor.
|
|
19
|
-
- No permanent attachment storage across pi restarts.
|
|
20
|
-
- No changes to pi core unless extension APIs prove insufficient.
|
|
21
|
-
|
|
22
|
-
## UX
|
|
23
|
-
|
|
24
|
-
### Paste or drag-drop image path
|
|
25
|
-
|
|
26
|
-
User action:
|
|
27
|
-
|
|
28
|
-
```text
|
|
29
|
-
Here is the bug /var/folders/.../pi-clipboard-abc.png
|
|
30
|
-
```
|
|
31
|
-
|
|
32
|
-
Editor should become:
|
|
33
|
-
|
|
34
|
-
```text
|
|
35
|
-
Here is the bug [#image1]
|
|
36
|
-
```
|
|
37
|
-
|
|
38
|
-
A path-only paste must also be handled as a first-class case:
|
|
39
|
-
|
|
40
|
-
```text
|
|
41
|
-
/var/folders/.../pi-clipboard-abc.png
|
|
42
|
-
```
|
|
43
|
-
|
|
44
|
-
Editor should become:
|
|
45
|
-
|
|
46
|
-
```text
|
|
47
|
-
[#image1]
|
|
48
|
-
```
|
|
49
|
-
|
|
50
|
-
This path-only behavior is important because terminal drag-and-drop commonly inserts only the dropped file path. If that single pasted/dropped token is an image path, the extension should consume it, store the image payload, and insert only the placeholder.
|
|
51
|
-
|
|
52
|
-
Placeholder format:
|
|
53
|
-
|
|
54
|
-
- Use `[#image1]`, `[#image2]`, etc.
|
|
55
|
-
- No space inside the token. This makes placeholders easy to scan, easy to match exactly, and unlikely to conflict with normal prose.
|
|
56
|
-
- The number is allocated when the image is accepted into attachment state.
|
|
57
|
-
|
|
58
|
-
The extension records:
|
|
59
|
-
|
|
60
|
-
- placeholder: `[#image1]`
|
|
61
|
-
- original path: `/var/folders/.../pi-clipboard-abc.png`
|
|
62
|
-
- mime type: `image/png`
|
|
63
|
-
- base64 image payload
|
|
64
|
-
- optional size/dimensions metadata if easy to compute
|
|
65
|
-
|
|
66
|
-
### Multiple images
|
|
67
|
-
|
|
68
|
-
If a paste contains multiple image paths:
|
|
69
|
-
|
|
70
|
-
```text
|
|
71
|
-
/Users/me/a.png /Users/me/b.jpg
|
|
72
|
-
```
|
|
73
|
-
|
|
74
|
-
Editor should insert:
|
|
75
|
-
|
|
76
|
-
```text
|
|
77
|
-
[#image1] [#image2]
|
|
78
|
-
```
|
|
79
|
-
|
|
80
|
-
### Mixed paste
|
|
81
|
-
|
|
82
|
-
If a paste contains text and image paths, only image path tokens are replaced:
|
|
83
|
-
|
|
84
|
-
```text
|
|
85
|
-
compare /tmp/before.png with /tmp/after.png please
|
|
86
|
-
```
|
|
87
|
-
|
|
88
|
-
becomes:
|
|
89
|
-
|
|
90
|
-
```text
|
|
91
|
-
compare [#image1] with [#image2] please
|
|
92
|
-
```
|
|
93
|
-
|
|
94
|
-
### Submit behavior
|
|
95
|
-
|
|
96
|
-
When the user submits:
|
|
97
|
-
|
|
98
|
-
```text
|
|
99
|
-
What's wrong here? [#image1]
|
|
100
|
-
```
|
|
101
|
-
|
|
102
|
-
The extension should submit a single user message whose content order is equivalent to:
|
|
103
|
-
|
|
104
|
-
1. Text block: `What's wrong here? [#image1]`
|
|
105
|
-
2. Image block for `[#image1]`
|
|
106
|
-
|
|
107
|
-
This keeps the user's placeholder references in the prompt while attaching the actual image payload to the same turn.
|
|
108
|
-
|
|
109
|
-
## Technical design
|
|
110
|
-
|
|
111
|
-
### Extension APIs to use
|
|
112
|
-
|
|
113
|
-
Relevant pi extension APIs from the docs:
|
|
114
|
-
|
|
115
|
-
- `ctx.ui.setEditorComponent(...)` to install a custom editor.
|
|
116
|
-
- `CustomEditor` to preserve app-level keybindings and default editor behavior.
|
|
117
|
-
- `ctx.ui.getEditorComponent()` to detect/wrap a previously configured editor where possible.
|
|
118
|
-
- `pi.on("input", ...)` to transform submitted input and attach images.
|
|
119
|
-
- `event.images` / transformed `images` for attached image blocks.
|
|
120
|
-
- `ctx.ui.notify(...)` for non-blocking warnings.
|
|
121
|
-
|
|
122
|
-
### Editor interception
|
|
123
|
-
|
|
124
|
-
Implement an `ImageAttachmentEditor extends CustomEditor`.
|
|
125
|
-
|
|
126
|
-
Responsibilities:
|
|
127
|
-
|
|
128
|
-
1. Intercept bracketed paste input before default paste handling.
|
|
129
|
-
2. Parse pasted content into tokens, preserving non-image text.
|
|
130
|
-
3. For each candidate image path:
|
|
131
|
-
- resolve shell quoting/escaping,
|
|
132
|
-
- verify the file exists,
|
|
133
|
-
- detect supported image MIME type,
|
|
134
|
-
- read and store the image payload,
|
|
135
|
-
- insert a placeholder instead of the raw path.
|
|
136
|
-
4. Fall back to `super.handleInput(data)` for all non-image paste/input.
|
|
137
|
-
|
|
138
|
-
Important implementation note: `Editor.handlePaste()` is private in the pi-tui TypeScript declarations, so the extension should not override it directly. Instead, intercept bracketed paste sequences in `handleInput()` before delegating to `super.handleInput()`.
|
|
139
|
-
|
|
140
|
-
### Support pi's existing Ctrl+V image clipboard flow
|
|
141
|
-
|
|
142
|
-
Pi's existing image clipboard handler saves clipboard images to a temp file and calls `editor.insertTextAtCursor(filePath)`.
|
|
143
|
-
|
|
144
|
-
To convert that flow into placeholders too, override `insertTextAtCursor(text)` in `ImageAttachmentEditor`:
|
|
145
|
-
|
|
146
|
-
- If `text` is a recognized image path, read/store it and insert `[#imageN]`.
|
|
147
|
-
- Otherwise delegate to `super.insertTextAtCursor(text)`.
|
|
148
|
-
|
|
149
|
-
This lets the extension reuse pi's current clipboard-to-temp-file mechanism without reimplementing native clipboard image access.
|
|
150
|
-
|
|
151
|
-
### Attachment state
|
|
152
|
-
|
|
153
|
-
Maintain in-memory attachment state in the extension runtime:
|
|
154
|
-
|
|
155
|
-
```ts
|
|
156
|
-
interface ImageAttachment {
|
|
157
|
-
id: number;
|
|
158
|
-
placeholder: string; // e.g. "[#image1]"
|
|
159
|
-
originalPath: string;
|
|
160
|
-
mimeType: "image/png" | "image/jpeg" | "image/webp" | "image/gif";
|
|
161
|
-
data: string; // base64
|
|
162
|
-
createdAt: number;
|
|
163
|
-
}
|
|
164
|
-
```
|
|
165
|
-
|
|
166
|
-
State rules:
|
|
167
|
-
|
|
168
|
-
- IDs increment per session/runtime.
|
|
169
|
-
- Keep attachments even if placeholders are later deleted; only submit attachments whose placeholder still appears in the submitted text.
|
|
170
|
-
- Attach each placeholder at most once per submitted message, ordered by first occurrence in the text.
|
|
171
|
-
- Clear attachment state on `/new`, `/resume`, `/fork`, or extension reload via `session_start`/`session_shutdown` lifecycle.
|
|
172
|
-
|
|
173
|
-
### Input transformation
|
|
174
|
-
|
|
175
|
-
Register an `input` handler:
|
|
176
|
-
|
|
177
|
-
1. Ignore `event.source === "extension"` to avoid recursion.
|
|
178
|
-
2. Scan `event.text` for known placeholders.
|
|
179
|
-
3. Build `images` from matching attachment payloads.
|
|
180
|
-
4. Return:
|
|
181
|
-
|
|
182
|
-
```ts
|
|
183
|
-
return {
|
|
184
|
-
action: "transform",
|
|
185
|
-
text: event.text,
|
|
186
|
-
images: [...(event.images ?? []), ...matchedImages],
|
|
187
|
-
};
|
|
188
|
-
```
|
|
189
|
-
|
|
190
|
-
If no placeholders are found, return `continue`.
|
|
191
|
-
|
|
192
|
-
### Detection and parsing
|
|
193
|
-
|
|
194
|
-
The parser must explicitly support path-only paste/drop content. If the complete pasted input is a single image path, possibly quoted or shell-escaped, it should be replaced by one placeholder and should not fall back to default paste behavior. This is the primary drag-and-drop path for many terminals.
|
|
195
|
-
|
|
196
|
-
Supported image formats for MVP:
|
|
197
|
-
|
|
198
|
-
- PNG
|
|
199
|
-
- JPEG
|
|
200
|
-
- WebP
|
|
201
|
-
- GIF
|
|
202
|
-
|
|
203
|
-
Path parsing should handle:
|
|
204
|
-
|
|
205
|
-
- absolute paths: `/tmp/a.png`
|
|
206
|
-
- home paths: `~/Desktop/a.png`
|
|
207
|
-
- relative paths: `./a.png`
|
|
208
|
-
- shell-escaped spaces: `/Users/me/Desktop/My\ Image.png`
|
|
209
|
-
- quoted paths: `'/Users/me/My Image.png'`, `"/Users/me/My Image.png"`
|
|
210
|
-
- multiple paths separated by whitespace or newlines
|
|
211
|
-
|
|
212
|
-
MIME detection should use magic bytes, not only file extension. Extension-only implementation can keep this small:
|
|
213
|
-
|
|
214
|
-
- PNG: `89 50 4E 47 0D 0A 1A 0A`
|
|
215
|
-
- JPEG: `FF D8 FF`
|
|
216
|
-
- GIF: `GIF87a` or `GIF89a`
|
|
217
|
-
- WebP: `RIFF....WEBP`
|
|
218
|
-
|
|
219
|
-
### Image size handling
|
|
220
|
-
|
|
221
|
-
MVP can attach original base64 data, but should include a conservative max file size guard to avoid accidental huge context/provider payloads.
|
|
222
|
-
|
|
223
|
-
Suggested defaults:
|
|
224
|
-
|
|
225
|
-
- Max image file size: 10 MB.
|
|
226
|
-
- If over limit: leave path unchanged and show a warning.
|
|
227
|
-
|
|
228
|
-
Future improvement: use pi's internal image resize logic if it becomes exported/stable, or add a dependency such as `sharp`/`jimp` in the extension package.
|
|
229
|
-
|
|
230
|
-
## Open question: same user message vs additional user messages
|
|
231
|
-
|
|
232
|
-
The preferred MVP is a single user message containing:
|
|
233
|
-
|
|
234
|
-
- one text block with placeholders,
|
|
235
|
-
- then image blocks in placeholder order.
|
|
236
|
-
|
|
237
|
-
This is simpler and avoids multiple turns. `pi.sendUserMessage()` always triggers a turn, so creating separate user messages for each image would likely cause unwanted agent execution unless pi exposes a batch append/send API.
|
|
238
|
-
|
|
239
|
-
If we later need visually separate image messages, investigate whether `input` handling can return `handled` and manually append entries without triggering multiple turns. That is not part of MVP.
|
|
240
|
-
|
|
241
|
-
## Edge cases
|
|
242
|
-
|
|
243
|
-
- Placeholder deleted before submit: do not attach that image.
|
|
244
|
-
- Placeholder copied/duplicated: attach once; text can refer to it multiple times.
|
|
245
|
-
- User manually types `[#image1]`: attach only if it matches existing attachment state.
|
|
246
|
-
- File deleted after paste: not a problem if image payload was read immediately.
|
|
247
|
-
- Paste is not an image path: preserve default pi paste behavior.
|
|
248
|
-
- Unsupported image file: leave text unchanged and optionally notify.
|
|
249
|
-
- Busy/streaming state: input transform should work for queued steer/follow-up messages too, because images are part of the transformed prompt.
|
|
250
|
-
- Non-interactive modes: extension should no-op custom editor setup when `ctx.hasUI` is false; input transform may still work for text containing known placeholders only within the same runtime.
|
|
251
|
-
|
|
252
|
-
## Commands and tools
|
|
253
|
-
|
|
254
|
-
No slash commands or LLM-callable tools for the MVP. The extension should work automatically from paste/edit/submit behavior. Debugging helpers can be reconsidered later, but they should not be part of the initial UX.
|
|
255
|
-
|
|
256
|
-
## Testing strategy
|
|
257
|
-
|
|
258
|
-
Use a layered test approach:
|
|
259
|
-
|
|
260
|
-
1. Unit tests for pure helpers:
|
|
261
|
-
- placeholder allocation (`[#image1]`, `[#image2]`, ...),
|
|
262
|
-
- placeholder matching and submit ordering,
|
|
263
|
-
- path tokenization and shell unescaping,
|
|
264
|
-
- path resolution for absolute, home, and relative paths,
|
|
265
|
-
- MIME detection from magic bytes,
|
|
266
|
-
- max file size rejection,
|
|
267
|
-
- image loading to base64.
|
|
268
|
-
2. Editor-level tests where feasible:
|
|
269
|
-
- path-only paste becomes one placeholder,
|
|
270
|
-
- mixed text plus image paths preserves non-image text,
|
|
271
|
-
- multiple image paths become multiple placeholders,
|
|
272
|
-
- non-image paste delegates to default behavior.
|
|
273
|
-
3. Input-transform tests:
|
|
274
|
-
- only placeholders still present in submitted text are attached,
|
|
275
|
-
- duplicated placeholders attach once,
|
|
276
|
-
- attachments are ordered by first placeholder occurrence,
|
|
277
|
-
- existing `event.images` are preserved before extension-added images.
|
|
278
|
-
4. Manual TUI smoke tests for behavior that depends on pi's interactive editor or terminal integration:
|
|
279
|
-
- Ctrl+V image clipboard on macOS,
|
|
280
|
-
- dragging an image file from Finder into the terminal,
|
|
281
|
-
- pasting a path with spaces,
|
|
282
|
-
- deleting a placeholder before submit,
|
|
283
|
-
- confirming Escape, Ctrl+C, Ctrl+D, Ctrl+P, Ctrl+L, and Enter still behave normally.
|
|
284
|
-
|
|
285
|
-
Run automated tests with `vp test run`; run formatting, linting, and type checks with `vp check`.
|
|
286
|
-
|
|
287
|
-
## Implementation plan
|
|
288
|
-
|
|
289
|
-
1. Implement the package extension entrypoint at `src/index.ts` (the package manifest already exposes it via `pi.extensions`).
|
|
290
|
-
2. Define attachment state and helpers:
|
|
291
|
-
- path tokenization/unescaping,
|
|
292
|
-
- path resolution,
|
|
293
|
-
- MIME detection,
|
|
294
|
-
- image loading to base64,
|
|
295
|
-
- placeholder allocation.
|
|
296
|
-
3. Implement `ImageAttachmentEditor`:
|
|
297
|
-
- intercept bracketed paste,
|
|
298
|
-
- override `insertTextAtCursor`,
|
|
299
|
-
- delegate to `super` for all other input.
|
|
300
|
-
4. Register editor component on `session_start` when `ctx.hasUI`.
|
|
301
|
-
5. Register `input` event transformer to attach images by placeholder order.
|
|
302
|
-
6. Manual test scenarios:
|
|
303
|
-
- Ctrl+V image clipboard on macOS.
|
|
304
|
-
- Drag image file from Finder into terminal.
|
|
305
|
-
- Paste a normal image path.
|
|
306
|
-
- Paste path with spaces.
|
|
307
|
-
- Paste multiple images.
|
|
308
|
-
- Delete placeholder before submit.
|
|
309
|
-
- Submit while agent is streaming as steer/follow-up.
|
|
310
|
-
|
|
311
|
-
## API research notes for implementers
|
|
312
|
-
|
|
313
|
-
Use these references before re-researching pi/TUI internals:
|
|
314
|
-
|
|
315
|
-
- Extension docs: `/Users/beowulf/.vite-plus/packages/@earendil-works/pi-coding-agent/lib/node_modules/@earendil-works/pi-coding-agent/docs/extensions.md`
|
|
316
|
-
- Input transform API is in the "Input Events" section.
|
|
317
|
-
- Custom editor API examples are in the "Custom Editor" section.
|
|
318
|
-
- `ctx.ui.getEditorComponent()`/`setEditorComponent()` wrapping examples are near the UI API examples.
|
|
319
|
-
- TUI docs: `/Users/beowulf/.vite-plus/packages/@earendil-works/pi-coding-agent/lib/node_modules/@earendil-works/pi-coding-agent/docs/tui.md`
|
|
320
|
-
- Pattern 7 documents using `CustomEditor` while preserving app keybindings.
|
|
321
|
-
- Example custom editors:
|
|
322
|
-
- `node_modules/@earendil-works/pi-coding-agent/examples/extensions/modal-editor.ts`
|
|
323
|
-
- `node_modules/@earendil-works/pi-coding-agent/examples/extensions/rainbow-editor.ts`
|
|
324
|
-
- Runtime type declarations used during implementation:
|
|
325
|
-
- `node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/components/custom-editor.d.ts`
|
|
326
|
-
- `CustomEditor` constructor: `(tui, theme, keybindings, options?)`.
|
|
327
|
-
- Public hooks include `handleInput(data)` and `insertTextAtCursor(text)` via the base `Editor`.
|
|
328
|
-
- `node_modules/@earendil-works/pi-tui/dist/components/editor.d.ts`
|
|
329
|
-
- `Editor` exposes `getText()`, `getExpandedText()`, `setText()`, `insertTextAtCursor()`, and `handleInput()`.
|
|
330
|
-
- `handlePaste()` is private; do not override it.
|
|
331
|
-
- `node_modules/@earendil-works/pi-ai/dist/types.d.ts` (resolved through pnpm store if not directly symlinked)
|
|
332
|
-
- `ImageContent` shape is `{ type: "image"; data: string; mimeType: string }`.
|
|
333
|
-
- `node_modules/@earendil-works/pi-coding-agent/dist/core/extensions/types.d.ts`
|
|
334
|
-
- `InputEventResult` transform shape is `{ action: "transform"; text; images? }`.
|
|
335
|
-
- Bracketed paste details are visible in `node_modules/@earendil-works/pi-tui/dist/components/editor.js`:
|
|
336
|
-
- Paste start: `\x1b[200~`.
|
|
337
|
-
- Paste end: `\x1b[201~`.
|
|
338
|
-
- The built-in editor buffers paste content and calls its private `handlePaste()`; this extension should intercept complete bracketed paste sequences in `CustomEditor.handleInput()` before delegating.
|
|
339
|
-
- Existing pi image clipboard flow is in `node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/interactive-mode.js`:
|
|
340
|
-
- It saves a clipboard image to a temp file, then calls `editor.insertTextAtCursor?.(filePath)`, so overriding `insertTextAtCursor()` is the public extension hook for that path.
|
|
341
|
-
|
|
342
|
-
## Acceptance criteria
|
|
343
|
-
|
|
344
|
-
- Pasting or drag-dropping an image path replaces it with `[#imageN]` in the editor.
|
|
345
|
-
- Ctrl+V image clipboard still works, but inserts `[#imageN]` instead of the temp path.
|
|
346
|
-
- Submitting a prompt with `[#imageN]` sends the actual image as an attachment in the same user turn.
|
|
347
|
-
- The assistant can answer image-content questions without first calling the `read` tool.
|
|
348
|
-
- Non-image pastes behave exactly like default pi.
|
|
349
|
-
- The extension does not break core app keybindings such as Escape, Ctrl+C, Ctrl+D, Ctrl+P, Ctrl+L, and Enter.
|