@caupulican/pi-adaptative 0.80.2 → 0.80.4

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.
@@ -26,7 +26,7 @@ import { BUILTIN_SLASH_COMMANDS } from "../../core/slash-commands.js";
26
26
  import { isInstallTelemetryEnabled } from "../../core/telemetry.js";
27
27
  import { getChangelogPath, getNewEntries, parseChangelog } from "../../utils/changelog.js";
28
28
  import { copyToClipboard } from "../../utils/clipboard.js";
29
- import { extensionForImageMimeType, readClipboardImage } from "../../utils/clipboard-image.js";
29
+ import { readClipboardImage } from "../../utils/clipboard-image.js";
30
30
  import { parseGitUrl } from "../../utils/git.js";
31
31
  import { getCwdRelativePath, resolvePath } from "../../utils/paths.js";
32
32
  import { getPiUserAgent } from "../../utils/pi-user-agent.js";
@@ -206,6 +206,8 @@ export class InteractiveMode {
206
206
  isInitialized = false;
207
207
  onInputCallback;
208
208
  pendingUserInputs = [];
209
+ pendingClipboardImages = [];
210
+ clipboardImageCounter = 0;
209
211
  loadingAnimation = undefined;
210
212
  workingMessage = undefined;
211
213
  workingVisible = true;
@@ -641,7 +643,7 @@ export class InteractiveMode {
641
643
  while (true) {
642
644
  const userInput = await this.getUserInput();
643
645
  try {
644
- await this.session.prompt(userInput);
646
+ await this.session.prompt(userInput.text, { images: userInput.images });
645
647
  }
646
648
  catch (error) {
647
649
  const errorMessage = error instanceof Error ? error.message : "Unknown error occurred";
@@ -2107,21 +2109,49 @@ export class InteractiveMode {
2107
2109
  try {
2108
2110
  const image = await readClipboardImage();
2109
2111
  if (!image) {
2112
+ this.showStatus("No image found on the clipboard");
2110
2113
  return;
2111
2114
  }
2112
- // Write to temp file
2113
- const tmpDir = os.tmpdir();
2114
- const ext = extensionForImageMimeType(image.mimeType) ?? "png";
2115
- const fileName = `pi-clipboard-${crypto.randomUUID()}.${ext}`;
2116
- const filePath = path.join(tmpDir, fileName);
2117
- fs.writeFileSync(filePath, Buffer.from(image.bytes));
2118
- // Insert file path directly
2119
- this.editor.insertTextAtCursor?.(filePath);
2115
+ const label = this.nextClipboardImageLabel();
2116
+ const mimeType = image.mimeType.split(";")[0]?.trim().toLowerCase() || image.mimeType;
2117
+ this.pendingClipboardImages.push({
2118
+ label,
2119
+ content: {
2120
+ type: "image",
2121
+ data: Buffer.from(image.bytes).toString("base64"),
2122
+ mimeType,
2123
+ },
2124
+ });
2125
+ this.editor.insertTextAtCursor?.(`${label} `);
2126
+ this.showStatus(`Attached clipboard image ${label} (${mimeType})`);
2120
2127
  this.ui.requestRender();
2121
2128
  }
2122
- catch {
2123
- // Silently ignore clipboard errors (may not have permission, etc.)
2129
+ catch (error) {
2130
+ const message = error instanceof Error ? error.message : String(error);
2131
+ this.showWarning(`Failed to paste image: ${message}`);
2132
+ }
2133
+ }
2134
+ nextClipboardImageLabel() {
2135
+ if (this.pendingClipboardImages.length === 0) {
2136
+ this.clipboardImageCounter = 0;
2124
2137
  }
2138
+ this.clipboardImageCounter += 1;
2139
+ return `[Image #${this.clipboardImageCounter}]`;
2140
+ }
2141
+ takeClipboardImagesForText(text) {
2142
+ if (this.pendingClipboardImages.length === 0) {
2143
+ return undefined;
2144
+ }
2145
+ const images = this.pendingClipboardImages
2146
+ .filter((image) => text.includes(image.label))
2147
+ .map((image) => image.content);
2148
+ this.pendingClipboardImages = [];
2149
+ this.clipboardImageCounter = 0;
2150
+ return images.length > 0 ? images : undefined;
2151
+ }
2152
+ buildUserInputSubmission(text) {
2153
+ const images = this.takeClipboardImagesForText(text);
2154
+ return images ? { text, images } : { text };
2125
2155
  }
2126
2156
  setupEditorSubmitHandler() {
2127
2157
  this.defaultEditor.onSubmit = async (text) => {
@@ -2286,16 +2316,18 @@ export class InteractiveMode {
2286
2316
  await this.session.prompt(text);
2287
2317
  }
2288
2318
  else {
2289
- this.queueCompactionMessage(text, "steer");
2319
+ const images = this.takeClipboardImagesForText(text);
2320
+ this.queueCompactionMessage(text, "steer", images);
2290
2321
  }
2291
2322
  return;
2292
2323
  }
2293
2324
  // If streaming, use prompt() with steer behavior
2294
2325
  // This handles extension commands (execute immediately), prompt template expansion, and queueing
2295
2326
  if (this.session.isStreaming) {
2327
+ const images = this.takeClipboardImagesForText(text);
2296
2328
  this.editor.addToHistory?.(text);
2297
2329
  this.editor.setText("");
2298
- await this.session.prompt(text, { streamingBehavior: "steer" });
2330
+ await this.session.prompt(text, { streamingBehavior: "steer", images });
2299
2331
  this.updatePendingMessagesDisplay();
2300
2332
  this.ui.requestRender();
2301
2333
  return;
@@ -2303,11 +2335,12 @@ export class InteractiveMode {
2303
2335
  // Normal message submission
2304
2336
  // First, move any pending bash components to chat
2305
2337
  this.flushPendingBashComponents();
2338
+ const submission = this.buildUserInputSubmission(text);
2306
2339
  if (this.onInputCallback) {
2307
- this.onInputCallback(text);
2340
+ this.onInputCallback(submission);
2308
2341
  }
2309
2342
  else {
2310
- this.pendingUserInputs.push(text);
2343
+ this.pendingUserInputs.push(submission);
2311
2344
  }
2312
2345
  this.editor.addToHistory?.(text);
2313
2346
  };
@@ -2776,9 +2809,9 @@ export class InteractiveMode {
2776
2809
  return queuedInput;
2777
2810
  }
2778
2811
  return new Promise((resolve) => {
2779
- this.onInputCallback = (text) => {
2812
+ this.onInputCallback = (submission) => {
2780
2813
  this.onInputCallback = undefined;
2781
- resolve(text);
2814
+ resolve(submission);
2782
2815
  };
2783
2816
  });
2784
2817
  }
@@ -2975,23 +3008,25 @@ export class InteractiveMode {
2975
3008
  await this.session.prompt(text);
2976
3009
  }
2977
3010
  else {
2978
- this.queueCompactionMessage(text, "followUp");
3011
+ const images = this.takeClipboardImagesForText(text);
3012
+ this.queueCompactionMessage(text, "followUp", images);
2979
3013
  }
2980
3014
  return;
2981
3015
  }
2982
3016
  // Alt+Enter queues a follow-up message (waits until agent finishes)
2983
3017
  // This handles extension commands (execute immediately), prompt template expansion, and queueing
2984
3018
  if (this.session.isStreaming) {
3019
+ const images = this.takeClipboardImagesForText(text);
2985
3020
  this.editor.addToHistory?.(text);
2986
3021
  this.editor.setText("");
2987
- await this.session.prompt(text, { streamingBehavior: "followUp" });
3022
+ await this.session.prompt(text, { streamingBehavior: "followUp", images });
2988
3023
  this.updatePendingMessagesDisplay();
2989
3024
  this.ui.requestRender();
2990
3025
  }
2991
3026
  // If not streaming, Alt+Enter acts like regular Enter (trigger onSubmit)
2992
3027
  else if (this.editor.onSubmit) {
3028
+ await this.editor.onSubmit(text);
2993
3029
  this.editor.setText("");
2994
- this.editor.onSubmit(text);
2995
3030
  }
2996
3031
  }
2997
3032
  handleDequeue() {
@@ -3246,8 +3281,8 @@ export class InteractiveMode {
3246
3281
  }
3247
3282
  return allQueued.length;
3248
3283
  }
3249
- queueCompactionMessage(text, mode) {
3250
- this.compactionQueuedMessages.push({ text, mode });
3284
+ queueCompactionMessage(text, mode, images) {
3285
+ this.compactionQueuedMessages.push({ text, mode, images });
3251
3286
  this.editor.addToHistory?.(text);
3252
3287
  this.editor.setText("");
3253
3288
  this.updatePendingMessagesDisplay();
@@ -3282,10 +3317,10 @@ export class InteractiveMode {
3282
3317
  await this.session.prompt(message.text);
3283
3318
  }
3284
3319
  else if (message.mode === "followUp") {
3285
- await this.session.followUp(message.text);
3320
+ await this.session.followUp(message.text, message.images);
3286
3321
  }
3287
3322
  else {
3288
- await this.session.steer(message.text);
3323
+ await this.session.steer(message.text, message.images);
3289
3324
  }
3290
3325
  }
3291
3326
  this.updatePendingMessagesDisplay();
@@ -3308,7 +3343,7 @@ export class InteractiveMode {
3308
3343
  await this.session.prompt(message.text);
3309
3344
  }
3310
3345
  // Send first prompt (starts streaming)
3311
- const promptPromise = this.session.prompt(firstPrompt.text).catch((error) => {
3346
+ const promptPromise = this.session.prompt(firstPrompt.text, { images: firstPrompt.images }).catch((error) => {
3312
3347
  restoreQueue(error);
3313
3348
  });
3314
3349
  // Queue remaining messages
@@ -3317,10 +3352,10 @@ export class InteractiveMode {
3317
3352
  await this.session.prompt(message.text);
3318
3353
  }
3319
3354
  else if (message.mode === "followUp") {
3320
- await this.session.followUp(message.text);
3355
+ await this.session.followUp(message.text, message.images);
3321
3356
  }
3322
3357
  else {
3323
- await this.session.steer(message.text);
3358
+ await this.session.steer(message.text, message.images);
3324
3359
  }
3325
3360
  }
3326
3361
  this.updatePendingMessagesDisplay();