Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

## [1.0.5] - 2026-09-06

### Fixed

- Dictation still not pasting into terminals after 1.0.4: the system-wide `AXFocusedUIElement` lookup returns nothing for some apps (terminals), so detection saw "no focused element" and diverted to the clipboard. It now falls back to the frontmost application's own focused element, which those apps do expose.

## [1.0.4] - 2026-09-06

### Fixed
Expand Down
4 changes: 2 additions & 2 deletions Resources/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>1.0.4</string>
<string>1.0.5</string>
<key>CFBundleVersion</key>
<string>1.0.4</string>
<string>1.0.5</string>
<key>LSMinimumSystemVersion</key>
<string>14.0</string>
<key>LSUIElement</key>
Expand Down
26 changes: 21 additions & 5 deletions Sources/EchoType/Paster.swift
Original file line number Diff line number Diff line change
Expand Up @@ -76,14 +76,10 @@ enum Paster {
/// Warp/Ghostty) expose an opaque focused view — treat that as a text target
/// rather than losing the paste, matching pre-1.0.3 behavior.
private static func hasTextTarget() -> Bool {
let system = AXUIElementCreateSystemWide()
var focused: AnyObject?
guard AXUIElementCopyAttributeValue(system, kAXFocusedUIElementAttribute as CFString, &focused) == .success,
let element = focused else {
guard let axElement = focusedElement() else {
Log.write("paste: no focused element — copying to clipboard")
return false
}
let axElement = element as! AXUIElement

var role = "<none>"
var roleValue: AnyObject?
Expand All @@ -104,6 +100,26 @@ enum Paster {
return true
}

/// The focused UI element. The system-wide `AXFocusedUIElement` is empty for
/// some apps (terminals), so fall back to the frontmost application's own
/// focused element.
private static func focusedElement() -> AXUIElement? {
let system = AXUIElementCreateSystemWide()
var focused: AnyObject?
if AXUIElementCopyAttributeValue(system, kAXFocusedUIElementAttribute as CFString, &focused) == .success,
let element = focused {
return (element as! AXUIElement)
}
guard let pid = NSWorkspace.shared.frontmostApplication?.processIdentifier else { return nil }
let appElement = AXUIElementCreateApplication(pid)
var appFocused: AnyObject?
if AXUIElementCopyAttributeValue(appElement, kAXFocusedUIElementAttribute as CFString, &appFocused) == .success,
let element = appFocused {
return (element as! AXUIElement)
}
return nil
}

private static func isValueSettable(_ element: AXUIElement) -> Bool {
var settable: DarwinBoolean = false
guard AXUIElementIsAttributeSettable(element, kAXValueAttribute as CFString, &settable) == .success else {
Expand Down
Loading