|
1 | | -#pragma once |
2 | | -// Read an image from the system clipboard via platform-native tooling. |
3 | | -// |
4 | | -// Bracketed paste delivers UTF-8 text only; binary clipboard content |
5 | | -// (a PNG that a screenshot tool put on the clipboard) is dropped or |
6 | | -// mangled by every mainstream terminal. The reliable path is to ASK |
7 | | -// the platform clipboard for its image content out-of-band, via a |
8 | | -// small subprocess: |
9 | | -// |
10 | | -// Linux/Wayland → wl-paste --type image/png |
11 | | -// Linux/X11 → xclip -selection clipboard -t image/png -o |
12 | | -// macOS → pngpaste - (brew install pngpaste) |
13 | | -// Windows → powershell System.Windows.Forms.Clipboard.GetImage |
14 | | -// |
15 | | -// All capture stdout as raw bytes — `tools::util::Subprocess` runs its |
16 | | -// captured output through to_valid_utf8(), which would replace every |
17 | | -// non-UTF-8 byte with U+FFFD and corrupt the image. So this module |
18 | | -// has its own popen-based capture that doesn't touch the bytes. |
19 | | -// |
20 | | -// The reducer arm in update/composer.cpp calls read_clipboard_image() |
21 | | -// synchronously on Ctrl+V. The subprocesses all exit immediately when |
22 | | -// no image is available (non-zero status, empty stdout) so the worst |
23 | | -// case is the shell-spawn cost (~10-50 ms on Linux, ~100-200 ms on |
24 | | -// macOS via osascript). Acceptable on the UI thread for an explicit |
25 | | -// user action. |
26 | | - |
27 | | -#include <optional> |
28 | | -#include <string> |
29 | | - |
30 | | -namespace agentty { |
31 | | - |
32 | | -struct ClipboardImage { |
33 | | - /// Raw image bytes — PNG / JPEG / GIF / WEBP. Sniffed from the |
34 | | - /// magic prefix of the captured stdout, so format mismatch |
35 | | - /// between tool flags and actual content (rare, but possible |
36 | | - /// under odd compositor configurations) is surfaced cleanly. |
37 | | - std::string bytes; |
38 | | - /// MIME type (e.g. "image/png") matching the magic prefix. |
39 | | - std::string media_type; |
40 | | -}; |
41 | | - |
42 | | -/// Read an image from the system clipboard. On success returns the |
43 | | -/// raw bytes + sniffed MIME type. On failure (tool missing, clipboard |
44 | | -/// empty, no image type on clipboard, image too large), returns |
45 | | -/// nullopt and writes a one-line human-readable diagnostic to |
46 | | -/// `*error_out` so the caller can surface it as a status toast that |
47 | | -/// names the actual failure (the previous "no image on clipboard" |
48 | | -/// blanket message was unhelpful when the real fix was "install |
49 | | -/// wl-clipboard" or "the clipboard has only text"). |
50 | | -[[nodiscard]] std::optional<ClipboardImage> |
51 | | -read_clipboard_image(std::string* error_out = nullptr); |
52 | | - |
53 | | -/// Read plain UTF-8 text from the system clipboard. Used by the |
54 | | -/// composer's "smart paste" path: when Ctrl+V / Ctrl+Shift+V arrive |
55 | | -/// via a trigger that didn't already carry bracketed-paste content |
56 | | -/// (Windows Terminal swallows Ctrl+V and emits nothing on an |
57 | | -/// image-only clipboard; the user's Alt+V fallback also goes through |
58 | | -/// the same path), the composer asks the OS clipboard for whatever |
59 | | -/// it has — image first, text second — so the same shortcut "just |
60 | | -/// works" regardless of clipboard contents. |
61 | | -/// |
62 | | -/// Returns nullopt if the clipboard has no text or the platform tool |
63 | | -/// is missing; writes a diagnostic to `*error_out` for the toast |
64 | | -/// path. |
| 1 | +#pragma once |
| 2 | +// Read an image from the system clipboard via platform-native tooling. |
| 3 | +// |
| 4 | +// Bracketed paste delivers UTF-8 text only; binary clipboard content |
| 5 | +// (a PNG that a screenshot tool put on the clipboard) is dropped or |
| 6 | +// mangled by every mainstream terminal. The reliable path is to ASK |
| 7 | +// the platform clipboard for its image content out-of-band, via a |
| 8 | +// small subprocess: |
| 9 | +// |
| 10 | +// Linux/Wayland → wl-paste --type image/png |
| 11 | +// Linux/X11 → xclip -selection clipboard -t image/png -o |
| 12 | +// macOS → pngpaste - (brew install pngpaste) |
| 13 | +// Windows → powershell System.Windows.Forms.Clipboard.GetImage |
| 14 | +// |
| 15 | +// All capture stdout as raw bytes — `tools::util::Subprocess` runs its |
| 16 | +// captured output through to_valid_utf8(), which would replace every |
| 17 | +// non-UTF-8 byte with U+FFFD and corrupt the image. So this module |
| 18 | +// has its own popen-based capture that doesn't touch the bytes. |
| 19 | +// |
| 20 | +// The reducer arm in update/composer.cpp calls read_clipboard_image() |
| 21 | +// synchronously on Ctrl+V. The subprocesses all exit immediately when |
| 22 | +// no image is available (non-zero status, empty stdout) so the worst |
| 23 | +// case is the shell-spawn cost (~10-50 ms on Linux, ~100-200 ms on |
| 24 | +// macOS via osascript). Acceptable on the UI thread for an explicit |
| 25 | +// user action. |
| 26 | + |
| 27 | +#include <optional> |
| 28 | +#include <string> |
| 29 | + |
| 30 | +namespace agentty { |
| 31 | + |
| 32 | +struct ClipboardImage { |
| 33 | + /// Raw image bytes — PNG / JPEG / GIF / WEBP. Sniffed from the |
| 34 | + /// magic prefix of the captured stdout, so format mismatch |
| 35 | + /// between tool flags and actual content (rare, but possible |
| 36 | + /// under odd compositor configurations) is surfaced cleanly. |
| 37 | + std::string bytes; |
| 38 | + /// MIME type (e.g. "image/png") matching the magic prefix. |
| 39 | + std::string media_type; |
| 40 | +}; |
| 41 | + |
| 42 | +/// Read an image from the system clipboard. On success returns the |
| 43 | +/// raw bytes + sniffed MIME type. On failure (tool missing, clipboard |
| 44 | +/// empty, no image type on clipboard, image too large), returns |
| 45 | +/// nullopt and writes a one-line human-readable diagnostic to |
| 46 | +/// `*error_out` so the caller can surface it as a status toast that |
| 47 | +/// names the actual failure (the previous "no image on clipboard" |
| 48 | +/// blanket message was unhelpful when the real fix was "install |
| 49 | +/// wl-clipboard" or "the clipboard has only text"). |
| 50 | +[[nodiscard]] std::optional<ClipboardImage> |
| 51 | +read_clipboard_image(std::string* error_out = nullptr); |
| 52 | + |
| 53 | +/// Read plain UTF-8 text from the system clipboard. Used by the |
| 54 | +/// composer's "smart paste" path: when Ctrl+V / Ctrl+Shift+V arrive |
| 55 | +/// via a trigger that didn't already carry bracketed-paste content |
| 56 | +/// (Windows Terminal swallows Ctrl+V and emits nothing on an |
| 57 | +/// image-only clipboard; the user's Alt+V fallback also goes through |
| 58 | +/// the same path), the composer asks the OS clipboard for whatever |
| 59 | +/// it has — image first, text second — so the same shortcut "just |
| 60 | +/// works" regardless of clipboard contents. |
| 61 | +/// |
| 62 | +/// Returns nullopt if the clipboard has no text or the platform tool |
| 63 | +/// is missing; writes a diagnostic to `*error_out` for the toast |
| 64 | +/// path. |
65 | 65 | [[nodiscard]] std::optional<std::string> |
66 | 66 | read_clipboard_text(std::string* error_out = nullptr); |
67 | 67 |
|
|
0 commit comments