Files
cmux/Sources/TextBoxInputTextView+ExternalTextSynchronization.swift
Austin Wang 31fd3b9c77 Keep screenshot paste preparation off the main thread (#8838)
* test: require image pasteboard reads off main

* fix: prepare composer image pastes off main

* test: preserve composer selection during async paste

* fix: make pending composer pastes undo-safe

* fix: parse pasted HTML without AppKit importer

* refactor: make HTML paste parser an instance service

* refactor: satisfy paste review policy

* test: cover malformed composer HTML paste

* fix: parse pasted HTML with Foundation tokenizer

* refactor: remove blocking image prepare overload

* Revert "refactor: remove blocking image prepare overload"

This reverts commit 36f65f6b02.

* test: cover async paste caret and preformatted HTML

* fix: preserve async paste caret and preformatted text

* test: cover edits during async paste

* fix: preserve composer edits during async paste

* fix: bound asynchronous paste preparation

* fix: preserve paste ordering under blocking providers

* test: cover paste worker exhaustion and reservation sync

* fix: isolate paste preparation in killable worker

* test: cover paste binding and encoded HTML regressions

* fix: preserve composer state during async paste

* test: cover overlapping paste and undo ordering

* test: model separate undo events during pending paste

* fix: keep pending paste edits undo-stable

* test: cover image paste with whitespace HTML

* fix: preserve image detection and unique test wiring

* test: cover repeated and large text pastes

* fix: preserve input and bulk paste ordering

* fix: hop clipboard confirmation to main actor

* test: bound bulk paste text transport

* fix: bound bulk paste text transport

* refactor: inject paste file operations

* refactor: simplify clipboard input routing

* test: preserve existing file URLs through paste worker

* test: document pending paste typing order

* test: cover clipboard admission ordering race

* fix: admit clipboard reads before main actor hop

* refactor: isolate clipboard callbacks on main actor

* test: bound HTML paste parsing

* fix: bound rich clipboard parsing

* docs: explain clipboard input overflow policy

* fix: satisfy strict paste concurrency checks

* fix: resolve paste pipeline CI compilation

* Make screenshot recovery test deterministic

* Document paste worker cancellation contract

* Fix paste test helper imports

* Preserve hidden HTML templates during paste

* Align HTML normalizer with package policy

* Use public AppKit attachment character in test

* Test self-closing HTML templates during paste

* Preserve text after self-closing templates

* Test HTML entities from data paste input

* Preserve non-ASCII entities in HTML paste

* Test UTF-16 HTML pasteboard data

* Decode BOM-marked HTML before paste normalization

* Test bounded HTML nesting during paste

* Bound HTML paste traversal depth

* Test temporary image destination validation

* Validate temporary image adoption destination

* Reject stale clipboard confirmation callbacks

* Fix merged main actor default warning

* Fence async paste completion to surface lifetime

* Test paste rollback and rejected HTML fallback

* Harden paste failure and worker lifetimes

* Expose clipboard callback context across extension files

* Test pending paste publication edge cases

* Fix pending paste publication edge cases

* Test pointer ordering during terminal paste

* Harden asynchronous paste sequencing

* Test stale terminal callback identity

* Fence paste callbacks to native surface lifetimes

* Test paste sequencing and content fidelity edges

* Close paste sequencing and fidelity edge cases

* Test bounded generation-aware paste input

* Bound paste input by runtime generation

* Test hidden HTML and runtime clipboard teardown

* Test atomic runtime clipboard ownership

* Close clipboard preparation and teardown races

* Test pre-admission overflow and CSS visibility

* Preserve overflow input and visible HTML descendants

* fix: make clipboard reservation sendable

* fix: expose callback identity dependency

* fix: express overflow path as conditional

* fix: type isolated workspace lookup

* test: cover paste deadline and overflow ordering

* test: fix weak reference declaration

* fix: close paste deadline and overflow races

* fix: bound runtime clipboard admission

* test: cover remaining paste lifecycle regressions

* fix: close remaining paste preparation gaps

* test: fix terminal image concurrency target compilation

* test: cover admission-scoped paste deadlines

* fix: enforce paste deadlines from admission

* test: align clipboard pointer fixture epoch

* test: stabilize clipboard input sequencing fixture

* fix: publish one clipboard completion per read

* fix: distinguish invalid paste image files

* fix: keep clipboard overflow cancellation sendable

* test: discard pre-admission input during teardown

* test: cover programmatic input during clipboard reads

* test: publish orphaned pending paste commits

* fix: close clipboard ordering races

* fix: preserve clipboard rollback admission

* fix: compile strict clipboard test targets

* test: cover clipboard review regressions

* test: cover isolated clipboard rollback capture

* fix: isolate clipboard rollback capture

* fix: export pasteboard snapshot AppKit dependency

* test: cover cancellation before mutation publication

* fix: compile strict pasteboard lane paths

* test: expose clipboard overflow replay inversion

* fix: prevent deferred input replay inversion

* test: compile async clipboard readiness assertions

* fix: keep rollback ownership across cancellation

* fix: preserve overflow handler actor isolation

* test: compile terminal clipboard package coverage

* refactor: split clipboard concurrency coverage

* test: cover abandoned clipboard restore failure

* fix: retry abandoned clipboard restoration

* test: preserve adopted image on permission failure

* fix: roll back image adoption on permission failure

* test: preserve content after self-closing raw tags

* test: align callback fixture with agent shims

* fix: close self-closing raw-text elements

* test: preserve RTF after rejected image HTML

* fix: preserve RTF fallback after rejected HTML

* fix: preserve unquoted slash template values

* test: align clipboard fixtures with runtime validation

* test: await textbox paste admission

* test: align restored snapshot resume assertion

* test: await remaining textbox paste bindings

* test: hide iframe fallback paste text

* fix: hide iframe fallback paste text

* test: discard superseded clipboard write

* fix: discard stale coalesced clipboard writes

* test: preserve RTFD rejected HTML fallback

* fix: preserve RTFD rejected HTML fallback

* test: cover clipboard cancellation and whitespace fallback

* fix: report admitted clipboard writes accurately

* test: cover mobile click and quoted raw text ordering

* fix: keep mobile clicks and raw text boundaries intact
2026-08-11 17:02:36 -07:00

62 lines
2.0 KiB
Swift

import AppKit
extension TextBoxInputTextView {
/// Projects binding-visible content while restoring private pending-paste selections.
@MainActor
func bindingContentForPreservation() -> (
text: String,
attachments: [TextBoxAttachment]
) {
guard pendingPasteReservations.values.contains(
where: { $0.usesMarker }
) else {
return (plainText(), inlineAttachments())
}
let projectedParts = TextBoxSubmissionFormatter.parts(
from: attributedContentForPreservation()
)
var projectedText = ""
var projectedAttachments: [TextBoxAttachment] = []
for part in projectedParts {
switch part {
case .text(let text):
projectedText += text
case .attachment(let attachment):
projectedAttachments.append(attachment)
}
}
return (projectedText, projectedAttachments)
}
/// Synchronizes an authoritative plain-text binding without exposing or
/// destroying private pending-paste markers.
@MainActor
@discardableResult
func synchronizeExternalTextIfNeeded(_ externalText: String) -> Bool {
let projectedContent = bindingContentForPreservation()
guard Self.shouldSynchronizeExternalTextToTextBox(
inlineAttachmentCount: projectedContent.attachments.count,
plainText: projectedContent.text,
externalText: externalText,
hasMarkedText: hasMarkedText()
) else {
return false
}
invalidatePendingAttachmentUploads()
string = externalText
return true
}
private static func shouldSynchronizeExternalTextToTextBox(
inlineAttachmentCount: Int,
plainText: String,
externalText: String,
hasMarkedText: Bool
) -> Bool {
inlineAttachmentCount == 0
&& !hasMarkedText
&& plainText != externalText
}
}