You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Release v1.7.3: Dock mode fix, audio diagnostics, typing for remote desktops - #128
Windows App copies the Mac clipboard to the remote PC only after the user leaves its window, even with Clipboard mode: Bidirectional (confirmed on Windows App 11.4.0 for a Cloud PC). OpenWritr's auto-paste writes the clipboard and sends Cmd+V, so the remote PC pastes stale text. Option+hotkey clipboard-only output has the same limit.
This adds an opt-in, experimental setting, Type Text in Remote Desktop Apps, that avoids the clipboard: when Auto-Paste is on, the setting is on, and the frontmost app is Windows App (com.microsoft.rdc.macos), OpenWritr types the text key by key instead of pasting. The clipboard is not touched, so nothing needs restoring.
Why key codes, not Unicode text
The first version sent Unicode keyboard events. On a real Cloud PC that typed only aaa: Windows App reads the physical key code of an event (key code 0 is "A") and ignores its Unicode string. The typing is therefore done with real key codes:
The current keyboard layout (UCKeyTranslate over key codes 0-127, unshifted and Shift) is turned into a character → key map. Unshifted keys win over shifted ones; dead keys, function keys and control characters are excluded.
Each character is sent as that key. Shift is sent as a real modifier key event around the key, because a remote desktop client tracks modifier changes rather than the flags of a key event.
Events are created up front (so failures are detected before anything is sent) and posted in order on a background queue with 3 ms between events, so the main thread is not blocked and the client is not flooded.
\n/\r\n become Return and \t becomes Tab. Text is composed (NFC) before mapping.
Typographic quotes, dashes, the ellipsis and non-breaking spaces are replaced by their plain equivalents. Characters with no key on the two base layers (emoji, and Option-layer symbols such as @, €, brackets on a German layout) are skipped and only their count is logged (never the text).
Requirement: the remote PC must use the same keyboard layout as the Mac; otherwise accents and symbols come out wrong. The maintainer confirmed they can set the Cloud PC layout to match their German Mac layout.
Applies only to standard output with Auto-Paste on. Clipboard-only recordings (Option) still copy. Any other frontmost app still pastes as before. A failure to build the events cancels output with a dedicated "Typing Failed" message and keeps the transcript in OpenWritr. Off by default; the toggle is disabled when Auto-Paste is off.
Related issue
Maintainer request for a typing function as a special option after the clipboard-redirection setting turned out to be already Bidirectional. Related to the clipboard-only shortcut (#121).
#126 and #127 are already merged. This PR now also carries the 1.7.3 release preparation: both bundle versions are set to 1.7.3 and the changelog section is dated 2026-10-13. Verified on a real Cloud PC by the maintainer; see Validation.
Prepared with an AI assistant using Copilot SDK in VS Code.
Validation
swift build -c release -Xswiftc -warnings-as-errors, swiftlint lint --strict, and swift test succeed locally
For audio, hotkey, paste, overlay, or update changes: I ran the built app and checked the affected flow
How the change was verified (the tests cover pure logic only, so say what you tried by hand for UI, audio, hotkey, or update changes):
swift build -c release -Xswiftc -warnings-as-errors: passed. swiftlint lint --strict: 0 violations. swift test: 95 tests passed. New tests cover the planner (order, Shift, newlines/tabs, typographic replacement, skipped and counted characters, composed accents, a replacement skipped when the layout lacks its characters), the real layout (every mapped character round-trips through UCKeyTranslate on the German layout of the test machine; letters unshifted and capitals add Shift; no control characters), and routing (types only when Windows App is frontmost, falls back to paste otherwise, failure cancels with typingFailed and leaves the clipboard unchanged, option/destination matrix, pending restore flushed).
The layout access must run on the main thread (Text Input Source Services); it is main-actor isolated, and a first test run that called it off the main thread crashed the test process, which is why.
The first (Unicode) version was tried by the maintainer on a real Cloud PC and typed only aaa. The key-code version was then tried by the maintainer against the same real Windows 365 Cloud PC through Windows App 11.4.0 with matching German keyboard layouts on both sides, and worked ("funktioniert hervorragend"), including Shift handling. Not tested: other remote layouts, other remote desktop clients, and long texts. Events were also not verified in a local text view, because that needs window focus and the machine was in use.
Impact
User or operational risk: only when the option is enabled and Windows App is frontmost. In the worst case the remote session receives unexpected characters (for example with mismatched layouts); the text stays in OpenWritr either way. Skipped characters (emoji, Option-layer symbols) are not typed.
Security or privacy impact (permissions, network destinations, stored or transmitted data): no new permissions (the same Accessibility access as pasting), no network, no stored data beyond one boolean preference. Typing avoids writing the transcript to the clipboard. Logs contain only the number of skipped characters.
Compatibility or migration impact (macOS versions, saved preferences, update path): new preference typeIntoRemoteDesktopsEnabled, default false. Existing behavior is unchanged unless the user turns it on.
Documentation
README, docs, and changelog are updated where the change is user-visible
setActivationPolicy returns false when the policy is already in effect. That made every launch log a restore failure and made switching between Dock Only and Dock and Menu Bar fail.
Fixes#113
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Audio error logs became public issues with only an OSStatus, so the device details needed to diagnose them had to be requested afterwards. Append how the microphone was chosen, its transport, format, and whether another process uses it. Never log device names or UIDs.
Refs #114, #119
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…top apps
Windows App syncs the Mac clipboard to the remote PC only after its window loses focus, so an automatic paste inserts stale text. When the option is on and Windows App is frontmost, type the text as keyboard events instead of pasting, and leave the clipboard untouched.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The reason will be displayed to describe this comment to others. Learn more.
No high-confidence actionable HIG defects were found.
Rendered surfaces and variants reviewed:
Settings: light, dark, and light with accessibility-size text.
About: light, dark, and dark with accessibility-size text.
Overlay, light and dark: listening, transcribing, enhancing, done, copied, clipboard, enhanced-clipboard, and error.
The new Settings toggle uses a native Toggle. Its secondary caption uses semantic .secondary and .caption, and wraps with fixedSize(horizontal: false, vertical: true). The toggle is disabled when auto-paste is off. The typing-failure error keeps the transcript in OpenWritr and does not touch the clipboard. I found no concrete privacy or Reduce Motion issue in the diff, and I did not report anything inferred from unrendered behavior.
Windows App reads the physical key code of a keyboard event and ignores its Unicode string, so the first version typed only a. Map each character to the key that produces it on the current keyboard layout and send Shift as a real modifier event. Skip and count characters without a plain key.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The reason will be displayed to describe this comment to others. Learn more.
No high-confidence actionable HIG defects were found.
Rendered surfaces and variants reviewed
Settings: light, dark, light with larger accessibility text
About: light, dark, dark with larger accessibility text
Overlay, each in light and dark: listening, transcribing, enhancing, done, copied, clipboard, enhanced-clipboard, error
Basis
The Settings change is a native Toggle with a visible label, bound to an immediate preference with no Apply button. It is disabled when auto-paste is off, and the caption uses .caption and .secondary, so it follows semantic text styles and colors.
The new "Typing Failed" error path reuses the existing error presentation. It tells the user the transcript is retained, and the typing path leaves the clipboard untouched.
I found nothing in the PR diff that adds motion or credential or provider handoff.
I did not infer unrendered behavior, such as the typing events themselves, from the screenshots.
The reason will be displayed to describe this comment to others. Learn more.
No high-confidence actionable HIG defects were found in PR #128.
I opened every PNG and compared the variants. I did not identify a visible defect that I could tie to the diff (the new Settings toggle and caption, and the new "Typing failed" overlay message).
Rendered surfaces and variants reviewed
Settings: light, dark, and light with larger accessibility text.
About: light, dark, and dark with larger accessibility text.
Overlay, each in light and dark: listening, transcribing, enhancing, done, copied, clipboard, enhanced-clipboard, and error.
Diff checks
The new Settings toggle is a native Toggle. It is disabled when auto-paste is off. The caption uses .caption with .secondary and fixedSize, so it wraps.
The remote-desktop typing path does not touch the clipboard. A typing failure keeps the transcript in OpenWritr and shows an error. I found no concrete privacy defect.
The "Typing failed" overlay state is not among the rendered snapshots, so I did not judge it visually.
trsdn
changed the title
feat(paste): add an experimental option to type text into remote desktop apps
Release v1.7.3: Dock mode fix, audio diagnostics, typing for remote desktops
Oct 13, 2026
The reason will be displayed to describe this comment to others. Learn more.
HIG review of the rendered UI (Settings, About, overlay) and the diff.
Findings
Medium – Sources/OpenWritr/TextTyping.swift, typeText and TextTypingPlanner.plan: dropped characters are reported as success.
Characters with no key in the layout are skipped. Examples are emoji, accented letters on a US layout, and non-Latin text.
The skip is only logged, and typeText still returns true. typeTextIfRemoteDesktop then returns .pasted, so the overlay shows the normal success state while the remote text is missing content.
Impact: the user can silently lose part of a transcript and be told it succeeded.
Fix: surface plan.skippedCharacters in the outcome. For example, show a non-success overlay message such as "N characters couldn't be typed", or return .cancelled or .retained when characters are skipped. Keep the transcript in OpenWritr in that case, as the .typingFailed path does.
Medium – Sources/OpenWritr/TextTyping.swift, typeText: the "Typing failed" error path is unreachable for the most likely failure.
Events are posted asynchronously on a background queue. true is returned as soon as the events are built.
If Accessibility access is missing, CGEvent.post is silently ignored. The user gets a success state with nothing typed. The new recovery text ("Check Accessibility access") never appears.
Fix: check AXIsProcessTrusted() before typing and return false when it is not trusted. This triggers the existing .typingFailed presentation.
Low – Sources/OpenWritr/SettingsView.swift, lines around the new toggle: the disabled state is not explained.
The toggle is disabled when "Auto-paste" is off, but nothing says so. The caption describes only the enabled behavior.
Fix: add a caption or help text such as "Requires Auto-Paste".
Rendered surfaces and variants reviewed
Settings: light, dark, and light with accessibility text.
About: light, dark, and dark with accessibility text.
Overlay, each in light and dark: listening, transcribing, enhancing, done, copied, clipboard, enhanced-clipboard, and error.
I found no layout, contrast, or clipping defects in the rendered images that I could attribute to this PR.
Typing runs in the background and can take seconds. Check before every key that the app that was frontmost is still frontmost, so the rest of a transcript is never typed into a different app, and make the typing error advice mention the keyboard layout as well as Accessibility.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Revalidate frontmost app when capturing target PID
Sources/OpenWritr/TextTyping.swift:232
The remote-app check in PasteManager happens before layout planning and this second frontmost-app lookup. If focus changes in that interval, target becomes the newly focused local app; the later per-key checks then keep accepting that PID and can type the transcript into it. Revalidate the bundle identifier in the same lookup that captures the target PID so a focus change cancels typing instead of trusting a different app.
The reason will be displayed to describe this comment to others. Learn more.
Reviewed all 22 rendered PNGs: Settings (light, dark, light large-text), About (light, dark, dark large-text), and the overlay states (listening, transcribing, enhancing, done, copied, clipboard, enhanced-clipboard, error) in light and dark. The new Settings toggle and caption render legibly, wrap without clipping in the large-text variant, and use semantic styles. The findings below come from the diff and source, not from the screenshots.
Medium: Sources/OpenWritr/TextTyping.swift, SystemTextTyper.typeText (the Self.queue.async block and return true).
typeText returns true before any key is posted, so PasteManager returns .pasted and the overlay shows the success state.
Typing then runs asynchronously. It stops silently if focus leaves the remote-desktop app, and the log notice is the only record of that. The user sees a success overlay with a truncated or empty insertion.
Correction: report partial delivery to the main actor, for example by making typing awaitable or adding a completion callback. When delivered < groups.count, surface a .typingFailed-style error that says the transcript is retained.
Also hold the overlay's "in progress" state until typing finishes.
Characters with no key in the layout are dropped, and the count only goes to the log. The Settings caption warns about this, but the success overlay still looks complete after silent data loss.
Correction: when skippedCharacters > 0, show a distinct non-error notice such as "Typed; N characters skipped". The transcript stays available, so the user can recover it.
Low: Sources/OpenWritr/SettingsView.swift, new toggle and caption.
The toggle is disabled when Auto-Paste is off, and nothing says why.
The explanatory Text is a separate element, so VoiceOver does not announce it with the toggle.
Correction: add an .accessibilityHint to the toggle with the key explanation. Add a short note that the toggle requires Auto-Paste.
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
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.
Pull Request
Summary
Windows App copies the Mac clipboard to the remote PC only after the user leaves its window, even with Clipboard mode: Bidirectional (confirmed on Windows App 11.4.0 for a Cloud PC). OpenWritr's auto-paste writes the clipboard and sends Cmd+V, so the remote PC pastes stale text. Option+hotkey clipboard-only output has the same limit.
This adds an opt-in, experimental setting, Type Text in Remote Desktop Apps, that avoids the clipboard: when Auto-Paste is on, the setting is on, and the frontmost app is Windows App (
com.microsoft.rdc.macos), OpenWritr types the text key by key instead of pasting. The clipboard is not touched, so nothing needs restoring.Why key codes, not Unicode text
The first version sent Unicode keyboard events. On a real Cloud PC that typed only
aaa: Windows App reads the physical key code of an event (key code 0 is "A") and ignores its Unicode string. The typing is therefore done with real key codes:UCKeyTranslateover key codes 0-127, unshifted and Shift) is turned into a character → key map. Unshifted keys win over shifted ones; dead keys, function keys and control characters are excluded.\n/\r\nbecome Return and\tbecomes Tab. Text is composed (NFC) before mapping.@,€, brackets on a German layout) are skipped and only their count is logged (never the text).Requirement: the remote PC must use the same keyboard layout as the Mac; otherwise accents and symbols come out wrong. The maintainer confirmed they can set the Cloud PC layout to match their German Mac layout.
Applies only to standard output with Auto-Paste on. Clipboard-only recordings (Option) still copy. Any other frontmost app still pastes as before. A failure to build the events cancels output with a dedicated "Typing Failed" message and keeps the transcript in OpenWritr. Off by default; the toggle is disabled when Auto-Paste is off.
Related issue
Maintainer request for a typing function as a special option after the clipboard-redirection setting turned out to be already Bidirectional. Related to the clipboard-only shortcut (#121).
#126 and #127 are already merged. This PR now also carries the 1.7.3 release preparation: both bundle versions are set to 1.7.3 and the changelog section is dated 2026-10-13. Verified on a real Cloud PC by the maintainer; see Validation.
Prepared with an AI assistant using Copilot SDK in VS Code.
Validation
swift build -c release -Xswiftc -warnings-as-errors,swiftlint lint --strict, andswift testsucceed locallyHow the change was verified (the tests cover pure logic only, so say what you tried by hand for UI, audio, hotkey, or update changes):
swift build -c release -Xswiftc -warnings-as-errors: passed.swiftlint lint --strict: 0 violations.swift test: 95 tests passed. New tests cover the planner (order, Shift, newlines/tabs, typographic replacement, skipped and counted characters, composed accents, a replacement skipped when the layout lacks its characters), the real layout (every mapped character round-trips throughUCKeyTranslateon the German layout of the test machine; letters unshifted and capitals add Shift; no control characters), and routing (types only when Windows App is frontmost, falls back to paste otherwise, failure cancels withtypingFailedand leaves the clipboard unchanged, option/destination matrix, pending restore flushed).aaa. The key-code version was then tried by the maintainer against the same real Windows 365 Cloud PC through Windows App 11.4.0 with matching German keyboard layouts on both sides, and worked ("funktioniert hervorragend"), including Shift handling. Not tested: other remote layouts, other remote desktop clients, and long texts. Events were also not verified in a local text view, because that needs window focus and the machine was in use.Impact
typeIntoRemoteDesktopsEnabled, default false. Existing behavior is unchanged unless the user turns it on.Documentation