fix(ioscompanion): replace field content on input so blackout-skipped erase cannot accumulate text

This commit is contained in:
pj committed 2026-06-07 12:19:56 +05:30
1 parent d8b6dcaf98
commit 6cc84fcafe
2 files changed
+34 -5

No files matched your search

+7
View File
@@ -831,7 +831,14 @@ func waitForListener(ctx context.Context, address string) error {
}
}
// ReplacesTextOnInput reports that InputText replaces the field's content, so
// the runner skips its pre-erase. The driver clears the field inside InputText,
// which is robust even when a collapsed accessibility bridge would make the
// runner read the field length as zero and wrongly skip erasing.
func (d *Driver) ReplacesTextOnInput() bool { return true }
var (
_ driver.DeviceDriver = (*Driver)(nil)
_ driver.ForegroundChecker = (*Driver)(nil)
_ driver.TextReplacer = (*Driver)(nil)
)
+27 -5
View File
@@ -103,17 +103,39 @@ func usesPasteboard(text string) bool {
return len(skipped) > 0
}
// inputText types text into the focused field. Mappable text goes through the
// hardware keyboard in one HID stream; anything else falls back to the
// pasteboard. The field target is only consulted on the pasteboard path.
// inputText replaces the focused field's content with text. It selects any
// existing content and deletes it first, so the result is the typed text alone
// regardless of what the field held. Replacing (rather than relying on the
// runner's pre-erase) keeps input correct even when the accessibility bridge is
// momentarily collapsed and the runner cannot read the field's length. Mappable
// text goes through the hardware keyboard in one HID stream; anything else falls
// back to the pasteboard. The field target is only consulted on the pasteboard
// path.
func inputText(ctx context.Context, run runner, text string, field fieldTarget) error {
if !usesPasteboard(text) {
presses, _ := typeString(text)
return run.sendHID(ctx, keyPressEvents(presses)...)
events := append(clearFieldEvents(), keyPressEvents(typeStringPresses(text))...)
return run.sendHID(ctx, events...)
}
if err := run.sendHID(ctx, clearFieldEvents()...); err != nil {
return fmt.Errorf("clear field: %w", err)
}
return pasteText(ctx, run, text, field)
}
// typeStringPresses is typeString's presses, dropping the skipped runes (the
// caller already decided this text is fully mappable).
func typeStringPresses(text string) []KeyPress {
presses, _ := typeString(text)
return presses
}
// clearFieldEvents selects the whole field (command+A) and deletes it, so a
// following type or paste lands in an empty field. On an already-empty field
// the select selects nothing and the delete is a no-op.
func clearFieldEvents() []transport.HIDEvent {
return append(selectAllChordEvents(), keyPressEvents(backspaces(1))...)
}
// pasteText copies the full text to the pasteboard, sends the paste chord
// once, and polls until the field reflects the text. The chord is re-sent ONLY
// after dismissing a permission dialog (the dialog swallowed that paste);