feat!: replace decoy-textarea clipboard handling with the async Clipboard API - #1270
Draft
6pac-ai wants to merge 2 commits into
Draft
feat!: replace decoy-textarea clipboard handling with the async Clipboard API#12706pac-ai wants to merge 2 commits into
6pac-ai wants to merge 2 commits into
Conversation
…oard API BREAKING CHANGE: CellExternalCopyManager now copies via navigator.clipboard.writeText and pastes via navigator.clipboard.readText (secure context required). The bodyElement and clipboardPasteDelay options are removed, _decodeTabularData takes the clipboard text instead of a textarea, and paste completes asynchronously. New clipboardWriteOverride / clipboardReadOverride plugin options replace the transport where the Clipboard API is unavailable. The row split in _decodeTabularData now treats CRLF as one delimiter (the decoy textarea normalized CRLF away; readText does not). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Collaborator
|
oh ok so this change is to use the new Clipboard API like I do in my repo. However as discussed this should be tagged as a Major Version (aka breaking changes), so I added the label tag and I converted the PR to draft to avoid merging it by mistake |
ghiscoding
marked this pull request as draft
August 7, 2026 17:53
Owner
|
Great. Note there is a warning at the start, and also note that it claims to have found and fixed a bug in the slickgrid-universal implementation. |
Headless CI runners deny real clipboard access (focus/permission), so the realPress Ctrl+C/Ctrl+V test now stubs navigator.clipboard while keeping the real keystroke pipeline; the real-hardware path stays a manual check. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Collaborator
|
oh thanks, I didn't notice that comment, will check it out on Monday |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Warning
Breaking change — targeted at the next breaking release, per the discussion on #1261 ("we will update the code and examples to use the modern clipboard API next breaking release"). Please do not merge into the current minor line.
Replaces
CellExternalCopyManager's decoy-textarea clipboard handling with the asynchronous Clipboard API, following slickgrid-universal's implementation.What changed
Copy serializes the selected ranges in memory (loop unchanged) and calls
await navigator.clipboard.writeText(clipText). The IEwindow.clipboardDatabranch, the decoy textarea, and the focus steal/restore dance are gone.Paste calls
await navigator.clipboard.readText()and feeds the text straight to_decodeTabularData. No decoy, noinputlistener, no delay — the paste-delivery race fixed by fix: decode clipboard paste on delivery instead of a fixed delay #1261 is now structurally impossible, so that interim machinery is deleted with the decoy.handleKeyDownisasync, wrapped in try/catch: an unavailable Clipboard API or denied permission surfaces as a console error, never a throw.e.preventDefault()is issued synchronously before the firstawait(the old flow deliberately allowed the native default because the decoy needed it; the new flow must suppress it so a stray DOM selection can't race thewriteText).Key detection modernized to
e.key(case-insensitive on the letters, so Caps Lock behaves as before).New plugin options as an escape hatch for non-secure contexts or app-managed clipboards:
clipboardWriteOverride?: (text: string) => void | Promise<void>clipboardReadOverride?: () => string | Promise<string>These are plugin options rather than grid options (where slickgrid-universal put its
clipboardWriteOverride) — the concern is entirely the plugin's.CRLF fix beyond the straight port
The decoy textarea silently normalized CRLF→LF when the browser delivered a paste into it, so
_decodeTabularData'ssplit(/[\n\f\r]/)never actually met a\r\npair. WithreadText()the raw\r\nsurvives, and that split treats it as two delimiters — injecting a phantom blank row after every line (a 2×2 Excel paste lands as 4 rows). The split is now/\r\n|[\n\f\r]/, restoring the old effective behavior for both LF and CRLF payloads.Heads-up @ghiscoding: slickgrid-universal has the identical split with the
readTexttransport, so it looks like it carries this latently — multi-row pastes from Windows Excel would gain interleaved blank rows there too. (The stale 2024 draft branchfeat/clipboard-apiin this repo spotted the CRLF issue, but its.replace('\r\n', '\n')takes a string argument and so only fixes the first row boundary.)Note
This PR supersedes the stale 2024 draft branch
feat/clipboard-api(8a7b407, plugin-only — no override hooks, interface cleanup, example updates, or tests); that branch can be deleted once this merges.Breaking surface (migration notes)
navigator.clipboardrequires a secure context (https or localhost) and clipboard-read permission for paste (Firefox shows a paste prompt)clipboardWriteOverride/clipboardReadOverridebodyElementoption removed (existed only to place the decoy inside modal dialogs)clipboardPasteDelayoption removed (no delay exists)_decodeTabularData(grid, ta: HTMLTextAreaElement)→_decodeTabularData(grid, clipText: string)(protected, visible to subclassers)onPasteCellsrather than timingExamples
example-plugin-contextmenu.html,example-plugin-hybridselectionmodel.html,example-plugin-hybridselectionmodel-esm.html: thecopyCellValuehelper now usesnavigator.clipboard.writeText(per the "code and examples" scope). Incidentally this revives a dead path — the old fallback calledtmpElem.get(0)(jQuery residue) on a plain DOM node and threw into an empty catch, soexecCommandcopy never actually ran.examples/example-clipboard-api.htmlis a temporary demo page for this review — do not merge (delete before merging). Grid 1 uses the real clipboard; Grid 2 routes both hooks through a visible "app-managed clipboard" box.Tests
cypress/e2e/quirk-clipboard-paste-event-driven.cy.ts: it pinned the fixed-delay race via theclipboardPasteDelayoption, and both the option and the mechanism no longer exist.cypress/e2e/clipboard-api.cy.ts(self-hosting, 4 tests): copy serialization through a stubbedwriteText(asserting the exact tab/CRLF text + Escape cancel + no decoy textarea in the DOM), async paste decode through stubbedreadText(editor path), both override hooks withnavigator.clipboarduntouched (raw field-assignment path), and the console-error surface when the API is unavailable. Stub pattern mirrors slickgrid-universal's unit tests. Validated bidirectionally: all 4 tests fail on the pre-port build, all pass after.example-excel-compatible-spreadsheet.cy.tsupdated for the string signature; itsrealPressCtrl+C → Ctrl+V test drives the full real-keystroke pipeline with the transport stubbed. (The first CI run of this PR demonstrated why: local desktop Electron passes through the real clipboard, but the headless Linux runner denies the clipboard read — focus/permission — so the paste silently lands in the plugin's console-error path. The real-hardware path is a manual check via the demo page.)🤖 Generated with Claude Code