From 1045e1d07cf88f8f2ae1361976dd4b50513aa7cc Mon Sep 17 00:00:00 2001 From: l0ng-ai <24760907+l0ng-ai@users.noreply.github.com> Date: Thu, 30 Jul 2026 01:04:43 +0800 Subject: [PATCH] fix(ui): kick every local window's sync when the local link comes up MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A window built while the local control link was still dialing parks as Unprimed { dirty } — start_prime's unreachable arm leaves the retry to "the reconnect-triggered save", but the local link supervisor never triggered one. On a first launch (window built before the auto-spawned daemon binds its socket) nothing else re-enters sync_window until the next structural change, so quitting before one loses the window's layout: the machine never heard of it. Reproduced end-to-end on a scratch daemon: fresh launch, no user action, quit — the relaunch came up empty. With the link supervisor calling tree_sync::on_link_up on connect, the same launch syncs the tree within one pump tick. --- src/ui/local_link.rs | 5 +++++ src/ui/tree_sync.rs | 22 ++++++++++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/src/ui/local_link.rs b/src/ui/local_link.rs index 8a190f41..67e762e9 100644 --- a/src/ui/local_link.rs +++ b/src/ui/local_link.rs @@ -162,6 +162,11 @@ impl LocalLink { cx, tty7_core::host::HostId::LOCAL, ); + // …and re-runs every local window's sync: a window + // built while this link was still dialing is parked + // `Unprimed { dirty }` with nothing else scheduled to + // wake it (see `tree_sync::on_link_up`). + crate::ui::tree_sync::on_link_up(cx, tty7_core::host::HostId::LOCAL); } Err(e) => { // The next tick schedules the following attempt off diff --git a/src/ui/tree_sync.rs b/src/ui/tree_sync.rs index 98fde807..eb2fd8d7 100644 --- a/src/ui/tree_sync.rs +++ b/src/ui/tree_sync.rs @@ -852,6 +852,28 @@ pub(crate) fn sync_window(app: &Tty7App, cx: &mut App) { } } +/// A control link to `host` just came up (or came back): re-run the sync for +/// every window bound to that machine. +/// +/// This is the retry [`start_prime`]'s unreachable arm leaves behind. A window +/// built while the link was still dialing parks as `Unprimed { dirty }`, and +/// the only other thing that re-enters [`sync_window`] is the *next* +/// structural change — on a first launch that may never come, and a quit +/// before it comes loses the window's layout (the machine never heard of it). +/// The link supervisor calling this on connect is what turns "the reconnect +/// gets there first" from a hope into a mechanism. Harmless for windows that +/// are already synced: their diff is empty and queues nothing. +pub(crate) fn on_link_up(cx: &mut App, host: HostId) { + for (workspace, app) in crate::ui::windows::WindowRegistry::open_windows(cx) { + if WorkspaceStore::host_of(cx, workspace) != host { + continue; + } + if let Some(app) = app.upgrade() { + app.update(cx, |app, cx| sync_window(app, cx)); + } + } +} + /// Whether `client_ws`'s window has seen its machine's tree (or was declared /// authoritative). The gate for destructive acts an *empty* window licenses — /// a window whose hydration has not answered is empty because it is waiting,