pi-paster 0.2.0 → 0.2.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 +41 -6
- package/dist/index.d.mts +80 -2
- package/dist/index.mjs +372 -23
- package/package.json +2 -2
- package/src/compress.ts +329 -0
- package/src/config.ts +67 -0
- package/src/editor.ts +105 -11
- package/src/image-utils.ts +8 -0
- package/src/index.ts +46 -8
- package/src/preview.ts +93 -4
- package/src/types.ts +11 -0
package/README.md
CHANGED
|
@@ -30,6 +30,7 @@ Terminal image workflows are awkward: dragging a screenshot into a terminal usua
|
|
|
30
30
|
- Attaches only placeholders still present in the submitted prompt.
|
|
31
31
|
- Preserves attachment order by first placeholder occurrence.
|
|
32
32
|
- Shows submitted image previews in chat history.
|
|
33
|
+
- Provides `/image-compress` to fork the current branch into a new session where image blocks are replaced with text summaries.
|
|
33
34
|
- Optional custom editor integration:
|
|
34
35
|
- cursor-based image preview above the input
|
|
35
36
|
- atomic deletion of whole image placeholders
|
|
@@ -79,6 +80,22 @@ What is wrong in this screenshot? [#image 1]
|
|
|
79
80
|
|
|
80
81
|
On submit, the text and matching image attachment are sent together.
|
|
81
82
|
|
|
83
|
+
## Image compression command
|
|
84
|
+
|
|
85
|
+
Run `/image-compress` to summarize every image block in the current conversation branch and switch to a new session where those image blocks are replaced by text summaries.
|
|
86
|
+
|
|
87
|
+
This is intentionally different from summarizing or compacting the whole session. Sometimes the text/tool context is still useful as-is, but screenshots are making the context heavy. Image compression preserves the same branch shape and surrounding conversation while pruning expensive image blocks into concise descriptions, so the agent keeps the relevant context without carrying every original image.
|
|
88
|
+
|
|
89
|
+
The original session is not modified. Paster copies the active branch into a new session, replaces image blocks during the copy, and links the new session back to the original as its parent. By default, paster also adds a visible collapsible compression report for the user; the report details are stored outside model context.
|
|
90
|
+
|
|
91
|
+
By default the command uses a pi subprocess with `openai-codex/gpt-5.4-mini` and a short 2-4 sentence summarization prompt. If that model is not configured or lacks credentials, paster shows a setup hint and you can configure a different summarization model. You can pass a one-off model after the command:
|
|
92
|
+
|
|
93
|
+
```text
|
|
94
|
+
/image-compress openrouter/google/gemini-2.5-flash
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
No extra runtime dependencies are used; the command shells out to the installed `pi` executable.
|
|
98
|
+
|
|
82
99
|
## Clipboard image paste
|
|
83
100
|
|
|
84
101
|
On macOS, pi exposes an image paste action through its keybinding system. In the default pi keybindings this is `Ctrl+V`.
|
|
@@ -87,7 +104,7 @@ On macOS, pi exposes an image paste action through its keybinding system. In the
|
|
|
87
104
|
|
|
88
105
|
## Configuration
|
|
89
106
|
|
|
90
|
-
By default all editor integrations are enabled.
|
|
107
|
+
By default all editor integrations are enabled, and submitted image previews render in the raw chat-history style.
|
|
91
108
|
|
|
92
109
|
To customize behavior, load a small wrapper extension:
|
|
93
110
|
|
|
@@ -95,6 +112,15 @@ To customize behavior, load a small wrapper extension:
|
|
|
95
112
|
import { createPaster } from "pi-paster";
|
|
96
113
|
|
|
97
114
|
export default createPaster({
|
|
115
|
+
submittedPreviewStyle: "raw",
|
|
116
|
+
includeImagePathsInPrompt: true,
|
|
117
|
+
imageCompression: {
|
|
118
|
+
enabled: true,
|
|
119
|
+
command: "image-compress",
|
|
120
|
+
model: "openai-codex/gpt-5.4-mini",
|
|
121
|
+
prompt: "Summarize this image in 2-4 concise sentences.",
|
|
122
|
+
includeReport: true,
|
|
123
|
+
},
|
|
98
124
|
customEditor: {
|
|
99
125
|
enabled: true,
|
|
100
126
|
showImagePreview: true,
|
|
@@ -105,11 +131,20 @@ export default createPaster({
|
|
|
105
131
|
|
|
106
132
|
### Options
|
|
107
133
|
|
|
108
|
-
| Option | Default
|
|
109
|
-
| --------------------------------------- |
|
|
110
|
-
| `
|
|
111
|
-
| `
|
|
112
|
-
| `
|
|
134
|
+
| Option | Default | Description |
|
|
135
|
+
| --------------------------------------- | ----------------------------- | --------------------------------------------------------------------------------------------------------------------------------------- |
|
|
136
|
+
| `submittedPreviewStyle` | `"raw"` | How submitted image previews render in chat history. Use `"collapsible"` to wrap them in pi's ctrl+o expandable/collapsible message UI. |
|
|
137
|
+
| `includeImagePathsInPrompt` | `true` | Appends placeholder-to-local-path mappings to the submitted prompt so the agent can manipulate the source image files when asked. |
|
|
138
|
+
| `imageCompression.enabled` | `true` | Registers the image compression command. |
|
|
139
|
+
| `imageCompression.command` | `"image-compress"` | Slash command name without the leading slash. |
|
|
140
|
+
| `imageCompression.model` | `"openai-codex/gpt-5.4-mini"` | Model passed to pi for per-image summarization. Set to `""` to use pi's default model. |
|
|
141
|
+
| `imageCompression.prompt` | built-in | Prompt used to summarize each image. |
|
|
142
|
+
| `imageCompression.piCommand` | `"pi"` | pi executable used for summarization subprocesses. |
|
|
143
|
+
| `imageCompression.timeoutMs` | `120000` | Per-image summarization timeout in milliseconds. |
|
|
144
|
+
| `imageCompression.includeReport` | `true` | Adds a visible collapsible compression report after the copied compressed branch. Report details are not sent to the agent. |
|
|
145
|
+
| `customEditor.enabled` | `true` | Replaces pi's input editor with paster's editor integration. Disable this to keep pi's default editor. |
|
|
146
|
+
| `customEditor.showImagePreview` | `true` | Shows an image preview above the input when the cursor is inside an image placeholder. Requires `customEditor.enabled`. |
|
|
147
|
+
| `customEditor.deletePlaceholderAsBlock` | `true` | Makes backspace/delete remove the whole placeholder when editing inside or adjacent to it. Requires `customEditor.enabled`. |
|
|
113
148
|
|
|
114
149
|
When `customEditor.enabled` is `false`, paster still handles bracketed terminal paste/drop image paths, but cursor previews, atomic placeholder deletion, and paster's clipboard-image handler are disabled.
|
|
115
150
|
|
package/dist/index.d.mts
CHANGED
|
@@ -3,7 +3,39 @@ import { Component, EditorTheme, ImageDimensions, ImageTheme, TUI } from "@earen
|
|
|
3
3
|
import { CustomEditor, ExtensionAPI, KeybindingsManager } from "@earendil-works/pi-coding-agent";
|
|
4
4
|
|
|
5
5
|
//#region src/config.d.ts
|
|
6
|
+
type SubmittedPreviewStyle = "raw" | "collapsible";
|
|
7
|
+
interface ImageCompressionConfig {
|
|
8
|
+
/** Enable the image compression slash command. */
|
|
9
|
+
enabled?: boolean;
|
|
10
|
+
/** Slash command name without the leading slash. */
|
|
11
|
+
command?: string;
|
|
12
|
+
/** Model passed to pi for image summarization. Pass an empty string to use pi's default model. */
|
|
13
|
+
model?: string;
|
|
14
|
+
/** Prompt used to summarize each image. */
|
|
15
|
+
prompt?: string;
|
|
16
|
+
/** pi executable used for summarization subprocesses. */
|
|
17
|
+
piCommand?: string;
|
|
18
|
+
/** Per-image summarization timeout in milliseconds. */
|
|
19
|
+
timeoutMs?: number;
|
|
20
|
+
/** Add a visible, collapsible UI report after compression. */
|
|
21
|
+
includeReport?: boolean;
|
|
22
|
+
}
|
|
23
|
+
interface ResolvedImageCompressionConfig {
|
|
24
|
+
enabled: boolean;
|
|
25
|
+
command: string;
|
|
26
|
+
model: string;
|
|
27
|
+
prompt: string;
|
|
28
|
+
piCommand: string;
|
|
29
|
+
timeoutMs: number;
|
|
30
|
+
includeReport: boolean;
|
|
31
|
+
}
|
|
6
32
|
interface PasterConfig {
|
|
33
|
+
/** How submitted attachment previews render in chat history. */
|
|
34
|
+
submittedPreviewStyle?: SubmittedPreviewStyle;
|
|
35
|
+
/** Append local image paths to the submitted prompt so the agent can manipulate the source files. */
|
|
36
|
+
includeImagePathsInPrompt?: boolean;
|
|
37
|
+
/** Configure the /image-compress command. */
|
|
38
|
+
imageCompression?: ImageCompressionConfig;
|
|
7
39
|
customEditor?: {
|
|
8
40
|
/** Replace pi's input editor to enable inline image UX features. */enabled?: boolean; /** Show an image preview above the input while the cursor is inside an image placeholder. */
|
|
9
41
|
showImagePreview?: boolean; /** Treat image placeholders as atomic blocks for backspace/delete. */
|
|
@@ -11,6 +43,9 @@ interface PasterConfig {
|
|
|
11
43
|
};
|
|
12
44
|
}
|
|
13
45
|
interface ResolvedPasterConfig {
|
|
46
|
+
submittedPreviewStyle: SubmittedPreviewStyle;
|
|
47
|
+
includeImagePathsInPrompt: boolean;
|
|
48
|
+
imageCompression: ResolvedImageCompressionConfig;
|
|
14
49
|
customEditor: {
|
|
15
50
|
enabled: boolean;
|
|
16
51
|
showImagePreview: boolean;
|
|
@@ -65,6 +100,15 @@ type LoadImageResult = {
|
|
|
65
100
|
interface PasterPreviewDetails {
|
|
66
101
|
placeholders: string[];
|
|
67
102
|
}
|
|
103
|
+
interface ImageCompressionReportItem {
|
|
104
|
+
index: number;
|
|
105
|
+
summary: string;
|
|
106
|
+
}
|
|
107
|
+
interface ImageCompressionReportDetails {
|
|
108
|
+
imageCount: number;
|
|
109
|
+
summaryCount: number;
|
|
110
|
+
items: ImageCompressionReportItem[];
|
|
111
|
+
}
|
|
68
112
|
//#endregion
|
|
69
113
|
//#region src/clipboard.d.ts
|
|
70
114
|
type ClipboardImageResult = {
|
|
@@ -76,6 +120,10 @@ type ClipboardImageResult = {
|
|
|
76
120
|
};
|
|
77
121
|
declare function readClipboardImage(maxBytes?: number): ClipboardImageResult;
|
|
78
122
|
//#endregion
|
|
123
|
+
//#region src/compress.d.ts
|
|
124
|
+
declare const DEFAULT_IMAGE_SUMMARY_PROMPT = "Summarize this image in 2-4 concise sentences. Include important visible text, UI elements, errors, diagrams, and details that may matter for future coding or design work.";
|
|
125
|
+
declare function registerImageCompressionCommand(pi: ExtensionAPI, config: ResolvedImageCompressionConfig): void;
|
|
126
|
+
//#endregion
|
|
79
127
|
//#region src/optimize-image.d.ts
|
|
80
128
|
interface OptimizeResult {
|
|
81
129
|
data: string;
|
|
@@ -109,6 +157,7 @@ declare class AttachmentStore {
|
|
|
109
157
|
//#region src/editor.d.ts
|
|
110
158
|
declare const PASTE_START = "\u001B[200~";
|
|
111
159
|
declare const PASTE_END = "\u001B[201~";
|
|
160
|
+
declare function segmentTextWithAtomicImages(text: string, store: AttachmentStore, validPasteIds?: Set<number>): Intl.SegmentData[];
|
|
112
161
|
declare class PasterEditor extends CustomEditor {
|
|
113
162
|
private readonly pasterKeybindings;
|
|
114
163
|
private readonly pasterOptions;
|
|
@@ -125,9 +174,12 @@ declare class PasterEditor extends CustomEditor {
|
|
|
125
174
|
insertTextAtCursor(text: string): void;
|
|
126
175
|
handleInput(data: string): void;
|
|
127
176
|
clearCursorPreview(): void;
|
|
177
|
+
private installAtomicImageSegmentation;
|
|
128
178
|
private handlePasteClipboardImage;
|
|
129
179
|
private handleBracketedPaste;
|
|
180
|
+
private handleAtomicPlaceholderNavigation;
|
|
130
181
|
private handleAtomicPlaceholderDelete;
|
|
182
|
+
private setCursor;
|
|
131
183
|
private deleteLineRange;
|
|
132
184
|
private transform;
|
|
133
185
|
private updateCursorPreview;
|
|
@@ -165,6 +217,7 @@ declare function replaceImagePathsInText(text: string, options: {
|
|
|
165
217
|
accepted: ImageAttachment[];
|
|
166
218
|
};
|
|
167
219
|
declare function imagesForText(store: AttachmentStore, text: string, existing?: PasterImageContent[]): PasterImageContent[];
|
|
220
|
+
declare function appendImagePathContext(text: string, attachments: ImageAttachment[]): string;
|
|
168
221
|
/**
|
|
169
222
|
* Async variant of imagesForText that runs each attachment through the
|
|
170
223
|
* Anthropic-aware image optimizer (resize to 8000px cap, JPEG ladder to stay
|
|
@@ -180,11 +233,36 @@ declare function describeReject(result: Exclude<LoadImageResult, {
|
|
|
180
233
|
}>, notify?: (message: string) => void): void;
|
|
181
234
|
//#endregion
|
|
182
235
|
//#region src/preview.d.ts
|
|
236
|
+
type ImagePreviewMessageStyle = "raw" | "collapsible";
|
|
237
|
+
interface ImagePreviewMessageTheme extends ImageTheme {
|
|
238
|
+
background?: (text: string) => string;
|
|
239
|
+
title?: (text: string) => string;
|
|
240
|
+
muted?: (text: string) => string;
|
|
241
|
+
}
|
|
183
242
|
declare class ImagePreviewMessage implements Component {
|
|
184
243
|
private readonly attachments;
|
|
185
244
|
private readonly theme;
|
|
245
|
+
private readonly options;
|
|
186
246
|
private readonly images;
|
|
187
|
-
constructor(attachments: ImageAttachment[], theme:
|
|
247
|
+
constructor(attachments: ImageAttachment[], theme: ImagePreviewMessageTheme, options?: {
|
|
248
|
+
expanded?: boolean;
|
|
249
|
+
style?: ImagePreviewMessageStyle;
|
|
250
|
+
});
|
|
251
|
+
invalidate(): void;
|
|
252
|
+
render(width: number): string[];
|
|
253
|
+
private renderRaw;
|
|
254
|
+
private renderCollapsible;
|
|
255
|
+
}
|
|
256
|
+
interface CompressionReportTheme {
|
|
257
|
+
background?: (text: string) => string;
|
|
258
|
+
title: (text: string) => string;
|
|
259
|
+
muted: (text: string) => string;
|
|
260
|
+
}
|
|
261
|
+
declare class ImageCompressionReportMessage implements Component {
|
|
262
|
+
private readonly details;
|
|
263
|
+
private readonly theme;
|
|
264
|
+
private readonly expanded;
|
|
265
|
+
constructor(details: ImageCompressionReportDetails, theme: CompressionReportTheme, expanded?: boolean);
|
|
188
266
|
render(width: number): string[];
|
|
189
267
|
invalidate(): void;
|
|
190
268
|
}
|
|
@@ -222,4 +300,4 @@ declare function createImagePasteTerminalInputHandler(options: {
|
|
|
222
300
|
declare function createPaster(config?: PasterConfig): (pi: ExtensionAPI) => void;
|
|
223
301
|
declare function paster(pi: ExtensionAPI, config?: PasterConfig): void;
|
|
224
302
|
//#endregion
|
|
225
|
-
export { ANTHROPIC_MAX_DIMENSION, ANTHROPIC_MAX_IMAGE_BYTES, AttachmentStore, ClipboardImageResult, CursorImagePreviewWidget, DEFAULT_PASTER_CONFIG, EXTENSION_NAME, ImageAttachment, ImagePreviewMessage, LoadImageResult, LoadedImage, MAX_IMAGE_BYTES, OptimizeResult, PASTE_END, PASTE_START, PasterConfig, PasterEditor, PasterImageContent, PasterPreviewDetails, ResolvedPasterConfig, SupportedImageMimeType, TerminalInputResult, createImagePasteTerminalInputHandler, createPaster, paster as default, describeReject, detectImageMimeType, dimensionsForImage, imagesForText, imagesForTextOptimized, isWindowsDrivePath, isWindowsLikePath, isWindowsUncPath, isWsl, loadImageFromPath, optimizeImageBytes, readClipboardImage, replaceImagePathsInText, resolveImagePath, resolvePasterConfig, shellUnescape, tokenizePathLikeText, windowsToWslPath };
|
|
303
|
+
export { ANTHROPIC_MAX_DIMENSION, ANTHROPIC_MAX_IMAGE_BYTES, AttachmentStore, ClipboardImageResult, CursorImagePreviewWidget, DEFAULT_IMAGE_SUMMARY_PROMPT, DEFAULT_PASTER_CONFIG, EXTENSION_NAME, ImageAttachment, ImageCompressionConfig, ImageCompressionReportDetails, ImageCompressionReportItem, ImageCompressionReportMessage, ImagePreviewMessage, ImagePreviewMessageStyle, LoadImageResult, LoadedImage, MAX_IMAGE_BYTES, OptimizeResult, PASTE_END, PASTE_START, PasterConfig, PasterEditor, PasterImageContent, PasterPreviewDetails, ResolvedImageCompressionConfig, ResolvedPasterConfig, SubmittedPreviewStyle, SupportedImageMimeType, TerminalInputResult, appendImagePathContext, createImagePasteTerminalInputHandler, createPaster, paster as default, describeReject, detectImageMimeType, dimensionsForImage, imagesForText, imagesForTextOptimized, isWindowsDrivePath, isWindowsLikePath, isWindowsUncPath, isWsl, loadImageFromPath, optimizeImageBytes, readClipboardImage, registerImageCompressionCommand, replaceImagePathsInText, resolveImagePath, resolvePasterConfig, segmentTextWithAtomicImages, shellUnescape, tokenizePathLikeText, windowsToWslPath };
|