Skip to content

Add a bounded async Clipboard API host ABI for native runtimes #86

Description

@wieslawsoltes

AppScene coordination epic: SceneTech/AppScene#66

Problem

The native V8 runtime currently exposes ClipboardItem and navigator.clipboard.write(items), but writes are fire-and-forget JSON host requests and clipboard reads are absent. Code OSS's browser clipboard service calls writeText, readText, write, and read; copy can fall through to an unsupported legacy execCommand, and paste/image operations cannot reach the operating-system clipboard.

Proposed contract

  • Add a versioned native clipboard request/completion ABI with request ID, operation, MIME type, immutable bytes, and explicit completed/cancelled/failed status.
  • Implement Promise-based navigator.clipboard.writeText, readText, write, and read, plus ClipboardItem, on top of that ABI.
  • Dispatch bubbling, cancelable browser copy, cut, and paste events for non-prevented native Ctrl/Command-C/X/V shortcuts. Expose bounded clipboardData text/plain and text/html representations and preserve the initiating target across async paste reads.
  • Preserve the existing host-request write path only as a compatibility fallback for older hosts; report its partial capability accurately.
  • Support text/plain, text/html, and bounded image bytes initially. Reject unsupported MIME types explicitly.
  • Require a trusted top-level document and recent user activation for reads. Bind requests to the originating realm/navigation generation and cancel them on navigation or engine teardown.
  • Bound pending requests, item count, per-item bytes, and total bytes. Do not serialize binary payloads through base64 JSON in the new ABI.

Acceptance criteria

  • Web IDL/API shape, brand checks, Promise behavior, MIME handling, and DOMException names match the supported subset.
  • Native Ctrl/Command-C/X/V dispatches browser-compatible clipboard events; exact text/HTML host bytes, paste target lifetime, event cancellation, and input-queue ordering are covered.
  • Native contracts cover text/typed success, cancellation, host failure, unsupported MIME, invalid receiver/input, queue saturation, stale/double completion, navigation, and teardown.
  • A 10,000-operation small-text round-trip gate and maximum-payload gate publish elapsed time and retained-memory/request-pool high-water metrics; no timing sleeps determine correctness.
  • Existing Avalonia/Uno hosts either implement the ABI or expose an explicit unsupported result without regression.
  • Hosted and packaged Windows, macOS, and Linux jobs plus cross-RID/consumer checks pass.
  • The Code OSS package passes real native copy/paste text and image qualification through AppScene with no VS Code source change.

This belongs in WebScene because it is a standard browser API and shared native host ABI, independent of Electron and of any one application.

