fix: do not cancel HTML or text paste when no image is in the event

Empty text/plain used to preventDefault before native clipboard read, which discarded HTML-only paste if no image was recovered.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-08-16 12:46:10 +08:00
parent 00209d55e6
commit d6a7a3ab08
3 changed files with 13 additions and 5 deletions

View File

@@ -15,7 +15,7 @@ DeepSeek, liustack, xiaobright, or AgentRQ.
| ModLens (`@liustack/modlens`) | [liustack/modlens](https://github.com/liustack/modlens) | `3.16.6` | MIT, © 2026 Leon Liu (liustack) | Copied into `~/.dsh/profiles/web` so text-only models can read images. See `docs/licenses/modlens.LICENSE`. | | ModLens (`@liustack/modlens`) | [liustack/modlens](https://github.com/liustack/modlens) | `3.16.6` | MIT, © 2026 Leon Liu (liustack) | Copied into `~/.dsh/profiles/web` so text-only models can read images. See `docs/licenses/modlens.LICENSE`. |
| Anchored Standard | [xiaobright/dsh-anchored-standard](https://github.com/xiaobright/dsh-anchored-standard) | commit `ffb845c5480adc953392a6db6f8a98ede621174b` | MIT, © 2026 xiaobright; portions © 2026 DeepSeek | Localized as **锚定式标准(实验)** and **零工具锚定式标准(实验)**, written to `~/.dsh/.agent-presets/`. See `docs/licenses/dsh-anchored-standard.LICENSE` and `.NOTICE`. | | Anchored Standard | [xiaobright/dsh-anchored-standard](https://github.com/xiaobright/dsh-anchored-standard) | commit `ffb845c5480adc953392a6db6f8a98ede621174b` | MIT, © 2026 xiaobright; portions © 2026 DeepSeek | Localized as **锚定式标准(实验)** and **零工具锚定式标准(实验)**, written to `~/.dsh/.agent-presets/`. See `docs/licenses/dsh-anchored-standard.LICENSE` and `.NOTICE`. |
| `dsh-desktop-vision` | this repo `plugins/dsh-desktop-vision/` | `0.1.4` | MIT, © 2026 TommyFang2077 | Settings page **设置 → 视觉模型**; writes `~/.modlens/config.json`. | | `dsh-desktop-vision` | this repo `plugins/dsh-desktop-vision/` | `0.1.4` | MIT, © 2026 TommyFang2077 | Settings page **设置 → 视觉模型**; writes `~/.modlens/config.json`. |
| AgentRQ (`agentrq`) | [agentrq/agentrq](https://github.com/agentrq/agentrq) | `0.2.1` | Apache-2.0 | Copied into `~/.dsh/profiles/web` so Harness can manage AgentRQ tasks. Idle until `AGENTRQ_WORKSPACE_MCP_URL` or the profile `url` is set. See `docs/licenses/agentrq.LICENSE`. | | AgentRQ (`agentrq`) | [agentrq/agentrq](https://github.com/agentrq/agentrq) | `0.2.1` | Apache-2.0, © AgentRQ authors | Copied into `~/.dsh/profiles/web` so Harness can manage AgentRQ tasks. Idle until `AGENTRQ_WORKSPACE_MCP_URL` or the profile `url` is set. See `docs/licenses/agentrq.LICENSE`. |
The Anchored Standard NOTICE records that the presets adapt the DeepSeek The Anchored Standard NOTICE records that the presets adapt the DeepSeek
Harness Standard agent preset from Harness Standard agent preset from

View File

@@ -46,10 +46,13 @@ class ClipboardIngestTests(unittest.TestCase):
self.assertIn("navigator.clipboard.read", chrome) self.assertIn("navigator.clipboard.read", chrome)
self.assertIn("read_clipboard_images", chrome) self.assertIn("read_clipboard_images", chrome)
self.assertIn("__dshDesktopIngesting", chrome) self.assertIn("__dshDesktopIngesting", chrome)
self.assertIn('clipboardText(e, "text/html")', chrome)
self.assertNotIn( self.assertNotIn(
'item.kind === "file" && item.getAsFile()', 'item.kind === "file" && item.getAsFile()',
chrome, chrome,
) )
self.assertNotIn("steal", chrome)
self.assertNotIn("|| !String(text).trim()", chrome)
class BundledAttributionTests(unittest.TestCase): class BundledAttributionTests(unittest.TestCase):

View File

@@ -186,10 +186,15 @@
const local = fromEvent ? fromEvent(e) : []; const local = fromEvent ? fromEvent(e) : [];
const text = clipboardText(e, "text/plain"); const text = clipboardText(e, "text/plain");
const uris = clipboardText(e, "text/uri-list"); const uris = clipboardText(e, "text/uri-list");
const steal = local.length > 0 const html = clipboardText(e, "text/html");
|| (pathLike && (pathLike(text) || pathLike(uris))) const imagePath = Boolean(pathLike && (pathLike(text) || pathLike(uris)));
|| !String(text).trim(); const hasText = Boolean(String(text).trim() || String(html).trim());
if (!steal) return; // Image files in the event, or a pasted image path, are ours. Empty
// payload is the Linux case where the image is only on the native
// clipboard. Non-empty text/html must reach the page — do not cancel
// first and then fail to recover an image.
if (!local.length && !imagePath && hasText) return;
e.preventDefault(); e.preventDefault();
e.stopImmediatePropagation(); e.stopImmediatePropagation();
window.__dshDesktopIngesting = true; window.__dshDesktopIngesting = true;