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 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 | Description |
109
- | --------------------------------------- | ------- | --------------------------------------------------------------------------------------------------------------------------- |
110
- | `customEditor.enabled` | `true` | Replaces pi's input editor with paster's editor integration. Disable this to keep pi's default editor. |
111
- | `customEditor.showImagePreview` | `true` | Shows an image preview above the input when the cursor is inside an image placeholder. Requires `customEditor.enabled`. |
112
- | `customEditor.deletePlaceholderAsBlock` | `true` | Makes backspace/delete remove the whole placeholder when editing inside or adjacent to it. Requires `customEditor.enabled`. |
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: ImageTheme);
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 };