diff --git a/CHANGELOG.md b/CHANGELOG.md index 1900ad9..aa29354 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,14 +12,6 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). -## [v1.4.1] - 2026-06-08 - -### Bug Fixes - -- The `heph` CLI and `heph-tui` now survive a daemon restart. Previously the unix-socket client connected once and never reconnected, so an opt-in self-update or `heph daemon restart` left every subsequent call failing — `heph-tui` would sit on errors until relaunched. The client now reconnects on a dropped socket: a request that never went out is retried transparently, while a reply lost mid-request is surfaced (not silently retried) so a mutation is never double-applied. A long-running TUI self-heals on its next refresh tick. -- Quick-add popover (⌘'): hand keyboard focus back to the previously active app when it hides, and stop the (now invisible) overlay from intercepting clicks where it used to sit. - - ## [v1.4.0] - 2026-06-08 ### Features diff --git a/Cargo.lock b/Cargo.lock index cc9b3a6..be8f974 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2237,8 +2237,6 @@ dependencies = [ "heph-core", "hephd", "libc", - "objc2 0.6.4", - "objc2-app-kit 0.3.2", "serde_json", "winit", ] diff --git a/crates/heph-quickadd/Cargo.toml b/crates/heph-quickadd/Cargo.toml index 57bbb98..5b1889b 100644 --- a/crates/heph-quickadd/Cargo.toml +++ b/crates/heph-quickadd/Cargo.toml @@ -19,16 +19,7 @@ global-hotkey = "0.8" # macOS-only: winit for the accessory-mode activation policy (no Dock icon), # pinned to the same minor eframe carries so cargo unifies to one winit; libc -# for getppid() (orphan detection — self-exit when the supervising daemon dies); -# objc2 + objc2-app-kit to hand keyboard focus back to the previously active app -# when the popover hides (NSApplication.hide:/unhide:). Pinned to the 0.6/0.3 -# line global-hotkey already pulls in, so cargo unifies to one copy. +# for getppid() (orphan detection — self-exit when the supervising daemon dies). [target.'cfg(target_os = "macos")'.dependencies] winit = "0.30" libc = "0.2" -objc2 = "0.6" -objc2-app-kit = { version = "0.3", default-features = false, features = [ - "std", - "NSApplication", - "NSResponder", -] } diff --git a/crates/heph-quickadd/src/app.rs b/crates/heph-quickadd/src/app.rs index a334b22..b08bf03 100644 --- a/crates/heph-quickadd/src/app.rs +++ b/crates/heph-quickadd/src/app.rs @@ -226,9 +226,6 @@ impl QuickAdd { } fn show(&mut self, ctx: &egui::Context) { - // Undo the app-level hide from the previous `hide()` so we can take focus - // again (no-op the first time / off macOS). - app_take_focus(); self.visible = true; self.focus_pending = true; self.current_hint = random_hint(self.current_hint); @@ -259,13 +256,6 @@ impl QuickAdd { ctx.send_viewport_cmd(egui::ViewportCommand::InnerSize(egui::vec2(WIN_W, BASE_H))); self.win_h_applied = BASE_H; } - // Hand keyboard focus back to the app underneath us. winit's - // `Visible(false)` alone leaves *us* the active application, so focus - // never returns and the borderless always-on-top overlay can keep eating - // clicks where it used to sit. `NSApplication.hide:` orders our windows - // fully out and activates the next app in line — exactly the one the user - // was in (no-op off macOS). - app_yield_focus(); } /// Optimistic submit: hide now, create in the background. @@ -606,39 +596,6 @@ impl QuickAdd { } } -/// Hide the popover at the *application* level so macOS hands keyboard focus -/// back to the previously active app. `NSApplication.hide:` orders all our -/// windows out and activates the next app in line — the one the user was in — -/// which a plain winit `Visible(false)` does not do. No-op off macOS. -#[cfg(target_os = "macos")] -fn app_yield_focus() { - use objc2::MainThreadMarker; - use objc2_app_kit::NSApplication; - // eframe's `update` runs on the main thread, so this marker is always Some. - if let Some(mtm) = MainThreadMarker::new() { - NSApplication::sharedApplication(mtm).hide(None); - } -} - -#[cfg(not(target_os = "macos"))] -fn app_yield_focus() {} - -/// Undo [`app_yield_focus`]: clear the app-level hidden flag before re-showing, -/// so the window the viewport `Focus` command then makes key actually appears. -/// (`unhide:` also re-activates us; the per-window `Focus`/`Visible` viewport -/// commands do the rest.) No-op off macOS. -#[cfg(target_os = "macos")] -fn app_take_focus() { - use objc2::MainThreadMarker; - use objc2_app_kit::NSApplication; - if let Some(mtm) = MainThreadMarker::new() { - NSApplication::sharedApplication(mtm).unhide(None); - } -} - -#[cfg(not(target_os = "macos"))] -fn app_take_focus() {} - /// The current parent process id, for orphan detection. `None` off macOS (where /// hephd does not supervise a helper — there is no Aqua session to inherit). fn current_parent_pid() -> Option { diff --git a/docs/changelog.d/client-reconnect.bugfix.md b/docs/changelog.d/client-reconnect.bugfix.md new file mode 100644 index 0000000..ae987b8 --- /dev/null +++ b/docs/changelog.d/client-reconnect.bugfix.md @@ -0,0 +1 @@ +The `heph` CLI and `heph-tui` now survive a daemon restart. Previously the unix-socket client connected once and never reconnected, so an opt-in self-update or `heph daemon restart` left every subsequent call failing — `heph-tui` would sit on errors until relaunched. The client now reconnects on a dropped socket: a request that never went out is retried transparently, while a reply lost mid-request is surfaced (not silently retried) so a mutation is never double-applied. A long-running TUI self-heals on its next refresh tick.