pi-paster 0.1.5 → 0.2.1

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
@@ -87,7 +87,7 @@ On macOS, pi exposes an image paste action through its keybinding system. In the
87
87
 
88
88
  ## Configuration
89
89
 
90
- By default all editor integrations are enabled.
90
+ By default all editor integrations are enabled, and submitted image previews render in the raw chat-history style.
91
91
 
92
92
  To customize behavior, load a small wrapper extension:
93
93
 
@@ -95,6 +95,8 @@ To customize behavior, load a small wrapper extension:
95
95
  import { createPaster } from "pi-paster";
96
96
 
97
97
  export default createPaster({
98
+ submittedPreviewStyle: "raw",
99
+ includeImagePathsInPrompt: true,
98
100
  customEditor: {
99
101
  enabled: true,
100
102
  showImagePreview: true,
@@ -105,11 +107,13 @@ export default createPaster({
105
107
 
106
108
  ### Options
107
109
 
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`. |
110
+ | Option | Default | Description |
111
+ | --------------------------------------- | ------- | --------------------------------------------------------------------------------------------------------------------------------------- |
112
+ | `submittedPreviewStyle` | `"raw"` | How submitted image previews render in chat history. Use `"collapsible"` to wrap them in pi's ctrl+o expandable/collapsible message UI. |
113
+ | `includeImagePathsInPrompt` | `true` | Appends placeholder-to-local-path mappings to the submitted prompt so the agent can manipulate the source image files when asked. |
114
+ | `customEditor.enabled` | `true` | Replaces pi's input editor with paster's editor integration. Disable this to keep pi's default editor. |
115
+ | `customEditor.showImagePreview` | `true` | Shows an image preview above the input when the cursor is inside an image placeholder. Requires `customEditor.enabled`. |
116
+ | `customEditor.deletePlaceholderAsBlock` | `true` | Makes backspace/delete remove the whole placeholder when editing inside or adjacent to it. Requires `customEditor.enabled`. |
113
117
 
114
118
  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
119
 
package/dist/index.d.mts CHANGED
@@ -3,7 +3,12 @@ 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";
6
7
  interface PasterConfig {
8
+ /** How submitted attachment previews render in chat history. */
9
+ submittedPreviewStyle?: SubmittedPreviewStyle;
10
+ /** Append local image paths to the submitted prompt so the agent can manipulate the source files. */
11
+ includeImagePathsInPrompt?: boolean;
7
12
  customEditor?: {
8
13
  /** 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
14
  showImagePreview?: boolean; /** Treat image placeholders as atomic blocks for backspace/delete. */
@@ -11,6 +16,8 @@ interface PasterConfig {
11
16
  };
12
17
  }
13
18
  interface ResolvedPasterConfig {
19
+ submittedPreviewStyle: SubmittedPreviewStyle;
20
+ includeImagePathsInPrompt: boolean;
14
21
  customEditor: {
15
22
  enabled: boolean;
16
23
  showImagePreview: boolean;
@@ -23,6 +30,8 @@ declare function resolvePasterConfig(config?: PasterConfig): ResolvedPasterConfi
23
30
  //#region src/types.d.ts
24
31
  declare const EXTENSION_NAME = "paster";
25
32
  declare const MAX_IMAGE_BYTES: number;
33
+ declare const ANTHROPIC_MAX_DIMENSION = 8000;
34
+ declare const ANTHROPIC_MAX_IMAGE_BYTES: number;
26
35
  type SupportedImageMimeType = "image/png" | "image/jpeg" | "image/webp" | "image/gif";
27
36
  interface ImageAttachment {
28
37
  id: number;
@@ -32,6 +41,14 @@ interface ImageAttachment {
32
41
  data: string;
33
42
  dimensions?: ImageDimensions;
34
43
  createdAt: number;
44
+ /** True once optimizeImageBytes has run on this attachment. */
45
+ optimized?: boolean;
46
+ /** Original (pre-optimization) base64 size in bytes — informational. */
47
+ originalBytes?: number;
48
+ /** Final (post-optimization) base64 size in bytes — informational. */
49
+ finalBytes?: number;
50
+ /** Human-readable trail of optimization actions applied, if any. */
51
+ optimizeActions?: string[];
35
52
  }
36
53
  interface LoadedImage {
37
54
  originalPath: string;
@@ -66,6 +83,25 @@ type ClipboardImageResult = {
66
83
  };
67
84
  declare function readClipboardImage(maxBytes?: number): ClipboardImageResult;
68
85
  //#endregion
86
+ //#region src/optimize-image.d.ts
87
+ interface OptimizeResult {
88
+ data: string;
89
+ mimeType: SupportedImageMimeType;
90
+ originalBytes: number;
91
+ finalBytes: number;
92
+ originalDim?: {
93
+ width: number;
94
+ height: number;
95
+ };
96
+ finalDim?: {
97
+ width: number;
98
+ height: number;
99
+ };
100
+ actions: string[];
101
+ changed: boolean;
102
+ }
103
+ declare function optimizeImageBytes(input: Buffer, mime: SupportedImageMimeType): Promise<OptimizeResult>;
104
+ //#endregion
69
105
  //#region src/store.d.ts
70
106
  declare class AttachmentStore {
71
107
  private nextId;
@@ -80,6 +116,7 @@ declare class AttachmentStore {
80
116
  //#region src/editor.d.ts
81
117
  declare const PASTE_START = "\u001B[200~";
82
118
  declare const PASTE_END = "\u001B[201~";
119
+ declare function segmentTextWithAtomicImages(text: string, store: AttachmentStore, validPasteIds?: Set<number>): Intl.SegmentData[];
83
120
  declare class PasterEditor extends CustomEditor {
84
121
  private readonly pasterKeybindings;
85
122
  private readonly pasterOptions;
@@ -96,9 +133,12 @@ declare class PasterEditor extends CustomEditor {
96
133
  insertTextAtCursor(text: string): void;
97
134
  handleInput(data: string): void;
98
135
  clearCursorPreview(): void;
136
+ private installAtomicImageSegmentation;
99
137
  private handlePasteClipboardImage;
100
138
  private handleBracketedPaste;
139
+ private handleAtomicPlaceholderNavigation;
101
140
  private handleAtomicPlaceholderDelete;
141
+ private setCursor;
102
142
  private deleteLineRange;
103
143
  private transform;
104
144
  private updateCursorPreview;
@@ -110,8 +150,14 @@ interface PathToken {
110
150
  value: string;
111
151
  start: number;
112
152
  end: number;
153
+ bare: boolean;
113
154
  }
114
155
  declare function detectImageMimeType(bytes: Uint8Array): SupportedImageMimeType | undefined;
156
+ declare function isWindowsDrivePath(value: string): boolean;
157
+ declare function isWindowsUncPath(value: string): boolean;
158
+ declare function isWindowsLikePath(value: string): boolean;
159
+ declare function isWsl(): boolean;
160
+ declare function windowsToWslPath(windowsPath: string): string;
115
161
  declare function resolveImagePath(input: string, cwd: string): string;
116
162
  declare function shellUnescape(input: string): string;
117
163
  declare function tokenizePathLikeText(text: string): PathToken[];
@@ -130,15 +176,41 @@ declare function replaceImagePathsInText(text: string, options: {
130
176
  accepted: ImageAttachment[];
131
177
  };
132
178
  declare function imagesForText(store: AttachmentStore, text: string, existing?: PasterImageContent[]): PasterImageContent[];
179
+ declare function appendImagePathContext(text: string, attachments: ImageAttachment[]): string;
180
+ /**
181
+ * Async variant of imagesForText that runs each attachment through the
182
+ * Anthropic-aware image optimizer (resize to 8000px cap, JPEG ladder to stay
183
+ * under the 5 MB / 32 MB request caps). Optimization is cached on the
184
+ * attachment so the cost is paid once per image, not per submit.
185
+ *
186
+ * Used by paster's `input` handler; safe to await on the hot path because
187
+ * sharp is only invoked when the image is actually over the limits.
188
+ */
189
+ declare function imagesForTextOptimized(store: AttachmentStore, text: string, existing?: PasterImageContent[]): Promise<PasterImageContent[]>;
190
+ declare function describeReject(result: Exclude<LoadImageResult, {
191
+ ok: true;
192
+ }>, notify?: (message: string) => void): void;
133
193
  //#endregion
134
194
  //#region src/preview.d.ts
195
+ type ImagePreviewMessageStyle = "raw" | "collapsible";
196
+ interface ImagePreviewMessageTheme extends ImageTheme {
197
+ background?: (text: string) => string;
198
+ title?: (text: string) => string;
199
+ muted?: (text: string) => string;
200
+ }
135
201
  declare class ImagePreviewMessage implements Component {
136
202
  private readonly attachments;
137
203
  private readonly theme;
204
+ private readonly options;
138
205
  private readonly images;
139
- constructor(attachments: ImageAttachment[], theme: ImageTheme);
140
- render(width: number): string[];
206
+ constructor(attachments: ImageAttachment[], theme: ImagePreviewMessageTheme, options?: {
207
+ expanded?: boolean;
208
+ style?: ImagePreviewMessageStyle;
209
+ });
141
210
  invalidate(): void;
211
+ render(width: number): string[];
212
+ private renderRaw;
213
+ private renderCollapsible;
142
214
  }
143
215
  interface CursorPreviewTheme {
144
216
  title: (text: string) => string;
@@ -174,4 +246,4 @@ declare function createImagePasteTerminalInputHandler(options: {
174
246
  declare function createPaster(config?: PasterConfig): (pi: ExtensionAPI) => void;
175
247
  declare function paster(pi: ExtensionAPI, config?: PasterConfig): void;
176
248
  //#endregion
177
- export { AttachmentStore, ClipboardImageResult, CursorImagePreviewWidget, DEFAULT_PASTER_CONFIG, EXTENSION_NAME, ImageAttachment, ImagePreviewMessage, LoadImageResult, LoadedImage, MAX_IMAGE_BYTES, PASTE_END, PASTE_START, PasterConfig, PasterEditor, PasterImageContent, PasterPreviewDetails, ResolvedPasterConfig, SupportedImageMimeType, TerminalInputResult, createImagePasteTerminalInputHandler, createPaster, paster as default, detectImageMimeType, dimensionsForImage, imagesForText, loadImageFromPath, readClipboardImage, replaceImagePathsInText, resolveImagePath, resolvePasterConfig, shellUnescape, tokenizePathLikeText };
249
+ export { ANTHROPIC_MAX_DIMENSION, ANTHROPIC_MAX_IMAGE_BYTES, AttachmentStore, ClipboardImageResult, CursorImagePreviewWidget, DEFAULT_PASTER_CONFIG, EXTENSION_NAME, ImageAttachment, ImagePreviewMessage, ImagePreviewMessageStyle, LoadImageResult, LoadedImage, MAX_IMAGE_BYTES, OptimizeResult, PASTE_END, PASTE_START, PasterConfig, PasterEditor, PasterImageContent, PasterPreviewDetails, ResolvedPasterConfig, SubmittedPreviewStyle, SupportedImageMimeType, TerminalInputResult, appendImagePathContext, createImagePasteTerminalInputHandler, createPaster, paster as default, describeReject, detectImageMimeType, dimensionsForImage, imagesForText, imagesForTextOptimized, isWindowsDrivePath, isWindowsLikePath, isWindowsUncPath, isWsl, loadImageFromPath, optimizeImageBytes, readClipboardImage, replaceImagePathsInText, resolveImagePath, resolvePasterConfig, segmentTextWithAtomicImages, shellUnescape, tokenizePathLikeText, windowsToWslPath };