Activity

  1. wieslawsoltes commented on Sep 15, 2026

    @wieslawsoltes
    CollaboratorAuthor

    First implementation commit: 378dd29d on consolidated draft PR #76.

    This adds Promise-based readText, read, writeText, and write; explicit completed/cancelled/failed host completion; 16 pending-operation and 16 MiB limits; supported MIME checks; navigation cancellation; stale completion handling; and focused success/cancel/invalid contract tests. Writes now settle only after the native host completes them.

    This commit deliberately retains the bounded base64 JSON request envelope for compatibility with the current host callback while adding the completion ABI. The issue remains open for the typed immutable-byte request ABI that removes that encoding/copy cost, user-activation qualification, maximum-payload/high-water measurements across all hosted platforms, and real Code OSS text/image clipboard interaction.

  2. wieslawsoltes commented on Sep 15, 2026

    @wieslawsoltes
    CollaboratorAuthor

    Typed request follow-up is now committed as 33094be3 on draft PR #76.

    The new webscene_host_request_v1 lease carries request kind/ID, flags, target node, MIME, immutable bytes, and URL directly. Clipboard data no longer expands through base64 JSON. Standard clipboard, external URL, focus, close, reload, and fullscreen operations now use this queue; the JSON queue remains for older/application-defined messages. Multi-representation clipboard writes mark the first representation as replacement and append later representations without clearing the native clipboard.

    Additional gates cover a 16 MiB clipboard round trip, 10,000 typed window requests, missing user activation, the 16-operation clipboard limit, duplicate/stale completion, and native-initiated fullscreen/focus transitions. Full repository CI and packaged Code OSS interaction are running/remaining, so the issue stays open.

  3. wieslawsoltes commented on Sep 15, 2026

    @wieslawsoltes
    CollaboratorAuthor

    Implemented at da6ee30492294b94541ea5137d741916e53e4a2f in draft PR #76. Packaged Linux x64, macOS arm64, and Windows x64 contracts pass, including browser-compatible Ctrl/Command-C/X/V events, exact text/HTML writes, async paste dispatch to the initiating target, queue-frontier synchronization, limits, cancellation, and stale completion. The combined app smoke passes. AppScene #69 tracks the remaining real OS clipboard computer-use gate because synthetic pointer input could not focus Monaco.

  4. wieslawsoltes commented on Sep 15, 2026

    @wieslawsoltes
    CollaboratorAuthor

    Final compatibility follow-up is implemented at b639b4c49985891c2521421229a2f22e976ead2c on draft PR #76. During native user-input dispatch, Document.execCommand(copy/cut/paste) now routes through the existing clipboard event helper and typed host ABI; nested and unsupported commands return false. A real packaged Code OSS/Monaco run copied scenetechmanualinputok byte-exactly to the macOS clipboard, accepted all 569 bounded AppScene input records, and closed normally. The full Release editor smoke passed in 8.041 seconds with executable SHA-256 678b9106885e42ad37fe1db03d22cf32d1f2027387ab262899950138acec6e99.

    The combined acceptance criterion stays open. Paste copyback was inconclusive on the shared active desktop, and a later exact Command-C run crashed a stripped WebScene worker after all 368 input records were accepted. #89 tracks symbols, deterministic cross-thread completion stress, and the repeated packaged gate. The final three-RID package/consumer/release-evidence matrix is green; the hosted macOS CI rerun is still required before the branch is mergeable.

  5. wieslawsoltes commented on Sep 15, 2026

    @wieslawsoltes
    CollaboratorAuthor

    The rebuilt Code OSS/AppScene consumer exposed the remaining paste acceptance gap at WebScene 31e3d8ad.

    The bounded macOS computer-use gate:

    • typed and confirmed scenetechmanualinputok in real Monaco;
    • selected and copied it byte-exactly to the OS clipboard;
    • set scenetechmanualpasteok, then delivered Command-A / Command-V with Meta modifier flag 8 to the key window and first responder;
    • caused AppScene to accept a typed clipboard-read request and complete it from NSPasteboard;
    • selected and copied the resulting editor contents, but WebScene/AppScene produced a one-byte clipboard write rather than the full paste marker;
    • accepted all 88 bounded input records and closed normally with exit 0.

    This is now a product-level failing acceptance test rather than the earlier uncertain automation state: the native Command-V and typed read bridge are both observed. The generic WebScene paste path currently creates a JavaScript Event('paste') after the async read and dispatches it to the initiating target. The next diagnosis should determine whether Code OSS ignores that event because it lacks browser ClipboardEvent/trusted/default-action semantics, whether target/selection state changes before async completion, or whether the paste payload is truncated before the Monaco handler consumes it.

    Proposed quality gate: add a native contenteditable/Monaco-shaped consumer that asserts Command-V changes the editable model to the exact host-completed UTF-8 marker, then copies it back byte-for-byte. Cover async selection changes, detached targets, cancellation, and one maximum-payload case without timing sleeps. Keep the fix in draft PR #76 and preserve the existing bounded typed ABI.

  6. wieslawsoltes commented on Sep 15, 2026

    @wieslawsoltes
    CollaboratorAuthor

    Root cause isolated from the current Code OSS release smoke:

    • AppScene accepted the native clipboard read and returned the expected text.
    • WebScene populated clipboardData.types and getData("text/plain"), but exposed clipboardData.items as an empty array.
    • Code OSS src/vs/editor/browser/dataTransfer.ts converts only DataTransfer.items, so the default paste handler received an empty VSDataTransfer and inserted an empty string.

    Fix 9b9a17d adds a live, iterable string DataTransferItemList subset with kind, type, asynchronous getAsString(), getAsFile(), length, and item(). The native V8 test now exercises the same promise-based item consumption used by Code OSS and verifies asynchronous callback behavior.

    Local gate: WebScene.Architecture.Tests 26/26 passed. Cross-platform native/package/installed-consumer CI is running on the draft branch. No VS Code OSS source change is required.

  7. wieslawsoltes commented on Sep 15, 2026

    @wieslawsoltes
    CollaboratorAuthor

    Text paste is now qualified at 9b9a17d.

    The defect was DataTransfer shape rather than AppScene OS dispatch: WebScene exposed text/plain through types/getData but left items empty, while Code OSS builds its internal transfer only from DataTransfer.items. The fix adds an iterable string-item list with asynchronous getAsString() and a native V8 regression matching Code OSS's promise-based consumption.

    Validation:

    • Local architecture suite: 26/26.
    • GitHub: hosted macOS/Ubuntu/Windows, NativeAOT, Linux native contracts, all three runtime packages and installed consumers, required/candidate cross-RID evidence, and release-set verification all pass.
    • Release editor smoke: 4.029 s.
    • Three computer-use copy/paste/normal-close runs pass, including the separately signed distribution; each accepted all 88 input records and exited 0.
    • Packaged runtime UUID: A5D0DFD7-6BC8-3B61-A249-31319C6BE186.

    No VS Code source change was required. Keep this issue open for the remaining bounded image clipboard qualification in its acceptance criteria.

  8. wieslawsoltes commented on Sep 15, 2026

    @wieslawsoltes
    CollaboratorAuthor

    Implemented at 0f3a320e12c8cc716c8818e8ef5654054477df31 and pushed to draft PR #76.

    The native paste path now requests all supported representations and exposes returned images as browser-shaped file items:

    • DataTransferItem.kind === "file"
    • exact host MIME type
    • getAsFile() returns a WebScene File
    • DataTransfer.files contains the file
    • DataTransfer.types contains Files
    • File.arrayBuffer() preserves the host bytes
    • unsupported returned MIME types reject with NotSupportedError
    • readText() rejects a non-text result with DataError

    Local WebScene gates passed:

    • full native V8 runtime suite
    • RuntimeCompatibility: 25/25
    • Architecture: 29/29
    • exact 68-byte PNG signature/file metadata regression
    • maximum 16 MiB image paste: 0.157494 s (5 s budget)
    • typed maximum 16 MiB clipboard: 0.108963 s
    • typed clipboard 10,000 operations: 0.0367825 s
    • legacy clipboard 10,000 operations: 1.90916 s

    The production Code OSS 1.137.0 package built from this runtime also passed macOS computer use through the real unchanged workbench. NSPasteboard -> AppScene -> typed WebScene ABI -> paste delivered:

    {"status":"passed","kind":"file","type":"image/png","name":"clipboard.png","size":68,"byteLength":68,"first":137,"last":130,"fnv1a32":1588682394,"itemCount":1,"fileCount":1,"types":["Files"]}

    The same signed bundle passed editor render/edit/undo in 7.077 s, text keyboard/copy/paste in 11.308 s with 88/88 inputs accepted, image paste in 6.452 s, normal close, the 27-Mach-O dependency/signature audit, ZIP extraction verification, and DMG verification.

    Issue acceptance remains open until the new exact-head cross-platform CI matrix is green.

  9. wieslawsoltes commented on Sep 17, 2026

    @wieslawsoltes
    CollaboratorAuthor

    The exact final Code OSS/AppScene package exposed a mainline extraction gap, so this issue is reopened.

    The generic image clipboard implementation at 0f3a320e was validated on the consolidation branch but was never moved to a focused PR and is not an ancestor of current WebScene main (24db1a2f). Current main still calls clipboard.readText() for native paste. With an image-only NSPasteboard item, AppScene accepts and completes the wildcard clipboard request, but WebScene drops the result before dispatching the DOM paste event.

    Exact packaged evidence against executable SHA-256 3be428d249418493b72b313b8490c702d2c196c49409e8352cabdc0870d59729:

    • text typing/copy/paste/copyback passed in 12.695 seconds, 88/88 inputs accepted, normal close, exit 0;
    • PNG paste failed twice after 60 and 90 seconds;
    • both PNG attempts accepted 7/7 native inputs;
    • AppScene logged the accepted clipboard-read handoff (kind=3 accepted=1 bytes=0 in its internal Cocoa request enum);
    • the smoke probe remained imagePasted=false because no file-backed paste event arrived.

    Focused extraction branch fix/native-image-clipboard-mainline-86 restores the already-qualified browser-shaped file item, exact MIME/bytes, FileList, Files type, unsupported-MIME rejection, non-text readText() rejection, and the 16 MiB/performance gates on top of current main. Consolidation PR #76 remains open and unmerged.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions