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 +10 -6
- package/dist/index.d.mts +75 -3
- package/dist/index.mjs +546 -48
- package/package.json +5 -2
- package/src/clipboard.ts +73 -3
- package/src/config.ts +14 -0
- package/src/editor.ts +107 -19
- package/src/image-utils.ts +200 -11
- package/src/index.ts +27 -10
- package/src/optimize-image.ts +209 -0
- package/src/preview.ts +54 -5
- package/src/terminal-input.ts +2 -6
- package/src/types.ts +19 -1
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
|
-
| `
|
|
111
|
-
| `
|
|
112
|
-
| `customEditor.
|
|
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:
|
|
140
|
-
|
|
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 };
|