@duetso/agent 0.1.81 → 0.1.83
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/dist/package.json +1 -1
- package/dist/src/memory/observational.d.ts +7 -0
- package/dist/src/memory/observational.d.ts.map +1 -1
- package/dist/src/memory/observational.js +7 -1
- package/dist/src/memory/observational.js.map +1 -1
- package/dist/src/tui/app.d.ts +25 -35
- package/dist/src/tui/app.d.ts.map +1 -1
- package/dist/src/tui/app.js +195 -2308
- package/dist/src/tui/app.js.map +1 -1
- package/dist/src/tui/autocomplete-controller.d.ts +83 -0
- package/dist/src/tui/autocomplete-controller.d.ts.map +1 -0
- package/dist/src/tui/autocomplete-controller.js +308 -0
- package/dist/src/tui/autocomplete-controller.js.map +1 -0
- package/dist/src/tui/boot-screen.d.ts +64 -0
- package/dist/src/tui/boot-screen.d.ts.map +1 -0
- package/dist/src/tui/boot-screen.js +137 -0
- package/dist/src/tui/boot-screen.js.map +1 -0
- package/dist/src/tui/controllers.d.ts +39 -0
- package/dist/src/tui/controllers.d.ts.map +1 -0
- package/dist/src/tui/controllers.js +53 -0
- package/dist/src/tui/controllers.js.map +1 -0
- package/dist/src/tui/copy-controller.d.ts +75 -0
- package/dist/src/tui/copy-controller.d.ts.map +1 -0
- package/dist/src/tui/copy-controller.js +140 -0
- package/dist/src/tui/copy-controller.js.map +1 -0
- package/dist/src/tui/history-replay.d.ts +33 -0
- package/dist/src/tui/history-replay.d.ts.map +1 -0
- package/dist/src/tui/history-replay.js +47 -0
- package/dist/src/tui/history-replay.js.map +1 -0
- package/dist/src/tui/initial-prompt.d.ts +24 -0
- package/dist/src/tui/initial-prompt.d.ts.map +1 -0
- package/dist/src/tui/initial-prompt.js +29 -0
- package/dist/src/tui/initial-prompt.js.map +1 -0
- package/dist/src/tui/key-handlers.d.ts +49 -0
- package/dist/src/tui/key-handlers.d.ts.map +1 -0
- package/dist/src/tui/key-handlers.js +192 -0
- package/dist/src/tui/key-handlers.js.map +1 -0
- package/dist/src/tui/layout.d.ts +71 -0
- package/dist/src/tui/layout.d.ts.map +1 -0
- package/dist/src/tui/layout.js +245 -0
- package/dist/src/tui/layout.js.map +1 -0
- package/dist/src/tui/paste-controller.d.ts +90 -0
- package/dist/src/tui/paste-controller.d.ts.map +1 -0
- package/dist/src/tui/paste-controller.js +234 -0
- package/dist/src/tui/paste-controller.js.map +1 -0
- package/dist/src/tui/question-picker.d.ts +111 -0
- package/dist/src/tui/question-picker.d.ts.map +1 -0
- package/dist/src/tui/question-picker.js +308 -0
- package/dist/src/tui/question-picker.js.map +1 -0
- package/dist/src/tui/renderer-lifecycle.d.ts +20 -0
- package/dist/src/tui/renderer-lifecycle.d.ts.map +1 -0
- package/dist/src/tui/renderer-lifecycle.js +50 -0
- package/dist/src/tui/renderer-lifecycle.js.map +1 -0
- package/dist/src/tui/session-subscription.d.ts +30 -0
- package/dist/src/tui/session-subscription.d.ts.map +1 -0
- package/dist/src/tui/session-subscription.js +78 -0
- package/dist/src/tui/session-subscription.js.map +1 -0
- package/dist/src/tui/slash-commands.d.ts +23 -0
- package/dist/src/tui/slash-commands.d.ts.map +1 -0
- package/dist/src/tui/slash-commands.js +79 -0
- package/dist/src/tui/slash-commands.js.map +1 -0
- package/dist/src/tui/starter-section.d.ts +99 -0
- package/dist/src/tui/starter-section.d.ts.map +1 -0
- package/dist/src/tui/starter-section.js +254 -0
- package/dist/src/tui/starter-section.js.map +1 -0
- package/dist/src/tui/status-controller.d.ts +94 -0
- package/dist/src/tui/status-controller.d.ts.map +1 -0
- package/dist/src/tui/status-controller.js +186 -0
- package/dist/src/tui/status-controller.js.map +1 -0
- package/dist/src/tui/step-renderer.d.ts +55 -0
- package/dist/src/tui/step-renderer.d.ts.map +1 -0
- package/dist/src/tui/step-renderer.js +246 -0
- package/dist/src/tui/step-renderer.js.map +1 -0
- package/dist/src/tui/transcript-writer.d.ts +63 -0
- package/dist/src/tui/transcript-writer.d.ts.map +1 -0
- package/dist/src/tui/transcript-writer.js +130 -0
- package/dist/src/tui/transcript-writer.js.map +1 -0
- package/dist/src/turn-runner/turn-runner.d.ts +69 -3
- package/dist/src/turn-runner/turn-runner.d.ts.map +1 -1
- package/dist/src/turn-runner/turn-runner.js +123 -20
- package/dist/src/turn-runner/turn-runner.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { type PasteEvent, type TextareaRenderable } from "@opentui/core";
|
|
2
|
+
import { type PendingImage } from "./paste.js";
|
|
3
|
+
import type { StatusController } from "./status-controller.js";
|
|
4
|
+
export interface PasteControllerOptions {
|
|
5
|
+
/** Composer textarea. The controller inserts `[Image #N]` placeholders
|
|
6
|
+
* into its buffer and falls back to text inserts when a clipboard probe
|
|
7
|
+
* yields plain text after a keystroke ate a paste event. */
|
|
8
|
+
inputField: TextareaRenderable;
|
|
9
|
+
/** Session id used to namespace the on-disk paste cache under
|
|
10
|
+
* `~/.duet/cache/paste/<sessionId>/`. */
|
|
11
|
+
sessionId: string;
|
|
12
|
+
/** Working directory for resolving relative paths handed to `/image`. */
|
|
13
|
+
workDir: string;
|
|
14
|
+
/** Append a bordered transcript block (label + body) for paste diagnostics
|
|
15
|
+
* and per-attachment confirmation lines. */
|
|
16
|
+
appendBlock: (label: string | null, body: string, color: string) => void;
|
|
17
|
+
/** Status surface that owns the bottom-hint attachment counter. The
|
|
18
|
+
* controller calls `setPendingImageCount` whenever the queue changes so
|
|
19
|
+
* the hint stays in sync. */
|
|
20
|
+
statusController: Pick<StatusController, "setPendingImageCount">;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Owns the pending-image queue surfaced into the prompt as `[Image #N]`
|
|
24
|
+
* placeholders. Handles three intake paths that converge on the same
|
|
25
|
+
* `persistPastedImage` / `loadImageFromPath` machinery:
|
|
26
|
+
*
|
|
27
|
+
* 1. Bracketed-paste events that carry binary image bytes (kitty, ghostty,
|
|
28
|
+
* recent iTerm2).
|
|
29
|
+
* 2. Text-shaped pastes that resolve to a single existing image file path
|
|
30
|
+
* (Finder/Files drag-paste, file:// URLs).
|
|
31
|
+
* 3. Manual probes triggered by Cmd+V/Ctrl+V or the `/paste` slash command
|
|
32
|
+
* against terminals that strip binary clipboards.
|
|
33
|
+
*
|
|
34
|
+
* The queue resets after every submit (callers drive that via `consume()`),
|
|
35
|
+
* so users see a fresh `#1` label per turn.
|
|
36
|
+
*/
|
|
37
|
+
export declare class PasteController {
|
|
38
|
+
private readonly inputField;
|
|
39
|
+
private readonly sessionId;
|
|
40
|
+
private readonly workDir;
|
|
41
|
+
private readonly appendBlock;
|
|
42
|
+
private readonly statusController;
|
|
43
|
+
private pendingImages;
|
|
44
|
+
/** Monotonic id used to label the next attachment. Reset together with
|
|
45
|
+
* `pendingImages` so labels restart at `#1` after each submit. */
|
|
46
|
+
private nextImageId;
|
|
47
|
+
constructor(options: PasteControllerOptions);
|
|
48
|
+
/** Read-only view of the currently queued attachments. Useful for callers
|
|
49
|
+
* that need to render attachment footnotes alongside the user message. */
|
|
50
|
+
attachments(): readonly PendingImage[];
|
|
51
|
+
/**
|
|
52
|
+
* Drain the queue, returning the attachments that were pending at submit
|
|
53
|
+
* time. The caller forwards `.attachment` payloads to the runner; the
|
|
54
|
+
* controller resets so the next turn starts at `[Image #1]`.
|
|
55
|
+
*/
|
|
56
|
+
consume(): PendingImage[];
|
|
57
|
+
/** Drop all queued attachments. No-op when the queue is already empty so
|
|
58
|
+
* `/clear-images` does not redundantly repaint the hint. */
|
|
59
|
+
clearPendingImages(): void;
|
|
60
|
+
/**
|
|
61
|
+
* Handle a bracketed-paste event from the textarea. Decides synchronously
|
|
62
|
+
* whether the payload is image bytes, a file path, an empty trigger that
|
|
63
|
+
* warrants a clipboard probe, or plain text that should fall through to
|
|
64
|
+
* the textarea's default insert path.
|
|
65
|
+
*/
|
|
66
|
+
handlePasteEvent(event: PasteEvent): Promise<void>;
|
|
67
|
+
/**
|
|
68
|
+
* Manual clipboard probe. Read the OS clipboard for an image right now and
|
|
69
|
+
* attach it if found; otherwise fall back to a text probe so a Cmd+V/Ctrl+V
|
|
70
|
+
* keystroke that ate a legitimate text paste does not silently swallow it.
|
|
71
|
+
* The `source` distinguishes the keystroke entry point from the `/paste`
|
|
72
|
+
* slash command so we only emit the "no readable image" diagnostic for
|
|
73
|
+
* explicit user intent.
|
|
74
|
+
*/
|
|
75
|
+
triggerClipboardProbe(source: "keystroke" | "slash"): Promise<void>;
|
|
76
|
+
/**
|
|
77
|
+
* Attach an image from disk by path. Drives the `/image <path>` slash
|
|
78
|
+
* command. Inserts the `[Image #N]` placeholder back into the (likely now
|
|
79
|
+
* empty) input so the user can keep typing their prompt with the image
|
|
80
|
+
* already attached.
|
|
81
|
+
*/
|
|
82
|
+
attachImageFromPath(rawPath: string): Promise<void>;
|
|
83
|
+
/**
|
|
84
|
+
* Persist raw image bytes (from a bracketed-paste event or clipboard probe)
|
|
85
|
+
* under the session cache and surface the resulting `[Image #N]`
|
|
86
|
+
* placeholder in the prompt buffer.
|
|
87
|
+
*/
|
|
88
|
+
private attachPastedImageBytes;
|
|
89
|
+
}
|
|
90
|
+
//# sourceMappingURL=paste-controller.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"paste-controller.d.ts","sourceRoot":"","sources":["../../../src/tui/paste-controller.ts"],"names":[],"mappings":"AAAA,OAAO,EAAoB,KAAK,UAAU,EAAE,KAAK,kBAAkB,EAAE,MAAM,eAAe,CAAC;AAE3F,OAAO,EAIL,KAAK,YAAY,EAKlB,MAAM,YAAY,CAAC;AACpB,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAG/D,MAAM,WAAW,sBAAsB;IACrC;;iEAE6D;IAC7D,UAAU,EAAE,kBAAkB,CAAC;IAC/B;8CAC0C;IAC1C,SAAS,EAAE,MAAM,CAAC;IAClB,yEAAyE;IACzE,OAAO,EAAE,MAAM,CAAC;IAChB;iDAC6C;IAC7C,WAAW,EAAE,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACzE;;kCAE8B;IAC9B,gBAAgB,EAAE,IAAI,CAAC,gBAAgB,EAAE,sBAAsB,CAAC,CAAC;CAClE;AAED;;;;;;;;;;;;;;GAcG;AACH,qBAAa,eAAe;IAC1B,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAqB;IAChD,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAA8D;IAC1F,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAiD;IAElF,OAAO,CAAC,aAAa,CAAsB;IAC3C;uEACmE;IACnE,OAAO,CAAC,WAAW,CAAK;gBAEZ,OAAO,EAAE,sBAAsB;IAQ3C;+EAC2E;IAC3E,WAAW,IAAI,SAAS,YAAY,EAAE;IAItC;;;;OAIG;IACH,OAAO,IAAI,YAAY,EAAE;IAQzB;iEAC6D;IAC7D,kBAAkB,IAAI,IAAI;IAO1B;;;;;OAKG;IACG,gBAAgB,CAAC,KAAK,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC;IA8FxD;;;;;;;OAOG;IACG,qBAAqB,CAAC,MAAM,EAAE,WAAW,GAAG,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAsCzE;;;;;OAKG;IACG,mBAAmB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAqBzD;;;;OAIG;YACW,sBAAsB;CAyBrC"}
|
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
import { decodePasteBytes } from "@opentui/core";
|
|
2
|
+
import { describeMacClipboardTypes, loadImageFromPath, looksLikeImageFilePath, persistPastedImage, sniffImageMimeType, tryReadClipboardImage, tryReadClipboardText, } from "./paste.js";
|
|
3
|
+
import { COLORS } from "./theme.js";
|
|
4
|
+
/**
|
|
5
|
+
* Owns the pending-image queue surfaced into the prompt as `[Image #N]`
|
|
6
|
+
* placeholders. Handles three intake paths that converge on the same
|
|
7
|
+
* `persistPastedImage` / `loadImageFromPath` machinery:
|
|
8
|
+
*
|
|
9
|
+
* 1. Bracketed-paste events that carry binary image bytes (kitty, ghostty,
|
|
10
|
+
* recent iTerm2).
|
|
11
|
+
* 2. Text-shaped pastes that resolve to a single existing image file path
|
|
12
|
+
* (Finder/Files drag-paste, file:// URLs).
|
|
13
|
+
* 3. Manual probes triggered by Cmd+V/Ctrl+V or the `/paste` slash command
|
|
14
|
+
* against terminals that strip binary clipboards.
|
|
15
|
+
*
|
|
16
|
+
* The queue resets after every submit (callers drive that via `consume()`),
|
|
17
|
+
* so users see a fresh `#1` label per turn.
|
|
18
|
+
*/
|
|
19
|
+
export class PasteController {
|
|
20
|
+
inputField;
|
|
21
|
+
sessionId;
|
|
22
|
+
workDir;
|
|
23
|
+
appendBlock;
|
|
24
|
+
statusController;
|
|
25
|
+
pendingImages = [];
|
|
26
|
+
/** Monotonic id used to label the next attachment. Reset together with
|
|
27
|
+
* `pendingImages` so labels restart at `#1` after each submit. */
|
|
28
|
+
nextImageId = 1;
|
|
29
|
+
constructor(options) {
|
|
30
|
+
this.inputField = options.inputField;
|
|
31
|
+
this.sessionId = options.sessionId;
|
|
32
|
+
this.workDir = options.workDir;
|
|
33
|
+
this.appendBlock = options.appendBlock;
|
|
34
|
+
this.statusController = options.statusController;
|
|
35
|
+
}
|
|
36
|
+
/** Read-only view of the currently queued attachments. Useful for callers
|
|
37
|
+
* that need to render attachment footnotes alongside the user message. */
|
|
38
|
+
attachments() {
|
|
39
|
+
return this.pendingImages;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Drain the queue, returning the attachments that were pending at submit
|
|
43
|
+
* time. The caller forwards `.attachment` payloads to the runner; the
|
|
44
|
+
* controller resets so the next turn starts at `[Image #1]`.
|
|
45
|
+
*/
|
|
46
|
+
consume() {
|
|
47
|
+
const drained = this.pendingImages;
|
|
48
|
+
this.pendingImages = [];
|
|
49
|
+
this.nextImageId = 1;
|
|
50
|
+
this.statusController.setPendingImageCount(0);
|
|
51
|
+
return drained;
|
|
52
|
+
}
|
|
53
|
+
/** Drop all queued attachments. No-op when the queue is already empty so
|
|
54
|
+
* `/clear-images` does not redundantly repaint the hint. */
|
|
55
|
+
clearPendingImages() {
|
|
56
|
+
if (this.pendingImages.length === 0)
|
|
57
|
+
return;
|
|
58
|
+
this.pendingImages = [];
|
|
59
|
+
this.nextImageId = 1;
|
|
60
|
+
this.statusController.setPendingImageCount(0);
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Handle a bracketed-paste event from the textarea. Decides synchronously
|
|
64
|
+
* whether the payload is image bytes, a file path, an empty trigger that
|
|
65
|
+
* warrants a clipboard probe, or plain text that should fall through to
|
|
66
|
+
* the textarea's default insert path.
|
|
67
|
+
*/
|
|
68
|
+
async handlePasteEvent(event) {
|
|
69
|
+
const metadata = event.metadata;
|
|
70
|
+
const sniffed = sniffImageMimeType(event.bytes);
|
|
71
|
+
const inferredMime = metadata?.mimeType && metadata.mimeType.startsWith("image/") ? metadata.mimeType : sniffed;
|
|
72
|
+
// Synchronous fast paths — the paste payload itself is enough to decide.
|
|
73
|
+
if (inferredMime) {
|
|
74
|
+
event.preventDefault();
|
|
75
|
+
await this.attachPastedImageBytes(event.bytes, inferredMime);
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
if (metadata?.kind === "binary") {
|
|
79
|
+
// Non-image binary paste — we cannot meaningfully forward it, but the
|
|
80
|
+
// terminal already swallowed the keystroke, so suppress the default
|
|
81
|
+
// text-insert path that would otherwise garble the prompt.
|
|
82
|
+
event.preventDefault();
|
|
83
|
+
this.appendBlock("[paste]", "Unsupported binary clipboard contents (only PNG/JPEG/GIF/WebP).", COLORS.system);
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
// Text-shaped paste. Three sub-cases, ordered cheapest first so common
|
|
87
|
+
// text pastes never wait on the macOS Swift clipboard probe:
|
|
88
|
+
//
|
|
89
|
+
// 1. The text resolves to an image file path (Finder/Files drag-paste).
|
|
90
|
+
// 2. The terminal forwarded an empty payload but the OS clipboard
|
|
91
|
+
// may carry an image promise (e.g. Figma "Copy as PNG", screenshot,
|
|
92
|
+
// browser image copy that bracketed-paste cannot represent).
|
|
93
|
+
// 3. Plain text — just insert it.
|
|
94
|
+
//
|
|
95
|
+
// Sub-cases 1 and 3 are fully synchronous after the path heuristic, so
|
|
96
|
+
// the buffer paints immediately. Only sub-case 2 spawns the Swift
|
|
97
|
+
// probe, and only when there is literally no text to insert anyway.
|
|
98
|
+
const originalText = decodePasteBytes(event.bytes);
|
|
99
|
+
const candidate = looksLikeImageFilePath(originalText);
|
|
100
|
+
if (candidate) {
|
|
101
|
+
// Path-shaped paste: suppress the default insert so we can swap in
|
|
102
|
+
// the [Image #N] placeholder once load resolves.
|
|
103
|
+
event.preventDefault();
|
|
104
|
+
try {
|
|
105
|
+
const pending = await loadImageFromPath({
|
|
106
|
+
cwd: this.workDir,
|
|
107
|
+
rawPath: candidate,
|
|
108
|
+
id: this.nextImageId,
|
|
109
|
+
});
|
|
110
|
+
this.nextImageId += 1;
|
|
111
|
+
this.pendingImages.push(pending);
|
|
112
|
+
this.inputField.insertText(pending.label);
|
|
113
|
+
this.appendBlock("[paste]", `attached ${pending.label} from ${pending.path}`, COLORS.system);
|
|
114
|
+
this.statusController.setPendingImageCount(this.pendingImages.length);
|
|
115
|
+
}
|
|
116
|
+
catch (error) {
|
|
117
|
+
// The clipboard looked like an image path but we could not load
|
|
118
|
+
// it — surface why and restore the original text so the user can
|
|
119
|
+
// edit it manually instead of losing what they pasted.
|
|
120
|
+
this.appendBlock("[paste]", `looked like an image path but could not attach ${candidate}: ${error instanceof Error ? error.message : String(error)}`, COLORS.system);
|
|
121
|
+
if (originalText.length > 0)
|
|
122
|
+
this.inputField.insertText(originalText);
|
|
123
|
+
}
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
if (originalText.length === 0) {
|
|
127
|
+
// No text payload — the terminal had nothing to forward but the OS
|
|
128
|
+
// clipboard may still carry an image promise. Suppress the default
|
|
129
|
+
// (which would do nothing anyway) and run the slow probe.
|
|
130
|
+
event.preventDefault();
|
|
131
|
+
const clipboardImage = await tryReadClipboardImage();
|
|
132
|
+
if (clipboardImage) {
|
|
133
|
+
await this.attachPastedImageBytes(clipboardImage.bytes, clipboardImage.mimeType);
|
|
134
|
+
}
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
// Plain text paste — fall through to the InputRenderable's default
|
|
138
|
+
// insert path, which paints synchronously. Users whose intended image
|
|
139
|
+
// arrived as text-shaped bytes (e.g. Figma) can still trigger an
|
|
140
|
+
// explicit clipboard probe via the `/paste` slash command.
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Manual clipboard probe. Read the OS clipboard for an image right now and
|
|
144
|
+
* attach it if found; otherwise fall back to a text probe so a Cmd+V/Ctrl+V
|
|
145
|
+
* keystroke that ate a legitimate text paste does not silently swallow it.
|
|
146
|
+
* The `source` distinguishes the keystroke entry point from the `/paste`
|
|
147
|
+
* slash command so we only emit the "no readable image" diagnostic for
|
|
148
|
+
* explicit user intent.
|
|
149
|
+
*/
|
|
150
|
+
async triggerClipboardProbe(source) {
|
|
151
|
+
try {
|
|
152
|
+
const clipboardImage = await tryReadClipboardImage();
|
|
153
|
+
if (clipboardImage) {
|
|
154
|
+
await this.attachPastedImageBytes(clipboardImage.bytes, clipboardImage.mimeType);
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
// No image on the clipboard. The keystroke path may have eaten a
|
|
158
|
+
// legitimate text paste, so fall back to a text probe so users do not
|
|
159
|
+
// lose what they were trying to paste.
|
|
160
|
+
const text = await tryReadClipboardText();
|
|
161
|
+
if (text) {
|
|
162
|
+
this.inputField.insertText(text);
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
if (source === "slash") {
|
|
166
|
+
// Surface the actual clipboard UTI list when a /paste probe comes
|
|
167
|
+
// up empty — lets users see what their source app actually put
|
|
168
|
+
// there so the failure stops being mysterious.
|
|
169
|
+
const types = await describeMacClipboardTypes();
|
|
170
|
+
const detail = types
|
|
171
|
+
? ` — clipboard types: ${types}`
|
|
172
|
+
: " — (could not query clipboard types; clipboard may be empty)";
|
|
173
|
+
this.appendBlock("[paste]", `clipboard had no readable image or text${detail}`, COLORS.system);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
catch (error) {
|
|
177
|
+
this.appendBlock("[paste]", `clipboard probe failed: ${error instanceof Error ? error.message : String(error)}`, COLORS.error);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* Attach an image from disk by path. Drives the `/image <path>` slash
|
|
182
|
+
* command. Inserts the `[Image #N]` placeholder back into the (likely now
|
|
183
|
+
* empty) input so the user can keep typing their prompt with the image
|
|
184
|
+
* already attached.
|
|
185
|
+
*/
|
|
186
|
+
async attachImageFromPath(rawPath) {
|
|
187
|
+
try {
|
|
188
|
+
const pending = await loadImageFromPath({
|
|
189
|
+
cwd: this.workDir,
|
|
190
|
+
rawPath,
|
|
191
|
+
id: this.nextImageId,
|
|
192
|
+
});
|
|
193
|
+
this.nextImageId += 1;
|
|
194
|
+
this.pendingImages.push(pending);
|
|
195
|
+
this.inputField.insertText(pending.label);
|
|
196
|
+
this.appendBlock("[paste]", `attached ${pending.label} from ${pending.path}`, COLORS.system);
|
|
197
|
+
this.statusController.setPendingImageCount(this.pendingImages.length);
|
|
198
|
+
}
|
|
199
|
+
catch (error) {
|
|
200
|
+
this.appendBlock("[paste]", error instanceof Error ? error.message : String(error), COLORS.error);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
/**
|
|
204
|
+
* Persist raw image bytes (from a bracketed-paste event or clipboard probe)
|
|
205
|
+
* under the session cache and surface the resulting `[Image #N]`
|
|
206
|
+
* placeholder in the prompt buffer.
|
|
207
|
+
*/
|
|
208
|
+
async attachPastedImageBytes(bytes, mimeType) {
|
|
209
|
+
try {
|
|
210
|
+
const pending = await persistPastedImage({
|
|
211
|
+
sessionId: this.sessionId,
|
|
212
|
+
id: this.nextImageId,
|
|
213
|
+
bytes,
|
|
214
|
+
mimeType,
|
|
215
|
+
});
|
|
216
|
+
this.nextImageId += 1;
|
|
217
|
+
this.pendingImages.push(pending);
|
|
218
|
+
this.inputField.insertText(pending.label);
|
|
219
|
+
this.appendBlock("[paste]", `attached ${pending.label} (${mimeType}, ${formatBytes(bytes.length)})`, COLORS.system);
|
|
220
|
+
this.statusController.setPendingImageCount(this.pendingImages.length);
|
|
221
|
+
}
|
|
222
|
+
catch (error) {
|
|
223
|
+
this.appendBlock("[paste]", error instanceof Error ? error.message : String(error), COLORS.error);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
function formatBytes(n) {
|
|
228
|
+
if (n < 1024)
|
|
229
|
+
return `${n} B`;
|
|
230
|
+
if (n < 1024 * 1024)
|
|
231
|
+
return `${(n / 1024).toFixed(1)} KB`;
|
|
232
|
+
return `${(n / (1024 * 1024)).toFixed(1)} MB`;
|
|
233
|
+
}
|
|
234
|
+
//# sourceMappingURL=paste-controller.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"paste-controller.js","sourceRoot":"","sources":["../../../src/tui/paste-controller.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { type BoxRenderable, type KeyEvent, type TextareaRenderable, type TextRenderable } from "@opentui/core";
|
|
2
|
+
import type { Session } from "../session/session.js";
|
|
3
|
+
import type { TranscriptEntryKind } from "./transcript-log.js";
|
|
4
|
+
import type { TurnPromptImage, TurnQuestion } from "../types/protocol.js";
|
|
5
|
+
export interface QuestionPickerOptions {
|
|
6
|
+
/** Bordered panel that wraps the title + option rows; toggled visible
|
|
7
|
+
* alongside the row pool. */
|
|
8
|
+
questionPanel: BoxRenderable;
|
|
9
|
+
/** Single text row used as the panel header; rewritten on every render
|
|
10
|
+
* with the active question text and (i/N) progress. */
|
|
11
|
+
questionTitle: TextRenderable;
|
|
12
|
+
/** Fixed-size pool of selectable rows. The picker shows/hides entries
|
|
13
|
+
* from this pool rather than mounting fresh renderables per question
|
|
14
|
+
* so layout heights stay stable across questions. */
|
|
15
|
+
questionRows: TextRenderable[];
|
|
16
|
+
/** Composer textarea. Read for `plainText.length` so the picker can
|
|
17
|
+
* defer Space/Left/Right bindings to typing when the user is mid-prompt. */
|
|
18
|
+
inputField: TextareaRenderable;
|
|
19
|
+
/** Session that receives the assembled answer payload via `.answer(...)`
|
|
20
|
+
* once the user finishes the picker. */
|
|
21
|
+
session: Session;
|
|
22
|
+
/** Notify the host that the picker is about to swallow an Escape so the
|
|
23
|
+
* global escape-to-exit handler suppresses the next Escape it sees and
|
|
24
|
+
* doesn't double-handle the keystroke. */
|
|
25
|
+
onEscapeClose: () => void;
|
|
26
|
+
/** Append a bordered transcript block (label + body). Used for the
|
|
27
|
+
* `you:` echo when the user confirms an answer, mirroring how free-form
|
|
28
|
+
* prompts render. */
|
|
29
|
+
appendBlock: (label: string | null, body: string, color: string) => void;
|
|
30
|
+
/** Mirror committed answers into the in-memory transcript log so /copy
|
|
31
|
+
* and resume snapshots include them in order. */
|
|
32
|
+
recordTranscriptEntry: (kind: TranscriptEntryKind, text: string) => void;
|
|
33
|
+
/** Surface a dispatch failure (rejected `session.answer` promise) the
|
|
34
|
+
* same way the rest of runTui does. */
|
|
35
|
+
reportError: (error: unknown) => void;
|
|
36
|
+
/** Bridge to the status controller so the picker can flip the working
|
|
37
|
+
* indicator on when an answer kicks off a new turn. */
|
|
38
|
+
markRunning: () => void;
|
|
39
|
+
/** Asked before `markRunning()` so the picker mirrors the host's
|
|
40
|
+
* conditional "only mark when currently idle" behaviour. */
|
|
41
|
+
isRunning: () => boolean;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Owns the question picker: an ask terminal hands it a list of
|
|
45
|
+
* `TurnQuestion`s and the picker walks the user through them, accumulating
|
|
46
|
+
* selected option labels until the user either finishes the last question
|
|
47
|
+
* (dispatches an `answer`), types a free-form prompt over the top
|
|
48
|
+
* (`flushWithMessage`), or escapes out (host suppresses next escape so the
|
|
49
|
+
* TUI doesn't also exit).
|
|
50
|
+
*
|
|
51
|
+
* Single-select questions live-record the highlighted option as the answer
|
|
52
|
+
* so arrow navigation alone is enough; multi-select keeps the highlight
|
|
53
|
+
* separate from a checked-set toggled via Space/Enter and grows a synthetic
|
|
54
|
+
* "Done" row that advances to the next question.
|
|
55
|
+
*/
|
|
56
|
+
export declare class QuestionPicker {
|
|
57
|
+
private readonly opts;
|
|
58
|
+
private pendingQuestions;
|
|
59
|
+
private activeIndex;
|
|
60
|
+
private optionSelectedIndex;
|
|
61
|
+
private multiSelectChecked;
|
|
62
|
+
private accumulatedAnswers;
|
|
63
|
+
constructor(opts: QuestionPickerOptions);
|
|
64
|
+
isOpen(): boolean;
|
|
65
|
+
show(questions: TurnQuestion[]): void;
|
|
66
|
+
hide(): void;
|
|
67
|
+
/**
|
|
68
|
+
* Confirm whatever is currently highlighted. Multi-select on a regular row
|
|
69
|
+
* toggles; multi-select on the Done row advances. Single-select always
|
|
70
|
+
* advances (highlight = answer is already live-recorded by Up/Down). No-op
|
|
71
|
+
* when nothing is highlighted yet so the user is forced to make an
|
|
72
|
+
* explicit choice (or skip via Right-arrow).
|
|
73
|
+
*/
|
|
74
|
+
confirmSelection(): boolean;
|
|
75
|
+
/**
|
|
76
|
+
* Dispatch the partially-collected answers together with a free-form
|
|
77
|
+
* `message` (and any pending image attachments) as a single
|
|
78
|
+
* `session.answer({ ..., message, images })` turn, then hide the picker.
|
|
79
|
+
* Used when the user types over the top of an open picker — without this
|
|
80
|
+
* the partial selections would be discarded when the prompt fires.
|
|
81
|
+
*/
|
|
82
|
+
flushWithMessage(message: string, images: TurnPromptImage[]): void;
|
|
83
|
+
/**
|
|
84
|
+
* Handle a keystroke while the picker is open. Returns `true` if the key
|
|
85
|
+
* was claimed (and `key.preventDefault()` already called); the caller
|
|
86
|
+
* should stop further dispatch in that case. Returns `false` when the
|
|
87
|
+
* picker is closed or the key isn't one this controller handles.
|
|
88
|
+
*/
|
|
89
|
+
handleKey(key: KeyEvent): boolean;
|
|
90
|
+
private activeQuestion;
|
|
91
|
+
/**
|
|
92
|
+
* Total navigable rows for the active question. The Up/Down handler clamps
|
|
93
|
+
* navigation to what is actually rendered on screen so a user cannot land
|
|
94
|
+
* the highlight on a row they cannot see.
|
|
95
|
+
*/
|
|
96
|
+
private activeRowCount;
|
|
97
|
+
/**
|
|
98
|
+
* Row index of the synthetic Done row when the active question is
|
|
99
|
+
* multi-select; `undefined` otherwise so callers don't compare against a
|
|
100
|
+
* sentinel value. The Done row sits one past the last visible option,
|
|
101
|
+
* clamped to the same limit `render()` uses.
|
|
102
|
+
*/
|
|
103
|
+
private activeQuestionDoneIndex;
|
|
104
|
+
private render;
|
|
105
|
+
private moveActiveHighlight;
|
|
106
|
+
private toggleActiveMultiSelectOption;
|
|
107
|
+
private navigateActive;
|
|
108
|
+
private describeAnswerLabels;
|
|
109
|
+
private advanceOrSubmit;
|
|
110
|
+
}
|
|
111
|
+
//# sourceMappingURL=question-picker.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"question-picker.d.ts","sourceRoot":"","sources":["../../../src/tui/question-picker.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,aAAa,EAElB,KAAK,QAAQ,EAEb,KAAK,kBAAkB,EACvB,KAAK,cAAc,EACpB,MAAM,eAAe,CAAC;AASvB,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAC;AAErD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAC/D,OAAO,KAAK,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAI1E,MAAM,WAAW,qBAAqB;IACpC;kCAC8B;IAC9B,aAAa,EAAE,aAAa,CAAC;IAC7B;4DACwD;IACxD,aAAa,EAAE,cAAc,CAAC;IAC9B;;0DAEsD;IACtD,YAAY,EAAE,cAAc,EAAE,CAAC;IAC/B;iFAC6E;IAC7E,UAAU,EAAE,kBAAkB,CAAC;IAC/B;6CACyC;IACzC,OAAO,EAAE,OAAO,CAAC;IACjB;;+CAE2C;IAC3C,aAAa,EAAE,MAAM,IAAI,CAAC;IAC1B;;0BAEsB;IACtB,WAAW,EAAE,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACzE;sDACkD;IAClD,qBAAqB,EAAE,CAAC,IAAI,EAAE,mBAAmB,EAAE,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACzE;4CACwC;IACxC,WAAW,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;IACtC;4DACwD;IACxD,WAAW,EAAE,MAAM,IAAI,CAAC;IACxB;iEAC6D;IAC7D,SAAS,EAAE,MAAM,OAAO,CAAC;CAC1B;AAED;;;;;;;;;;;;GAYG;AACH,qBAAa,cAAc;IAiBb,OAAO,CAAC,QAAQ,CAAC,IAAI;IAhBjC,OAAO,CAAC,gBAAgB,CAAsB;IAC9C,OAAO,CAAC,WAAW,CAAK;IAKxB,OAAO,CAAC,mBAAmB,CAAgB;IAI3C,OAAO,CAAC,kBAAkB,CAAqB;IAI/C,OAAO,CAAC,kBAAkB,CAAgC;gBAE7B,IAAI,EAAE,qBAAqB;IAExD,MAAM,IAAI,OAAO;IAKjB,IAAI,CAAC,SAAS,EAAE,YAAY,EAAE,GAAG,IAAI;IASrC,IAAI,IAAI,IAAI;IAaZ;;;;;;OAMG;IACH,gBAAgB,IAAI,OAAO;IAW3B;;;;;;OAMG;IACH,gBAAgB,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,eAAe,EAAE,GAAG,IAAI;IAclE;;;;;OAKG;IACH,SAAS,CAAC,GAAG,EAAE,QAAQ,GAAG,OAAO;IA8CjC,OAAO,CAAC,cAAc;IAItB;;;;OAIG;IACH,OAAO,CAAC,cAAc;IAQtB;;;;;OAKG;IACH,OAAO,CAAC,uBAAuB;IAO/B,OAAO,CAAC,MAAM;IAyDd,OAAO,CAAC,mBAAmB;IAsB3B,OAAO,CAAC,6BAA6B;IAiBrC,OAAO,CAAC,cAAc;IAoBtB,OAAO,CAAC,oBAAoB;IAK5B,OAAO,CAAC,eAAe;CA8BxB"}
|