From 70c7d5b0ecde8cb8048d790999b7b61c20ca3bb3 Mon Sep 17 00:00:00 2001 From: wing-anara Date: Sun, 6 Sep 2026 18:15:07 +0000 Subject: [PATCH 1/4] Fix live transcript selection and preserve runway on prompt expansion --- crates/ui/src/markdown/render.rs | 12 + crates/ui/src/transcript.rs | 373 +++++++++++++++++- docs/transcript-selection-regressions.md | 56 +++ scripts/fixtures/README.md | 4 + .../transcript-selection-stream.jsonl | 53 +++ 5 files changed, 487 insertions(+), 11 deletions(-) create mode 100644 docs/transcript-selection-regressions.md create mode 100644 scripts/fixtures/transcript-selection-stream.jsonl diff --git a/crates/ui/src/markdown/render.rs b/crates/ui/src/markdown/render.rs index 184ed09ccb..de2a9920bf 100644 --- a/crates/ui/src/markdown/render.rs +++ b/crates/ui/src/markdown/render.rs @@ -903,6 +903,18 @@ thread_local! { static REGISTRY: RefCell> = const { RefCell::new(Vec::new()) }; } +#[cfg(test)] +pub(crate) fn selection_test_bounds(key: &str) -> gpui::Bounds { + REGISTRY.with(|r| { + r.borrow() + .iter() + .find(|entry| entry.key.as_ref() == key) + .expect("text must be registered") + .layout + .bounds() + }) +} + /// A zero-size canvas that clears the selection registry — paint it FIRST in /// the transcript root (before any markdown), so each frame's registry holds /// exactly that frame's visible text elements in paint order. diff --git a/crates/ui/src/transcript.rs b/crates/ui/src/transcript.rs index 0974fb784f..74f3576175 100644 --- a/crates/ui/src/transcript.rs +++ b/crates/ui/src/transcript.rs @@ -1991,6 +1991,9 @@ struct FoldState { /// Per-toggle duration. User bubbles scale this with travel distance; /// existing tool folds leave it at zero and keep their catalog constants. duration_ms: u64, + /// Extra user-body height revealed by Show more. It is not reply growth + /// and must not permanently consume the sent turn's reservation. + user_expansion_height: f32, } /// Viewport compensation paired with a long user-message collapse. While the @@ -2742,7 +2745,11 @@ impl Transcript { self.discard_pending_viewport(); self.cancel_user_hold(); self.user_collapse_scroll = None; - // Rail navigation within the session RELEASES the hold but keeps the + self.stop_automatic_scrolling(); + } + + fn stop_automatic_scrolling(&mut self) { + // Navigation and selection within the session release the hold but keep the // runway (user spec: only leaving and revisiting the session clears // it) — scrolling back down re-arms the hold like any restick. self.release_own_turn_hold(); @@ -2897,12 +2904,33 @@ impl Transcript { self.schedule_selection_scroll(cx); } + fn on_selection_mouse_down( + &mut self, + event: &gpui::MouseDownEvent, + _window: &mut Window, + cx: &mut Context, + ) { + // Text listeners claim the drag before it bubbles here. Stop following + // immediately: a stream commit can otherwise virtualize the anchor + // before the first mouse move. Keep the user bubble's long-press timer. + if !crate::markdown::selection::is_dragging() { + return; + } + self.selection_drag_position = Some(event.position); + self.discard_pending_viewport(); + self.user_collapse_scroll = None; + self.stop_automatic_scrolling(); + self.materialize_scroll_anchor(); + cx.notify(); + } + fn on_selection_mouse_up( &mut self, _event: &MouseUpEvent, _window: &mut Window, cx: &mut Context, ) { + let was_selecting = self.selection_drag_position.is_some(); self.stop_selection_scroll(); if let Some(_text) = crate::markdown::selection::end_active_drag() { // X11 middle-click paste parity, including the case where the @@ -2910,6 +2938,11 @@ impl Transcript { #[cfg(any(target_os = "linux", target_os = "freebsd"))] cx.write_to_primary(ClipboardItem::new_string(_text)); } + if was_selecting { + self.last_scroll_distance = self.distance_from_bottom(); + self.show_jump_button = self.last_scroll_distance > SCROLL_BUTTON_THRESHOLD_PX; + cx.notify(); + } } fn stop_selection_scroll(&mut self) { @@ -3090,11 +3123,30 @@ impl Transcript { /// Install the reservation before list layout. Its height follows the /// current viewport in that same pass, including window resizes. - fn update_runway_minimum(&mut self) { + fn update_runway_minimum(&mut self, cx: &gpui::App) { if let Some(ix) = self.own_turn_anchor_ix() { + // Revealing the prompt adds reading space; only reply growth + // consumes the runway. Include the same expansion tween in the + // reservation before layout, including its reverse on Show less. + let expansion = self.user_folds.get(&self.rows[ix].id).map_or(0.0, |fold| { + let target = if fold.open == Some(true) { + fold.user_expansion_height + } else { + 0.0 + }; + match fold.toggled_at { + Some(at) if !motion::reduced_motion(cx) && fold.duration_ms > 0 => { + let raw = (at.elapsed().as_secs_f32() * 1000.0 / fold.duration_ms as f32) + .clamp(0.0, 1.0); + let progress = user_resize_spec(fold.user_expansion_height).progress(raw); + motion::lerp(fold.user_expansion_height - target, target, progress) + } + _ => target, + } + }); self.list.set_tail_reservation(Some(( ix, - px(Self::own_send_inset(ix) - OWN_SEND_SCROLL_SLACK_PX), + px(Self::own_send_inset(ix) - OWN_SEND_SCROLL_SLACK_PX - expansion), ))); } else if self.own_turn.is_none() { self.list.set_tail_reservation(None); @@ -3150,7 +3202,11 @@ impl Transcript { let held = self.own_turn.take().is_some_and(|a| a.held); self.own_turn_last_tick = None; self.list.set_tail_reservation(None); - if held || self.pinned || self.distance_from_bottom() <= AT_BOTTOM_PX { + if held + || self.pinned + || (self.selection_drag_position.is_none() + && self.distance_from_bottom() <= AT_BOTTOM_PX) + { self.engage_pin(cx); } else { cx.notify(); @@ -3331,6 +3387,17 @@ impl Transcript { self.user_collapse_scroll = None; self.cancel_user_hold(); self.discard_pending_viewport(); + // An expanded prompt can be taller than the viewport. Its reservation + // is retained, but jumping should reveal the reply below that prompt. + if self.own_turn_anchor_ix().is_some_and(|ix| { + self.user_folds + .get(&self.rows[ix].id) + .is_some_and(|fold| fold.open == Some(true)) + }) { + self.release_own_turn_hold(); + self.engage_pin(cx); + return; + } // With a live runway, "bottom" IS the held position (the reservation // makes prompt-at-top and pad-bottom the same place): re-arm the hold // and glide back instead of destroying the runway (user spec — only @@ -3865,6 +3932,7 @@ impl Transcript { entry.epoch += 1; entry.toggled_at = Some(Instant::now()); entry.duration_ms = duration_ms; + entry.user_expansion_height = (full_h - collapsed_h).max(0.0); // Capture a screen-space anchor for the clicked row. We do not subtract // the removed height: that is only correct when the row's top is @@ -4180,6 +4248,10 @@ impl Transcript { || (measured > 0.0 && measured > collapsed_text_h + 0.5) || (measured == 0.0 && user_message_needs_collapse(&text)); let full_h = measured_h.get().max(collapsed_h); + if let Some(fold) = self.user_folds.get_mut(row_id) { + // Wrapping can change with the window width while expanded. + fold.user_expansion_height = (full_h - collapsed_h).max(0.0); + } let hold_key = row_id.clone(); let hold_height = measured_h.clone(); @@ -6776,7 +6848,7 @@ impl Render for Transcript { // region overlay): it must float just above the composer and paint // OVER the bottom fade gradient, which is a later sibling of this // outlet — an overlay here would be tinted by the fade. - self.update_runway_minimum(); + self.update_runway_minimum(cx); let list_el = list(self.list.clone(), cx.processor(Self::render_row)) .size_full() .with_sizing_behavior(gpui::ListSizingBehavior::Auto); @@ -6806,6 +6878,10 @@ impl Render for Transcript { .relative() .size_full() .min_h_0() + .on_mouse_down( + MouseButton::Left, + cx.listener(Self::on_selection_mouse_down), + ) .on_mouse_move(cx.listener(Self::on_selection_mouse_move)) .on_mouse_up(MouseButton::Left, cx.listener(Self::on_selection_mouse_up)) .on_mouse_up_out(MouseButton::Left, cx.listener(Self::on_selection_mouse_up)) @@ -7938,8 +8014,18 @@ mod tests { }); } + struct CachedTranscript(Entity); + + impl Render for CachedTranscript { + fn render(&mut self, _: &mut Window, _: &mut Context) -> impl IntoElement { + self.0 + .clone() + .cached(gpui::StyleRefinement::default().size_full()) + } + } + fn with_window( - test: impl FnOnce(Entity, gpui::WindowHandle, &mut gpui::App) + test: impl FnOnce(Entity, gpui::WindowHandle, &mut gpui::App) + 'static, ) { gpui_platform::headless().run(move |cx| { @@ -7954,10 +8040,14 @@ mod tests { ))), ..Default::default() }, - |_, cx| cx.new(|cx| Transcript::new(state, cx)), + |_, cx| { + let transcript = cx.new(|cx| Transcript::new(state, cx)); + cx.new(|_| CachedTranscript(transcript)) + }, ) .unwrap(); - test(window.entity(cx).unwrap(), window, cx); + let transcript = window.entity(cx).unwrap().read(cx).0.clone(); + test(transcript, window, cx); cx.spawn(async move |cx| { cx.update(|cx| cx.quit()); }) @@ -7965,7 +8055,7 @@ mod tests { }); } - fn draw(window: gpui::WindowHandle, cx: &mut gpui::App) { + fn draw(window: gpui::WindowHandle, cx: &mut gpui::App) { cx.update_window(window.into(), |_, window, cx| { window.refresh(); let _ = window.draw(cx); @@ -8075,7 +8165,7 @@ mod tests { fn tick( transcript: &Entity, - window: gpui::WindowHandle, + window: gpui::WindowHandle, cx: &mut gpui::App, ) { transcript.update(cx, |this, cx| { @@ -8091,7 +8181,7 @@ mod tests { draw(window, cx); } - fn wheel(window: gpui::WindowHandle, delta: f32, cx: &mut gpui::App) { + fn wheel(window: gpui::WindowHandle, delta: f32, cx: &mut gpui::App) { cx.update_window(window.into(), |_, window, cx| { window.dispatch_event( gpui::PlatformInput::ScrollWheel(gpui::ScrollWheelEvent { @@ -8105,6 +8195,186 @@ mod tests { .unwrap(); } + #[test] + fn selecting_live_tail_keeps_anchor_visible_during_a_stream_burst() { + with_window(|transcript, window, cx| { + let text = (0..40) + .map(|i| format!("Paragraph {i} has selectable response text.\n\n")) + .collect::(); + transcript.update(cx, |this, cx| { + feed( + this, + vec![assistant( + "reply", + MessageStatus::Streaming, + vec![text_part("text", &text)], + )], + cx, + ); + this.rail_enabled = false; + }); + draw(window, cx); + let bounds = render::selection_test_bounds("reply#text.39:39"); + let start = bounds.origin + gpui::point(px(1.0), px(8.0)); + cx.update_window(window.into(), |_, window, cx| { + window.dispatch_event( + gpui::PlatformInput::MouseDown(gpui::MouseDownEvent { + button: MouseButton::Left, + position: start, + click_count: 1, + ..Default::default() + }), + cx, + ); + }) + .unwrap(); + assert!(crate::markdown::selection::is_dragging()); + assert!( + !transcript.read(cx).pinned, + "text mouse-down must release following" + ); + assert!( + !transcript.read(cx).is_glued(), + "selection must materialize the viewport" + ); + transcript.update(cx, |this, cx| { + feed( + this, + vec![assistant( + "reply", + MessageStatus::Streaming, + vec![text_part("text", &(text.clone() + &text))], + )], + cx, + ); + }); + draw(window, cx); + cx.update_window(window.into(), |_, window, cx| { + window.dispatch_event( + gpui::PlatformInput::MouseMove(gpui::MouseMoveEvent { + position: start + gpui::point(px(90.0), px(0.0)), + pressed_button: Some(MouseButton::Left), + ..Default::default() + }), + cx, + ); + }) + .unwrap(); + assert!( + crate::markdown::selection::selected_text().is_some(), + "streaming must not virtualize the drag anchor before the first move" + ); + crate::markdown::selection::end_active_drag(); + crate::markdown::selection::clear_if_owner("reply#text.39:39"); + }); + } + + #[test] + fn active_reply_text_selection_survives_streaming_and_completion() { + with_window(|transcript, window, cx| { + transcript.update(cx, |this, cx| { + feed(this, vec![prompt("prompt")], cx); + this.rail_enabled = false; + this.state.update(cx, |state, _| { + state.sessions.push(zeron_proto::Session { + chat_id: "chat".into(), + device_id: "test".into(), + status: zeron_proto::SessionStatus::Working, + started_at: Some(chrono::Utc::now()), + updated_at: chrono::Utc::now(), + }) + }); + this.on_own_send("chat".into(), "prompt".into(), cx); + }); + let entries = |status, text: &str| { + vec![ + prompt("prompt"), + assistant("reply", status, vec![text_part("text", text)]), + ] + }; + transcript.update(cx, |this, cx| { + feed( + this, + entries(MessageStatus::Streaming, "Selectable response text."), + cx, + ) + }); + for _ in 0..80 { + tick(&transcript, window, cx); + } + let bounds = render::selection_test_bounds("reply#text.0:0"); + let start = bounds.origin + gpui::point(px(1.0), px(8.0)); + let end = start + gpui::point(px(90.0), px(0.0)); + cx.update_window(window.into(), |_, window, cx| { + window.dispatch_event( + gpui::PlatformInput::MouseDown(gpui::MouseDownEvent { + button: MouseButton::Left, + position: start, + click_count: 1, + ..Default::default() + }), + cx, + ); + }) + .unwrap(); + assert!(crate::markdown::selection::is_dragging()); + draw(window, cx); + cx.update_window(window.into(), |_, window, cx| { + window.dispatch_event( + gpui::PlatformInput::MouseMove(gpui::MouseMoveEvent { + position: end, + pressed_button: Some(MouseButton::Left), + ..Default::default() + }), + cx, + ); + }) + .unwrap(); + let selected = + crate::markdown::selection::selected_text().expect("active text must select"); + assert!(!selected.is_empty()); + transcript.update(cx, |this, cx| { + feed( + this, + entries( + MessageStatus::Streaming, + "Selectable response text. More output.", + ), + cx, + ) + }); + draw(window, cx); + assert_eq!( + crate::markdown::selection::selected_text(), + Some(selected.clone()) + ); + cx.update_window(window.into(), |_, window, cx| { + window.dispatch_event( + gpui::PlatformInput::MouseUp(gpui::MouseUpEvent { + button: MouseButton::Left, + position: end, + ..Default::default() + }), + cx, + ); + }) + .unwrap(); + transcript.update(cx, |this, cx| { + feed( + this, + entries( + MessageStatus::Complete, + "Selectable response text. More output.", + ), + cx, + ) + }); + draw(window, cx); + assert_eq!(crate::markdown::selection::selected_text(), Some(selected)); + crate::markdown::selection::clear_if_owner("reply#text.0:0"); + }); + } + #[test] fn runway_first_echo_starts_a_glide_in_an_empty_chat() { with_window(|transcript, window, cx| { @@ -8876,6 +9146,87 @@ mod tests { }); } + #[test] + fn expanding_streaming_prompt_does_not_retire_its_runway() { + with_window(|transcript, window, cx| { + let mut user = prompt("prompt"); + user.parts = vec![text_part("text", &"A long prompt line.\n".repeat(80))]; + transcript.update(cx, |this, cx| { + feed( + this, + vec![ + user.clone(), + assistant( + "reply", + MessageStatus::Streaming, + vec![text_part("text", "A short live reply.")], + ), + ], + cx, + ); + this.rail_enabled = false; + this.on_own_send("chat".into(), "prompt".into(), cx); + }); + for _ in 0..80 { + tick(&transcript, window, cx); + } + let before = transcript.read(cx).list.max_offset_for_scrollbar(); + let toggle = |this: &mut Transcript, cx: &mut Context| { + let full_h = this.user_heights["prompt"].get(); + this.toggle_user_fold( + "prompt".into(), + 0, + USER_LINE_HEIGHT * (USER_COLLAPSED_LINES + 1) as f32, + full_h, + true, + ); + this.user_folds.get_mut("prompt").unwrap().toggled_at = + Some(Instant::now() - Duration::from_secs(5)); + cx.notify(); + }; + transcript.update(cx, toggle); + draw(window, cx); + tick(&transcript, window, cx); + assert!( + transcript.read(cx).own_turn.is_some(), + "Show more must not consume the runway as assistant output" + ); + transcript.update(cx, toggle); + draw(window, cx); + tick(&transcript, window, cx); + assert!(transcript.read(cx).own_turn.is_some()); + assert!( + (transcript.read(cx).list.max_offset_for_scrollbar().y - before.y).abs() + <= px(1.0), + "Show less must restore the original reservation" + ); + transcript.update(cx, toggle); + draw(window, cx); + transcript.update(cx, |this, cx| { + feed( + this, + vec![ + user, + assistant( + "reply", + MessageStatus::Streaming, + vec![text_part("text", &"More assistant output.\n\n".repeat(80))], + ), + ], + cx, + ) + }); + transcript.update(cx, |this, cx| this.jump_to_bottom(cx)); + for _ in 0..80 { + tick(&transcript, window, cx); + } + assert!( + transcript.read(cx).own_turn.is_none(), + "real output must still retire the reservation while the prompt is expanded" + ); + }); + } + #[test] fn folding_releases_sent_turn_hold_without_removing_reservation() { with_transcript(|transcript, _| { diff --git a/docs/transcript-selection-regressions.md b/docs/transcript-selection-regressions.md new file mode 100644 index 0000000000..c814c9b0be --- /dev/null +++ b/docs/transcript-selection-regressions.md @@ -0,0 +1,56 @@ +# Streaming selection and prompt expansion + +Text selection now releases automatic viewport following at mouse-down and +materializes the current list position. A stream burst can no longer remove +the drag anchor before the first mouse move. Selection retains the runway; +edge scrolling and returning to the live tail use the existing navigation paths. + +Show more adds the prompt's revealed height to the reservation, using the +prompt's resize curve. Show less reverses it. Expanding the prompt therefore +does not count as assistant output or permanently retire the runway. Real +output still consumes the reservation, including when the prompt is expanded. + +## Native recordings + +- [Show more, Show less, and retained runway during streaming](https://github.com/user-attachments/assets/55162a94-8b6b-4725-9bfd-e67b173799c8) + (43 seconds): expand and collapse the prompt, apply downward wheel input, + select live text, and continue streaming with the reserved space intact. +- [Select an overflowing live reply, then resume scrolling](https://github.com/user-attachments/assets/aa4c50dc-a7c9-4b84-a02b-63da3809e877) + (38 seconds): hold a paragraph selection while more code arrives, release, + jump to the live tail, and scroll up and down during the same turn. + +These are native Linux/X11 recordings at 1280×800 using Xvfb and software +Vulkan, with four renderer workers. Both runs submitted through the composer +and replayed deterministic Claude adapter events without model API calls. +The long replay was briefly paused to position the pointer, then resumed at +mouse-down before the first drag movement, exercising the burst regression. +Both replay runs completed successfully. These are functional recordings, +not performance benchmarks or native macOS validation. + +The recorded release binary has SHA-256 +`d304b603cc56575742d05e1b09fc722e74e884ffdec191abac220f3f81990e5c`. + +## Automated validation + +All 620 release UI tests passed: + +```sh +cargo test --release --locked -p zeron-ui --lib -- --test-threads=1 +``` + +The new headless tests use the cached transcript view and real mouse events. +They cover a burst between mouse-down and the first drag movement, selection +across streaming and completion, and expanding/collapsing a live prompt before +jumping to enough output to retire the reservation. The burst and Show more +regressions both failed before their respective fixes. + +The existing suite also covers runway geometry, wheel escape and re-sticking, +resizes, completion shrinkage, viewport restoration, and selection edge scrolling. +The native app also built with `cargo build --release --locked -p zeron`. + +Replay with `scripts/resource-profile.mjs` and `scripts/replay-claude.py`: +use `scripts/fixtures/transcript-selection-stream.jsonl` at 1200 ms per delta +for the short turn, or `scripts/fixtures/resource-stream.jsonl` at 300 ms per +delta for overflow. Set `ZERON_PROFILE_SUBMIT_UI=1`; the short-turn prompt +must be long enough to expose Show more. The recordings used a 1939-character +prompt for that case. diff --git a/scripts/fixtures/README.md b/scripts/fixtures/README.md index 4d81193f57..2b639df9f5 100644 --- a/scripts/fixtures/README.md +++ b/scripts/fixtures/README.md @@ -15,3 +15,7 @@ for build settings, commands and measured results. `runway-short-stream.jsonl` is a synthetic short reply in 24-character chunks. It keeps the own-send runway active through completion. Use a 400 ms replay delay for the [runway performance comparison](../../docs/performance-runway-scroll.md). + +`transcript-selection-stream.jsonl` is a synthetic short reply in six-character +chunks. A 1200 ms replay delay leaves time to expand/collapse a long prompt and +select live text. See the [native regression recordings](../../docs/transcript-selection-regressions.md). diff --git a/scripts/fixtures/transcript-selection-stream.jsonl b/scripts/fixtures/transcript-selection-stream.jsonl new file mode 100644 index 0000000000..022cd6a66f --- /dev/null +++ b/scripts/fixtures/transcript-selection-stream.jsonl @@ -0,0 +1,53 @@ +{"event": {"type": "textDelta", "text": "## Liv"}} +{"event": {"type": "textDelta", "text": "e repl"}} +{"event": {"type": "textDelta", "text": "y\n\nSel"}} +{"event": {"type": "textDelta", "text": "ect th"}} +{"event": {"type": "textDelta", "text": "is sen"}} +{"event": {"type": "textDelta", "text": "tence "}} +{"event": {"type": "textDelta", "text": "while "}} +{"event": {"type": "textDelta", "text": "the tu"}} +{"event": {"type": "textDelta", "text": "rn is "}} +{"event": {"type": "textDelta", "text": "still "}} +{"event": {"type": "textDelta", "text": "workin"}} +{"event": {"type": "textDelta", "text": "g. The"}} +{"event": {"type": "textDelta", "text": " selec"}} +{"event": {"type": "textDelta", "text": "tion s"}} +{"event": {"type": "textDelta", "text": "hould "}} +{"event": {"type": "textDelta", "text": "stay i"}} +{"event": {"type": "textDelta", "text": "n plac"}} +{"event": {"type": "textDelta", "text": "e as n"}} +{"event": {"type": "textDelta", "text": "ew tex"}} +{"event": {"type": "textDelta", "text": "t arri"}} +{"event": {"type": "textDelta", "text": "ves.\n\n"}} +{"event": {"type": "textDelta", "text": "The pr"}} +{"event": {"type": "textDelta", "text": "ompt c"}} +{"event": {"type": "textDelta", "text": "an exp"}} +{"event": {"type": "textDelta", "text": "and an"}} +{"event": {"type": "textDelta", "text": "d coll"}} +{"event": {"type": "textDelta", "text": "apse w"}} +{"event": {"type": "textDelta", "text": "ithout"}} +{"event": {"type": "textDelta", "text": " losin"}} +{"event": {"type": "textDelta", "text": "g its "}} +{"event": {"type": "textDelta", "text": "reserv"}} +{"event": {"type": "textDelta", "text": "ed spa"}} +{"event": {"type": "textDelta", "text": "ce. Ne"}} +{"event": {"type": "textDelta", "text": "w outp"}} +{"event": {"type": "textDelta", "text": "ut con"}} +{"event": {"type": "textDelta", "text": "tinues"}} +{"event": {"type": "textDelta", "text": " below"}} +{"event": {"type": "textDelta", "text": " it. S"}} +{"event": {"type": "textDelta", "text": "treami"}} +{"event": {"type": "textDelta", "text": "ng con"}} +{"event": {"type": "textDelta", "text": "tinues"}} +{"event": {"type": "textDelta", "text": " here,"}} +{"event": {"type": "textDelta", "text": " leavi"}} +{"event": {"type": "textDelta", "text": "ng tim"}} +{"event": {"type": "textDelta", "text": "e to i"}} +{"event": {"type": "textDelta", "text": "nspect"}} +{"event": {"type": "textDelta", "text": " the p"}} +{"event": {"type": "textDelta", "text": "rompt "}} +{"event": {"type": "textDelta", "text": "and se"}} +{"event": {"type": "textDelta", "text": "lect t"}} +{"event": {"type": "textDelta", "text": "he rep"}} +{"event": {"type": "textDelta", "text": "ly. "}} +{"event": {"type": "done", "status": "completed"}} From c81678abcc9a596652b1f326b1c1cfa59a95d9a0 Mon Sep 17 00:00:00 2001 From: wing-anara Date: Sun, 6 Sep 2026 19:38:05 +0000 Subject: [PATCH 2/4] Recover macOS redraws after frame-source failure and wake --- .github/workflows/ui-tests.yml | 26 +++++++++++++ Cargo.lock | 46 +++++++++++------------ Cargo.toml | 6 +-- docs/macos-frame-recovery.md | 68 ++++++++++++++++++++++++++++++++++ 4 files changed, 120 insertions(+), 26 deletions(-) create mode 100644 docs/macos-frame-recovery.md diff --git a/.github/workflows/ui-tests.yml b/.github/workflows/ui-tests.yml index cb20161f62..a1292f6d49 100644 --- a/.github/workflows/ui-tests.yml +++ b/.github/workflows/ui-tests.yml @@ -44,3 +44,29 @@ jobs: - uses: Swatinem/rust-cache@v2 - name: UI and headless layout regressions run: cargo test --release --locked -p zeron-ui --lib -- --test-threads=1 + + macos-frame-recovery: + runs-on: macos-latest + timeout-minutes: 45 + steps: + - uses: actions/checkout@v4 + - name: Rust toolchain + run: rustup show active-toolchain || rustup toolchain install + - uses: Swatinem/rust-cache@v2 + with: + workspaces: | + . + frame-source-tests + - name: Check macOS application + run: cargo check --release --locked -p zeron + - name: Check out pinned GPUI for native frame-source tests + shell: bash + run: | + rev=$(python3 -c 'import tomllib; print(tomllib.load(open("Cargo.toml", "rb"))["workspace"]["dependencies"]["gpui"]["rev"])') + git init frame-source-tests + git -C frame-source-tests remote add origin https://github.com/zeronsh/zui + git -C frame-source-tests fetch --depth=1 origin "$rev" + git -C frame-source-tests checkout --detach FETCH_HEAD + - name: Native frame-source recovery regression + working-directory: frame-source-tests + run: cargo test --release --locked -p gpui_macos --lib display_link::tests -- --test-threads=1 diff --git a/Cargo.lock b/Cargo.lock index e26a3ac238..c62235be33 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1180,7 +1180,7 @@ dependencies = [ [[package]] name = "collections" version = "0.1.0" -source = "git+https://github.com/zeronsh/zui?rev=8a32c0e26a17b9fab3aa9c2dbb79ed2d28ab2dfe#8a32c0e26a17b9fab3aa9c2dbb79ed2d28ab2dfe" +source = "git+https://github.com/zeronsh/zui?rev=e9bffe0d979133bdfbd3783b252e1b08545b16d2#e9bffe0d979133bdfbd3783b252e1b08545b16d2" dependencies = [ "gpui_util", "indexmap", @@ -1667,7 +1667,7 @@ dependencies = [ [[package]] name = "derive_refineable" version = "0.1.0" -source = "git+https://github.com/zeronsh/zui?rev=8a32c0e26a17b9fab3aa9c2dbb79ed2d28ab2dfe#8a32c0e26a17b9fab3aa9c2dbb79ed2d28ab2dfe" +source = "git+https://github.com/zeronsh/zui?rev=e9bffe0d979133bdfbd3783b252e1b08545b16d2#e9bffe0d979133bdfbd3783b252e1b08545b16d2" dependencies = [ "proc-macro2", "quote", @@ -2554,7 +2554,7 @@ dependencies = [ [[package]] name = "gpui" version = "0.2.2" -source = "git+https://github.com/zeronsh/zui?rev=8a32c0e26a17b9fab3aa9c2dbb79ed2d28ab2dfe#8a32c0e26a17b9fab3aa9c2dbb79ed2d28ab2dfe" +source = "git+https://github.com/zeronsh/zui?rev=e9bffe0d979133bdfbd3783b252e1b08545b16d2#e9bffe0d979133bdfbd3783b252e1b08545b16d2" dependencies = [ "accesskit", "anyhow", @@ -2640,7 +2640,7 @@ dependencies = [ [[package]] name = "gpui_linux" version = "0.1.0" -source = "git+https://github.com/zeronsh/zui?rev=8a32c0e26a17b9fab3aa9c2dbb79ed2d28ab2dfe#8a32c0e26a17b9fab3aa9c2dbb79ed2d28ab2dfe" +source = "git+https://github.com/zeronsh/zui?rev=e9bffe0d979133bdfbd3783b252e1b08545b16d2#e9bffe0d979133bdfbd3783b252e1b08545b16d2" dependencies = [ "accesskit", "accesskit_unix", @@ -2692,7 +2692,7 @@ dependencies = [ [[package]] name = "gpui_macos" version = "0.1.0" -source = "git+https://github.com/zeronsh/zui?rev=8a32c0e26a17b9fab3aa9c2dbb79ed2d28ab2dfe#8a32c0e26a17b9fab3aa9c2dbb79ed2d28ab2dfe" +source = "git+https://github.com/zeronsh/zui?rev=e9bffe0d979133bdfbd3783b252e1b08545b16d2#e9bffe0d979133bdfbd3783b252e1b08545b16d2" dependencies = [ "accesskit", "accesskit_macos", @@ -2741,7 +2741,7 @@ dependencies = [ [[package]] name = "gpui_macros" version = "0.1.0" -source = "git+https://github.com/zeronsh/zui?rev=8a32c0e26a17b9fab3aa9c2dbb79ed2d28ab2dfe#8a32c0e26a17b9fab3aa9c2dbb79ed2d28ab2dfe" +source = "git+https://github.com/zeronsh/zui?rev=e9bffe0d979133bdfbd3783b252e1b08545b16d2#e9bffe0d979133bdfbd3783b252e1b08545b16d2" dependencies = [ "heck 0.5.0", "proc-macro2", @@ -2752,7 +2752,7 @@ dependencies = [ [[package]] name = "gpui_platform" version = "0.1.0" -source = "git+https://github.com/zeronsh/zui?rev=8a32c0e26a17b9fab3aa9c2dbb79ed2d28ab2dfe#8a32c0e26a17b9fab3aa9c2dbb79ed2d28ab2dfe" +source = "git+https://github.com/zeronsh/zui?rev=e9bffe0d979133bdfbd3783b252e1b08545b16d2#e9bffe0d979133bdfbd3783b252e1b08545b16d2" dependencies = [ "console_error_panic_hook", "gpui", @@ -2765,7 +2765,7 @@ dependencies = [ [[package]] name = "gpui_shared_string" version = "0.1.0" -source = "git+https://github.com/zeronsh/zui?rev=8a32c0e26a17b9fab3aa9c2dbb79ed2d28ab2dfe#8a32c0e26a17b9fab3aa9c2dbb79ed2d28ab2dfe" +source = "git+https://github.com/zeronsh/zui?rev=e9bffe0d979133bdfbd3783b252e1b08545b16d2#e9bffe0d979133bdfbd3783b252e1b08545b16d2" dependencies = [ "schemars", "serde", @@ -2775,7 +2775,7 @@ dependencies = [ [[package]] name = "gpui_tokio" version = "0.1.0" -source = "git+https://github.com/zeronsh/zui?rev=8a32c0e26a17b9fab3aa9c2dbb79ed2d28ab2dfe#8a32c0e26a17b9fab3aa9c2dbb79ed2d28ab2dfe" +source = "git+https://github.com/zeronsh/zui?rev=e9bffe0d979133bdfbd3783b252e1b08545b16d2#e9bffe0d979133bdfbd3783b252e1b08545b16d2" dependencies = [ "anyhow", "gpui", @@ -2786,7 +2786,7 @@ dependencies = [ [[package]] name = "gpui_util" version = "0.1.0" -source = "git+https://github.com/zeronsh/zui?rev=8a32c0e26a17b9fab3aa9c2dbb79ed2d28ab2dfe#8a32c0e26a17b9fab3aa9c2dbb79ed2d28ab2dfe" +source = "git+https://github.com/zeronsh/zui?rev=e9bffe0d979133bdfbd3783b252e1b08545b16d2#e9bffe0d979133bdfbd3783b252e1b08545b16d2" dependencies = [ "anyhow", "log", @@ -2796,7 +2796,7 @@ dependencies = [ [[package]] name = "gpui_web" version = "0.1.0" -source = "git+https://github.com/zeronsh/zui?rev=8a32c0e26a17b9fab3aa9c2dbb79ed2d28ab2dfe#8a32c0e26a17b9fab3aa9c2dbb79ed2d28ab2dfe" +source = "git+https://github.com/zeronsh/zui?rev=e9bffe0d979133bdfbd3783b252e1b08545b16d2#e9bffe0d979133bdfbd3783b252e1b08545b16d2" dependencies = [ "anyhow", "console_error_panic_hook", @@ -2820,7 +2820,7 @@ dependencies = [ [[package]] name = "gpui_wgpu" version = "0.1.0" -source = "git+https://github.com/zeronsh/zui?rev=8a32c0e26a17b9fab3aa9c2dbb79ed2d28ab2dfe#8a32c0e26a17b9fab3aa9c2dbb79ed2d28ab2dfe" +source = "git+https://github.com/zeronsh/zui?rev=e9bffe0d979133bdfbd3783b252e1b08545b16d2#e9bffe0d979133bdfbd3783b252e1b08545b16d2" dependencies = [ "anyhow", "bytemuck", @@ -2849,7 +2849,7 @@ dependencies = [ [[package]] name = "gpui_windows" version = "0.1.0" -source = "git+https://github.com/zeronsh/zui?rev=8a32c0e26a17b9fab3aa9c2dbb79ed2d28ab2dfe#8a32c0e26a17b9fab3aa9c2dbb79ed2d28ab2dfe" +source = "git+https://github.com/zeronsh/zui?rev=e9bffe0d979133bdfbd3783b252e1b08545b16d2#e9bffe0d979133bdfbd3783b252e1b08545b16d2" dependencies = [ "accesskit", "accesskit_windows", @@ -3108,7 +3108,7 @@ dependencies = [ [[package]] name = "http_client" version = "0.1.0" -source = "git+https://github.com/zeronsh/zui?rev=8a32c0e26a17b9fab3aa9c2dbb79ed2d28ab2dfe#8a32c0e26a17b9fab3aa9c2dbb79ed2d28ab2dfe" +source = "git+https://github.com/zeronsh/zui?rev=e9bffe0d979133bdfbd3783b252e1b08545b16d2#e9bffe0d979133bdfbd3783b252e1b08545b16d2" dependencies = [ "anyhow", "async-compression", @@ -4160,7 +4160,7 @@ checksum = "490cc448043f947bae3cbee9c203358d62dbee0db12107a74be5c30ccfd09771" [[package]] name = "media" version = "0.1.0" -source = "git+https://github.com/zeronsh/zui?rev=8a32c0e26a17b9fab3aa9c2dbb79ed2d28ab2dfe#8a32c0e26a17b9fab3aa9c2dbb79ed2d28ab2dfe" +source = "git+https://github.com/zeronsh/zui?rev=e9bffe0d979133bdfbd3783b252e1b08545b16d2#e9bffe0d979133bdfbd3783b252e1b08545b16d2" dependencies = [ "anyhow", "bindgen", @@ -4975,7 +4975,7 @@ checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" [[package]] name = "perf" version = "0.1.0" -source = "git+https://github.com/zeronsh/zui?rev=8a32c0e26a17b9fab3aa9c2dbb79ed2d28ab2dfe#8a32c0e26a17b9fab3aa9c2dbb79ed2d28ab2dfe" +source = "git+https://github.com/zeronsh/zui?rev=e9bffe0d979133bdfbd3783b252e1b08545b16d2#e9bffe0d979133bdfbd3783b252e1b08545b16d2" dependencies = [ "collections", "serde", @@ -5889,7 +5889,7 @@ dependencies = [ [[package]] name = "refineable" version = "0.1.0" -source = "git+https://github.com/zeronsh/zui?rev=8a32c0e26a17b9fab3aa9c2dbb79ed2d28ab2dfe#8a32c0e26a17b9fab3aa9c2dbb79ed2d28ab2dfe" +source = "git+https://github.com/zeronsh/zui?rev=e9bffe0d979133bdfbd3783b252e1b08545b16d2#e9bffe0d979133bdfbd3783b252e1b08545b16d2" dependencies = [ "derive_refineable", ] @@ -6192,7 +6192,7 @@ dependencies = [ [[package]] name = "scheduler" version = "0.1.0" -source = "git+https://github.com/zeronsh/zui?rev=8a32c0e26a17b9fab3aa9c2dbb79ed2d28ab2dfe#8a32c0e26a17b9fab3aa9c2dbb79ed2d28ab2dfe" +source = "git+https://github.com/zeronsh/zui?rev=e9bffe0d979133bdfbd3783b252e1b08545b16d2#e9bffe0d979133bdfbd3783b252e1b08545b16d2" dependencies = [ "async-task", "backtrace", @@ -6805,7 +6805,7 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "sum_tree" version = "0.1.0" -source = "git+https://github.com/zeronsh/zui?rev=8a32c0e26a17b9fab3aa9c2dbb79ed2d28ab2dfe#8a32c0e26a17b9fab3aa9c2dbb79ed2d28ab2dfe" +source = "git+https://github.com/zeronsh/zui?rev=e9bffe0d979133bdfbd3783b252e1b08545b16d2#e9bffe0d979133bdfbd3783b252e1b08545b16d2" dependencies = [ "heapless 0.9.3", "log", @@ -7988,7 +7988,7 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "util_macros" version = "0.1.0" -source = "git+https://github.com/zeronsh/zui?rev=8a32c0e26a17b9fab3aa9c2dbb79ed2d28ab2dfe#8a32c0e26a17b9fab3aa9c2dbb79ed2d28ab2dfe" +source = "git+https://github.com/zeronsh/zui?rev=e9bffe0d979133bdfbd3783b252e1b08545b16d2#e9bffe0d979133bdfbd3783b252e1b08545b16d2" dependencies = [ "perf", "quote", @@ -9703,7 +9703,7 @@ dependencies = [ [[package]] name = "zlog" version = "0.1.0" -source = "git+https://github.com/zeronsh/zui?rev=8a32c0e26a17b9fab3aa9c2dbb79ed2d28ab2dfe#8a32c0e26a17b9fab3aa9c2dbb79ed2d28ab2dfe" +source = "git+https://github.com/zeronsh/zui?rev=e9bffe0d979133bdfbd3783b252e1b08545b16d2#e9bffe0d979133bdfbd3783b252e1b08545b16d2" dependencies = [ "anyhow", "chrono", @@ -9720,7 +9720,7 @@ checksum = "29666d0abbfad1e3dc4dcf6144730dd3a3ab225bbbdac83319345b1b44ccfc1b" [[package]] name = "ztracing" version = "0.1.0" -source = "git+https://github.com/zeronsh/zui?rev=8a32c0e26a17b9fab3aa9c2dbb79ed2d28ab2dfe#8a32c0e26a17b9fab3aa9c2dbb79ed2d28ab2dfe" +source = "git+https://github.com/zeronsh/zui?rev=e9bffe0d979133bdfbd3783b252e1b08545b16d2#e9bffe0d979133bdfbd3783b252e1b08545b16d2" dependencies = [ "tracing", "tracing-subscriber", @@ -9731,7 +9731,7 @@ dependencies = [ [[package]] name = "ztracing_macro" version = "0.1.0" -source = "git+https://github.com/zeronsh/zui?rev=8a32c0e26a17b9fab3aa9c2dbb79ed2d28ab2dfe#8a32c0e26a17b9fab3aa9c2dbb79ed2d28ab2dfe" +source = "git+https://github.com/zeronsh/zui?rev=e9bffe0d979133bdfbd3783b252e1b08545b16d2#e9bffe0d979133bdfbd3783b252e1b08545b16d2" [[package]] name = "zune-core" diff --git a/Cargo.toml b/Cargo.toml index 05ba0e97e6..3591db6f97 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -61,14 +61,14 @@ loro-protocol = "0.3" # stopped vending CABackdropLayer for Selection — window blur went dead); # b68970e rasterizes BackdropBlur in the wgpu renderer (frosted floats on # Linux — the Metal path's snapshot/blur/composite, ported). -gpui = { git = "https://github.com/zeronsh/zui", rev = "8a32c0e26a17b9fab3aa9c2dbb79ed2d28ab2dfe" } -gpui_platform = { git = "https://github.com/zeronsh/zui", rev = "8a32c0e26a17b9fab3aa9c2dbb79ed2d28ab2dfe", features = [ +gpui = { git = "https://github.com/zeronsh/zui", rev = "e9bffe0d979133bdfbd3783b252e1b08545b16d2" } +gpui_platform = { git = "https://github.com/zeronsh/zui", rev = "e9bffe0d979133bdfbd3783b252e1b08545b16d2", features = [ "wayland", "x11", "font-kit", "runtime_shaders", ] } -gpui_tokio = { git = "https://github.com/zeronsh/zui", rev = "8a32c0e26a17b9fab3aa9c2dbb79ed2d28ab2dfe" } +gpui_tokio = { git = "https://github.com/zeronsh/zui", rev = "e9bffe0d979133bdfbd3783b252e1b08545b16d2" } # diffs similar = "2" diff --git a/docs/macos-frame-recovery.md b/docs/macos-frame-recovery.md new file mode 100644 index 0000000000..55c5bba468 --- /dev/null +++ b/docs/macos-frame-recovery.md @@ -0,0 +1,68 @@ +# macOS display freeze investigation + +Reported symptom: after the app has been open for a while, its contents stop +updating. The user can still send a message and does not see a beachball; +restarting the app restores rendering. Sleep/wake is a suspected trigger. + +## Identified failure + +The pinned macOS backend (`8a32c0e`) queues a display-link restart only when +`frame_requested` changes from false to true. `WindowFrameSource::start` set +that flag before its fallible subscription; stopping a source did not clear +it. A failed restart therefore left no subscription but a true request flag. +Later input and streaming updates changed application state while their +invalidations could never queue another restart. + +The backend also only registered for system wake when the application supplied +an optional callback. Comet supplies none, and window frame sources were never +explicitly reconnected on wake. CoreVideo can stop across session changes +without the expected window screen/occlusion notifications. This is consistent +with the report, but the original incident has not been reproduced on macOS +hardware or confirmed by a process sample. + +The fix is in [zeronsh/zui#3](https://github.com/zeronsh/zui/pull/3), pinned by +this PR. It clears the request flag on every stop, including the no-screen +path before a source exists, and sets it only after a successful subscription. +CoreVideo start/stop errors are accepted only when its actual running state +already matches the requested state. System wake, screen wake, and session +activation reconnect the window sources outside AppKit callbacks and locks. +All subscribers stop before any restart, so multiple windows sharing one +display really restart its shared link. + +Idle parking, vsync pacing, the immortal display-link lifetime, and registry +lock ordering remain in place. This adds no polling timer or continuous +redrawing. The change is confined to the macOS backend; it does not alter +transcript scrolling or runway geometry. + +## Validation + +The PR's macOS CI job compiles the application and runs the native +`stopped_frame_source_allows_invalidation_to_restart_it` regression in the +pinned dependency. It creates a real dispatch source and checks that stopping +it without a subscription clears the latch, allowing subsequent invalidation +to schedule another restart. The old stop implementation leaves the latch set. + +The existing Linux UI suite covers the transcript changes. Its recordings are +linked in [transcript-selection-regressions.md](transcript-selection-regressions.md); +they are not evidence of macOS sleep/wake recovery. + +Hardware follow-up: leave an idle window and an actively streaming window open, +sleep and wake the Mac repeatedly, then repeat with display sleep and session +switching. In each case check new composer text, message submission, live output, +selection, and scrolling. Repeat with two windows on the same display and with +an external monitor removed/reconnected. Verify that idle CPU returns to its +previous level after each cycle. + +If the original freeze persists, capture Activity Monitor's **Sample Process** +for the app before restarting, together with its rotating app log. This will +distinguish another frame-source failure from a blocked main thread. + +## References + +- [Chromium's CVDisplayLink start/stop handling](https://chromium.googlesource.com/chromium/src/+/refs/heads/main/ui/display/mac/cv_display_link_mac.mm) + checks actual running state when CoreVideo returns an error. +- [Mozilla's display-link freeze investigation](https://bugzilla.mozilla.org/show_bug.cgi?id=1422855#c97) + documents input processing with stalled display updates across user switches. +- Apple documents [screen wake](https://developer.apple.com/documentation/appkit/nsworkspace/screensdidwakenotification) + and [session activation](https://developer.apple.com/documentation/appkit/nsworkspace/sessiondidbecomeactivenotification) + notifications through the workspace notification center. From 4f1ed15a3b5ea32b306887b2853af2e5681d10e5 Mon Sep 17 00:00:00 2001 From: wing-anara Date: Sun, 6 Sep 2026 19:44:44 +0000 Subject: [PATCH 3/4] Cover failed native frame subscriptions and include panel wake recovery --- .github/workflows/ui-tests.yml | 16 ++++++------ Cargo.lock | 46 +++++++++++++++++----------------- Cargo.toml | 6 ++--- docs/macos-frame-recovery.md | 10 +++++--- 4 files changed, 40 insertions(+), 38 deletions(-) diff --git a/.github/workflows/ui-tests.yml b/.github/workflows/ui-tests.yml index a1292f6d49..173e0bb2ed 100644 --- a/.github/workflows/ui-tests.yml +++ b/.github/workflows/ui-tests.yml @@ -52,13 +52,6 @@ jobs: - uses: actions/checkout@v4 - name: Rust toolchain run: rustup show active-toolchain || rustup toolchain install - - uses: Swatinem/rust-cache@v2 - with: - workspaces: | - . - frame-source-tests - - name: Check macOS application - run: cargo check --release --locked -p zeron - name: Check out pinned GPUI for native frame-source tests shell: bash run: | @@ -67,6 +60,13 @@ jobs: git -C frame-source-tests remote add origin https://github.com/zeronsh/zui git -C frame-source-tests fetch --depth=1 origin "$rev" git -C frame-source-tests checkout --detach FETCH_HEAD + - uses: Swatinem/rust-cache@v2 + with: + workspaces: | + . + frame-source-tests + - name: Check macOS application + run: cargo check --release --locked -p zeron - name: Native frame-source recovery regression working-directory: frame-source-tests - run: cargo test --release --locked -p gpui_macos --lib display_link::tests -- --test-threads=1 + run: cargo test --release --locked -p gpui_macos --test frame_source_recovery diff --git a/Cargo.lock b/Cargo.lock index c62235be33..60657b39f1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1180,7 +1180,7 @@ dependencies = [ [[package]] name = "collections" version = "0.1.0" -source = "git+https://github.com/zeronsh/zui?rev=e9bffe0d979133bdfbd3783b252e1b08545b16d2#e9bffe0d979133bdfbd3783b252e1b08545b16d2" +source = "git+https://github.com/zeronsh/zui?rev=d70a4043cb7f7735b4f23c5abe293c2080b031da#d70a4043cb7f7735b4f23c5abe293c2080b031da" dependencies = [ "gpui_util", "indexmap", @@ -1667,7 +1667,7 @@ dependencies = [ [[package]] name = "derive_refineable" version = "0.1.0" -source = "git+https://github.com/zeronsh/zui?rev=e9bffe0d979133bdfbd3783b252e1b08545b16d2#e9bffe0d979133bdfbd3783b252e1b08545b16d2" +source = "git+https://github.com/zeronsh/zui?rev=d70a4043cb7f7735b4f23c5abe293c2080b031da#d70a4043cb7f7735b4f23c5abe293c2080b031da" dependencies = [ "proc-macro2", "quote", @@ -2554,7 +2554,7 @@ dependencies = [ [[package]] name = "gpui" version = "0.2.2" -source = "git+https://github.com/zeronsh/zui?rev=e9bffe0d979133bdfbd3783b252e1b08545b16d2#e9bffe0d979133bdfbd3783b252e1b08545b16d2" +source = "git+https://github.com/zeronsh/zui?rev=d70a4043cb7f7735b4f23c5abe293c2080b031da#d70a4043cb7f7735b4f23c5abe293c2080b031da" dependencies = [ "accesskit", "anyhow", @@ -2640,7 +2640,7 @@ dependencies = [ [[package]] name = "gpui_linux" version = "0.1.0" -source = "git+https://github.com/zeronsh/zui?rev=e9bffe0d979133bdfbd3783b252e1b08545b16d2#e9bffe0d979133bdfbd3783b252e1b08545b16d2" +source = "git+https://github.com/zeronsh/zui?rev=d70a4043cb7f7735b4f23c5abe293c2080b031da#d70a4043cb7f7735b4f23c5abe293c2080b031da" dependencies = [ "accesskit", "accesskit_unix", @@ -2692,7 +2692,7 @@ dependencies = [ [[package]] name = "gpui_macos" version = "0.1.0" -source = "git+https://github.com/zeronsh/zui?rev=e9bffe0d979133bdfbd3783b252e1b08545b16d2#e9bffe0d979133bdfbd3783b252e1b08545b16d2" +source = "git+https://github.com/zeronsh/zui?rev=d70a4043cb7f7735b4f23c5abe293c2080b031da#d70a4043cb7f7735b4f23c5abe293c2080b031da" dependencies = [ "accesskit", "accesskit_macos", @@ -2741,7 +2741,7 @@ dependencies = [ [[package]] name = "gpui_macros" version = "0.1.0" -source = "git+https://github.com/zeronsh/zui?rev=e9bffe0d979133bdfbd3783b252e1b08545b16d2#e9bffe0d979133bdfbd3783b252e1b08545b16d2" +source = "git+https://github.com/zeronsh/zui?rev=d70a4043cb7f7735b4f23c5abe293c2080b031da#d70a4043cb7f7735b4f23c5abe293c2080b031da" dependencies = [ "heck 0.5.0", "proc-macro2", @@ -2752,7 +2752,7 @@ dependencies = [ [[package]] name = "gpui_platform" version = "0.1.0" -source = "git+https://github.com/zeronsh/zui?rev=e9bffe0d979133bdfbd3783b252e1b08545b16d2#e9bffe0d979133bdfbd3783b252e1b08545b16d2" +source = "git+https://github.com/zeronsh/zui?rev=d70a4043cb7f7735b4f23c5abe293c2080b031da#d70a4043cb7f7735b4f23c5abe293c2080b031da" dependencies = [ "console_error_panic_hook", "gpui", @@ -2765,7 +2765,7 @@ dependencies = [ [[package]] name = "gpui_shared_string" version = "0.1.0" -source = "git+https://github.com/zeronsh/zui?rev=e9bffe0d979133bdfbd3783b252e1b08545b16d2#e9bffe0d979133bdfbd3783b252e1b08545b16d2" +source = "git+https://github.com/zeronsh/zui?rev=d70a4043cb7f7735b4f23c5abe293c2080b031da#d70a4043cb7f7735b4f23c5abe293c2080b031da" dependencies = [ "schemars", "serde", @@ -2775,7 +2775,7 @@ dependencies = [ [[package]] name = "gpui_tokio" version = "0.1.0" -source = "git+https://github.com/zeronsh/zui?rev=e9bffe0d979133bdfbd3783b252e1b08545b16d2#e9bffe0d979133bdfbd3783b252e1b08545b16d2" +source = "git+https://github.com/zeronsh/zui?rev=d70a4043cb7f7735b4f23c5abe293c2080b031da#d70a4043cb7f7735b4f23c5abe293c2080b031da" dependencies = [ "anyhow", "gpui", @@ -2786,7 +2786,7 @@ dependencies = [ [[package]] name = "gpui_util" version = "0.1.0" -source = "git+https://github.com/zeronsh/zui?rev=e9bffe0d979133bdfbd3783b252e1b08545b16d2#e9bffe0d979133bdfbd3783b252e1b08545b16d2" +source = "git+https://github.com/zeronsh/zui?rev=d70a4043cb7f7735b4f23c5abe293c2080b031da#d70a4043cb7f7735b4f23c5abe293c2080b031da" dependencies = [ "anyhow", "log", @@ -2796,7 +2796,7 @@ dependencies = [ [[package]] name = "gpui_web" version = "0.1.0" -source = "git+https://github.com/zeronsh/zui?rev=e9bffe0d979133bdfbd3783b252e1b08545b16d2#e9bffe0d979133bdfbd3783b252e1b08545b16d2" +source = "git+https://github.com/zeronsh/zui?rev=d70a4043cb7f7735b4f23c5abe293c2080b031da#d70a4043cb7f7735b4f23c5abe293c2080b031da" dependencies = [ "anyhow", "console_error_panic_hook", @@ -2820,7 +2820,7 @@ dependencies = [ [[package]] name = "gpui_wgpu" version = "0.1.0" -source = "git+https://github.com/zeronsh/zui?rev=e9bffe0d979133bdfbd3783b252e1b08545b16d2#e9bffe0d979133bdfbd3783b252e1b08545b16d2" +source = "git+https://github.com/zeronsh/zui?rev=d70a4043cb7f7735b4f23c5abe293c2080b031da#d70a4043cb7f7735b4f23c5abe293c2080b031da" dependencies = [ "anyhow", "bytemuck", @@ -2849,7 +2849,7 @@ dependencies = [ [[package]] name = "gpui_windows" version = "0.1.0" -source = "git+https://github.com/zeronsh/zui?rev=e9bffe0d979133bdfbd3783b252e1b08545b16d2#e9bffe0d979133bdfbd3783b252e1b08545b16d2" +source = "git+https://github.com/zeronsh/zui?rev=d70a4043cb7f7735b4f23c5abe293c2080b031da#d70a4043cb7f7735b4f23c5abe293c2080b031da" dependencies = [ "accesskit", "accesskit_windows", @@ -3108,7 +3108,7 @@ dependencies = [ [[package]] name = "http_client" version = "0.1.0" -source = "git+https://github.com/zeronsh/zui?rev=e9bffe0d979133bdfbd3783b252e1b08545b16d2#e9bffe0d979133bdfbd3783b252e1b08545b16d2" +source = "git+https://github.com/zeronsh/zui?rev=d70a4043cb7f7735b4f23c5abe293c2080b031da#d70a4043cb7f7735b4f23c5abe293c2080b031da" dependencies = [ "anyhow", "async-compression", @@ -4160,7 +4160,7 @@ checksum = "490cc448043f947bae3cbee9c203358d62dbee0db12107a74be5c30ccfd09771" [[package]] name = "media" version = "0.1.0" -source = "git+https://github.com/zeronsh/zui?rev=e9bffe0d979133bdfbd3783b252e1b08545b16d2#e9bffe0d979133bdfbd3783b252e1b08545b16d2" +source = "git+https://github.com/zeronsh/zui?rev=d70a4043cb7f7735b4f23c5abe293c2080b031da#d70a4043cb7f7735b4f23c5abe293c2080b031da" dependencies = [ "anyhow", "bindgen", @@ -4975,7 +4975,7 @@ checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" [[package]] name = "perf" version = "0.1.0" -source = "git+https://github.com/zeronsh/zui?rev=e9bffe0d979133bdfbd3783b252e1b08545b16d2#e9bffe0d979133bdfbd3783b252e1b08545b16d2" +source = "git+https://github.com/zeronsh/zui?rev=d70a4043cb7f7735b4f23c5abe293c2080b031da#d70a4043cb7f7735b4f23c5abe293c2080b031da" dependencies = [ "collections", "serde", @@ -5889,7 +5889,7 @@ dependencies = [ [[package]] name = "refineable" version = "0.1.0" -source = "git+https://github.com/zeronsh/zui?rev=e9bffe0d979133bdfbd3783b252e1b08545b16d2#e9bffe0d979133bdfbd3783b252e1b08545b16d2" +source = "git+https://github.com/zeronsh/zui?rev=d70a4043cb7f7735b4f23c5abe293c2080b031da#d70a4043cb7f7735b4f23c5abe293c2080b031da" dependencies = [ "derive_refineable", ] @@ -6192,7 +6192,7 @@ dependencies = [ [[package]] name = "scheduler" version = "0.1.0" -source = "git+https://github.com/zeronsh/zui?rev=e9bffe0d979133bdfbd3783b252e1b08545b16d2#e9bffe0d979133bdfbd3783b252e1b08545b16d2" +source = "git+https://github.com/zeronsh/zui?rev=d70a4043cb7f7735b4f23c5abe293c2080b031da#d70a4043cb7f7735b4f23c5abe293c2080b031da" dependencies = [ "async-task", "backtrace", @@ -6805,7 +6805,7 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "sum_tree" version = "0.1.0" -source = "git+https://github.com/zeronsh/zui?rev=e9bffe0d979133bdfbd3783b252e1b08545b16d2#e9bffe0d979133bdfbd3783b252e1b08545b16d2" +source = "git+https://github.com/zeronsh/zui?rev=d70a4043cb7f7735b4f23c5abe293c2080b031da#d70a4043cb7f7735b4f23c5abe293c2080b031da" dependencies = [ "heapless 0.9.3", "log", @@ -7988,7 +7988,7 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "util_macros" version = "0.1.0" -source = "git+https://github.com/zeronsh/zui?rev=e9bffe0d979133bdfbd3783b252e1b08545b16d2#e9bffe0d979133bdfbd3783b252e1b08545b16d2" +source = "git+https://github.com/zeronsh/zui?rev=d70a4043cb7f7735b4f23c5abe293c2080b031da#d70a4043cb7f7735b4f23c5abe293c2080b031da" dependencies = [ "perf", "quote", @@ -9703,7 +9703,7 @@ dependencies = [ [[package]] name = "zlog" version = "0.1.0" -source = "git+https://github.com/zeronsh/zui?rev=e9bffe0d979133bdfbd3783b252e1b08545b16d2#e9bffe0d979133bdfbd3783b252e1b08545b16d2" +source = "git+https://github.com/zeronsh/zui?rev=d70a4043cb7f7735b4f23c5abe293c2080b031da#d70a4043cb7f7735b4f23c5abe293c2080b031da" dependencies = [ "anyhow", "chrono", @@ -9720,7 +9720,7 @@ checksum = "29666d0abbfad1e3dc4dcf6144730dd3a3ab225bbbdac83319345b1b44ccfc1b" [[package]] name = "ztracing" version = "0.1.0" -source = "git+https://github.com/zeronsh/zui?rev=e9bffe0d979133bdfbd3783b252e1b08545b16d2#e9bffe0d979133bdfbd3783b252e1b08545b16d2" +source = "git+https://github.com/zeronsh/zui?rev=d70a4043cb7f7735b4f23c5abe293c2080b031da#d70a4043cb7f7735b4f23c5abe293c2080b031da" dependencies = [ "tracing", "tracing-subscriber", @@ -9731,7 +9731,7 @@ dependencies = [ [[package]] name = "ztracing_macro" version = "0.1.0" -source = "git+https://github.com/zeronsh/zui?rev=e9bffe0d979133bdfbd3783b252e1b08545b16d2#e9bffe0d979133bdfbd3783b252e1b08545b16d2" +source = "git+https://github.com/zeronsh/zui?rev=d70a4043cb7f7735b4f23c5abe293c2080b031da#d70a4043cb7f7735b4f23c5abe293c2080b031da" [[package]] name = "zune-core" diff --git a/Cargo.toml b/Cargo.toml index 3591db6f97..d1fe6db30d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -61,14 +61,14 @@ loro-protocol = "0.3" # stopped vending CABackdropLayer for Selection — window blur went dead); # b68970e rasterizes BackdropBlur in the wgpu renderer (frosted floats on # Linux — the Metal path's snapshot/blur/composite, ported). -gpui = { git = "https://github.com/zeronsh/zui", rev = "e9bffe0d979133bdfbd3783b252e1b08545b16d2" } -gpui_platform = { git = "https://github.com/zeronsh/zui", rev = "e9bffe0d979133bdfbd3783b252e1b08545b16d2", features = [ +gpui = { git = "https://github.com/zeronsh/zui", rev = "d70a4043cb7f7735b4f23c5abe293c2080b031da" } +gpui_platform = { git = "https://github.com/zeronsh/zui", rev = "d70a4043cb7f7735b4f23c5abe293c2080b031da", features = [ "wayland", "x11", "font-kit", "runtime_shaders", ] } -gpui_tokio = { git = "https://github.com/zeronsh/zui", rev = "e9bffe0d979133bdfbd3783b252e1b08545b16d2" } +gpui_tokio = { git = "https://github.com/zeronsh/zui", rev = "d70a4043cb7f7735b4f23c5abe293c2080b031da" } # diffs similar = "2" diff --git a/docs/macos-frame-recovery.md b/docs/macos-frame-recovery.md index 55c5bba468..c3223f5c05 100644 --- a/docs/macos-frame-recovery.md +++ b/docs/macos-frame-recovery.md @@ -37,10 +37,12 @@ transcript scrolling or runway geometry. ## Validation The PR's macOS CI job compiles the application and runs the native -`stopped_frame_source_allows_invalidation_to_restart_it` regression in the -pinned dependency. It creates a real dispatch source and checks that stopping -it without a subscription clears the latch, allowing subsequent invalidation -to schedule another restart. The old stop implementation leaves the latch set. +`frame_source_recovery` integration test in the pinned dependency. Its custom +harness runs on the native main thread, creates a real dispatch source, and +checks that stopping it clears the latch. It then forces two real CoreVideo +subscription failures with an invalid display ID and verifies that each failure +allows the next invalidation to request a restart. The old implementation +leaves the latch set. The existing Linux UI suite covers the transcript changes. Its recordings are linked in [transcript-selection-regressions.md](transcript-selection-regressions.md); From 1824a94427ed43865ff5c9b67d95c1b05f7cc0b5 Mon Sep 17 00:00:00 2001 From: wing-anara Date: Sun, 6 Sep 2026 19:53:16 +0000 Subject: [PATCH 4/4] Make native frame-start failure injection deterministic --- Cargo.lock | 46 ++++++++++++++++++------------------ Cargo.toml | 6 ++--- docs/macos-frame-recovery.md | 10 ++++---- 3 files changed, 32 insertions(+), 30 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 60657b39f1..61c0ac941f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1180,7 +1180,7 @@ dependencies = [ [[package]] name = "collections" version = "0.1.0" -source = "git+https://github.com/zeronsh/zui?rev=d70a4043cb7f7735b4f23c5abe293c2080b031da#d70a4043cb7f7735b4f23c5abe293c2080b031da" +source = "git+https://github.com/zeronsh/zui?rev=a1efdb9b7a4fb97824330af4bf49170a3deef986#a1efdb9b7a4fb97824330af4bf49170a3deef986" dependencies = [ "gpui_util", "indexmap", @@ -1667,7 +1667,7 @@ dependencies = [ [[package]] name = "derive_refineable" version = "0.1.0" -source = "git+https://github.com/zeronsh/zui?rev=d70a4043cb7f7735b4f23c5abe293c2080b031da#d70a4043cb7f7735b4f23c5abe293c2080b031da" +source = "git+https://github.com/zeronsh/zui?rev=a1efdb9b7a4fb97824330af4bf49170a3deef986#a1efdb9b7a4fb97824330af4bf49170a3deef986" dependencies = [ "proc-macro2", "quote", @@ -2554,7 +2554,7 @@ dependencies = [ [[package]] name = "gpui" version = "0.2.2" -source = "git+https://github.com/zeronsh/zui?rev=d70a4043cb7f7735b4f23c5abe293c2080b031da#d70a4043cb7f7735b4f23c5abe293c2080b031da" +source = "git+https://github.com/zeronsh/zui?rev=a1efdb9b7a4fb97824330af4bf49170a3deef986#a1efdb9b7a4fb97824330af4bf49170a3deef986" dependencies = [ "accesskit", "anyhow", @@ -2640,7 +2640,7 @@ dependencies = [ [[package]] name = "gpui_linux" version = "0.1.0" -source = "git+https://github.com/zeronsh/zui?rev=d70a4043cb7f7735b4f23c5abe293c2080b031da#d70a4043cb7f7735b4f23c5abe293c2080b031da" +source = "git+https://github.com/zeronsh/zui?rev=a1efdb9b7a4fb97824330af4bf49170a3deef986#a1efdb9b7a4fb97824330af4bf49170a3deef986" dependencies = [ "accesskit", "accesskit_unix", @@ -2692,7 +2692,7 @@ dependencies = [ [[package]] name = "gpui_macos" version = "0.1.0" -source = "git+https://github.com/zeronsh/zui?rev=d70a4043cb7f7735b4f23c5abe293c2080b031da#d70a4043cb7f7735b4f23c5abe293c2080b031da" +source = "git+https://github.com/zeronsh/zui?rev=a1efdb9b7a4fb97824330af4bf49170a3deef986#a1efdb9b7a4fb97824330af4bf49170a3deef986" dependencies = [ "accesskit", "accesskit_macos", @@ -2741,7 +2741,7 @@ dependencies = [ [[package]] name = "gpui_macros" version = "0.1.0" -source = "git+https://github.com/zeronsh/zui?rev=d70a4043cb7f7735b4f23c5abe293c2080b031da#d70a4043cb7f7735b4f23c5abe293c2080b031da" +source = "git+https://github.com/zeronsh/zui?rev=a1efdb9b7a4fb97824330af4bf49170a3deef986#a1efdb9b7a4fb97824330af4bf49170a3deef986" dependencies = [ "heck 0.5.0", "proc-macro2", @@ -2752,7 +2752,7 @@ dependencies = [ [[package]] name = "gpui_platform" version = "0.1.0" -source = "git+https://github.com/zeronsh/zui?rev=d70a4043cb7f7735b4f23c5abe293c2080b031da#d70a4043cb7f7735b4f23c5abe293c2080b031da" +source = "git+https://github.com/zeronsh/zui?rev=a1efdb9b7a4fb97824330af4bf49170a3deef986#a1efdb9b7a4fb97824330af4bf49170a3deef986" dependencies = [ "console_error_panic_hook", "gpui", @@ -2765,7 +2765,7 @@ dependencies = [ [[package]] name = "gpui_shared_string" version = "0.1.0" -source = "git+https://github.com/zeronsh/zui?rev=d70a4043cb7f7735b4f23c5abe293c2080b031da#d70a4043cb7f7735b4f23c5abe293c2080b031da" +source = "git+https://github.com/zeronsh/zui?rev=a1efdb9b7a4fb97824330af4bf49170a3deef986#a1efdb9b7a4fb97824330af4bf49170a3deef986" dependencies = [ "schemars", "serde", @@ -2775,7 +2775,7 @@ dependencies = [ [[package]] name = "gpui_tokio" version = "0.1.0" -source = "git+https://github.com/zeronsh/zui?rev=d70a4043cb7f7735b4f23c5abe293c2080b031da#d70a4043cb7f7735b4f23c5abe293c2080b031da" +source = "git+https://github.com/zeronsh/zui?rev=a1efdb9b7a4fb97824330af4bf49170a3deef986#a1efdb9b7a4fb97824330af4bf49170a3deef986" dependencies = [ "anyhow", "gpui", @@ -2786,7 +2786,7 @@ dependencies = [ [[package]] name = "gpui_util" version = "0.1.0" -source = "git+https://github.com/zeronsh/zui?rev=d70a4043cb7f7735b4f23c5abe293c2080b031da#d70a4043cb7f7735b4f23c5abe293c2080b031da" +source = "git+https://github.com/zeronsh/zui?rev=a1efdb9b7a4fb97824330af4bf49170a3deef986#a1efdb9b7a4fb97824330af4bf49170a3deef986" dependencies = [ "anyhow", "log", @@ -2796,7 +2796,7 @@ dependencies = [ [[package]] name = "gpui_web" version = "0.1.0" -source = "git+https://github.com/zeronsh/zui?rev=d70a4043cb7f7735b4f23c5abe293c2080b031da#d70a4043cb7f7735b4f23c5abe293c2080b031da" +source = "git+https://github.com/zeronsh/zui?rev=a1efdb9b7a4fb97824330af4bf49170a3deef986#a1efdb9b7a4fb97824330af4bf49170a3deef986" dependencies = [ "anyhow", "console_error_panic_hook", @@ -2820,7 +2820,7 @@ dependencies = [ [[package]] name = "gpui_wgpu" version = "0.1.0" -source = "git+https://github.com/zeronsh/zui?rev=d70a4043cb7f7735b4f23c5abe293c2080b031da#d70a4043cb7f7735b4f23c5abe293c2080b031da" +source = "git+https://github.com/zeronsh/zui?rev=a1efdb9b7a4fb97824330af4bf49170a3deef986#a1efdb9b7a4fb97824330af4bf49170a3deef986" dependencies = [ "anyhow", "bytemuck", @@ -2849,7 +2849,7 @@ dependencies = [ [[package]] name = "gpui_windows" version = "0.1.0" -source = "git+https://github.com/zeronsh/zui?rev=d70a4043cb7f7735b4f23c5abe293c2080b031da#d70a4043cb7f7735b4f23c5abe293c2080b031da" +source = "git+https://github.com/zeronsh/zui?rev=a1efdb9b7a4fb97824330af4bf49170a3deef986#a1efdb9b7a4fb97824330af4bf49170a3deef986" dependencies = [ "accesskit", "accesskit_windows", @@ -3108,7 +3108,7 @@ dependencies = [ [[package]] name = "http_client" version = "0.1.0" -source = "git+https://github.com/zeronsh/zui?rev=d70a4043cb7f7735b4f23c5abe293c2080b031da#d70a4043cb7f7735b4f23c5abe293c2080b031da" +source = "git+https://github.com/zeronsh/zui?rev=a1efdb9b7a4fb97824330af4bf49170a3deef986#a1efdb9b7a4fb97824330af4bf49170a3deef986" dependencies = [ "anyhow", "async-compression", @@ -4160,7 +4160,7 @@ checksum = "490cc448043f947bae3cbee9c203358d62dbee0db12107a74be5c30ccfd09771" [[package]] name = "media" version = "0.1.0" -source = "git+https://github.com/zeronsh/zui?rev=d70a4043cb7f7735b4f23c5abe293c2080b031da#d70a4043cb7f7735b4f23c5abe293c2080b031da" +source = "git+https://github.com/zeronsh/zui?rev=a1efdb9b7a4fb97824330af4bf49170a3deef986#a1efdb9b7a4fb97824330af4bf49170a3deef986" dependencies = [ "anyhow", "bindgen", @@ -4975,7 +4975,7 @@ checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" [[package]] name = "perf" version = "0.1.0" -source = "git+https://github.com/zeronsh/zui?rev=d70a4043cb7f7735b4f23c5abe293c2080b031da#d70a4043cb7f7735b4f23c5abe293c2080b031da" +source = "git+https://github.com/zeronsh/zui?rev=a1efdb9b7a4fb97824330af4bf49170a3deef986#a1efdb9b7a4fb97824330af4bf49170a3deef986" dependencies = [ "collections", "serde", @@ -5889,7 +5889,7 @@ dependencies = [ [[package]] name = "refineable" version = "0.1.0" -source = "git+https://github.com/zeronsh/zui?rev=d70a4043cb7f7735b4f23c5abe293c2080b031da#d70a4043cb7f7735b4f23c5abe293c2080b031da" +source = "git+https://github.com/zeronsh/zui?rev=a1efdb9b7a4fb97824330af4bf49170a3deef986#a1efdb9b7a4fb97824330af4bf49170a3deef986" dependencies = [ "derive_refineable", ] @@ -6192,7 +6192,7 @@ dependencies = [ [[package]] name = "scheduler" version = "0.1.0" -source = "git+https://github.com/zeronsh/zui?rev=d70a4043cb7f7735b4f23c5abe293c2080b031da#d70a4043cb7f7735b4f23c5abe293c2080b031da" +source = "git+https://github.com/zeronsh/zui?rev=a1efdb9b7a4fb97824330af4bf49170a3deef986#a1efdb9b7a4fb97824330af4bf49170a3deef986" dependencies = [ "async-task", "backtrace", @@ -6805,7 +6805,7 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "sum_tree" version = "0.1.0" -source = "git+https://github.com/zeronsh/zui?rev=d70a4043cb7f7735b4f23c5abe293c2080b031da#d70a4043cb7f7735b4f23c5abe293c2080b031da" +source = "git+https://github.com/zeronsh/zui?rev=a1efdb9b7a4fb97824330af4bf49170a3deef986#a1efdb9b7a4fb97824330af4bf49170a3deef986" dependencies = [ "heapless 0.9.3", "log", @@ -7988,7 +7988,7 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "util_macros" version = "0.1.0" -source = "git+https://github.com/zeronsh/zui?rev=d70a4043cb7f7735b4f23c5abe293c2080b031da#d70a4043cb7f7735b4f23c5abe293c2080b031da" +source = "git+https://github.com/zeronsh/zui?rev=a1efdb9b7a4fb97824330af4bf49170a3deef986#a1efdb9b7a4fb97824330af4bf49170a3deef986" dependencies = [ "perf", "quote", @@ -9703,7 +9703,7 @@ dependencies = [ [[package]] name = "zlog" version = "0.1.0" -source = "git+https://github.com/zeronsh/zui?rev=d70a4043cb7f7735b4f23c5abe293c2080b031da#d70a4043cb7f7735b4f23c5abe293c2080b031da" +source = "git+https://github.com/zeronsh/zui?rev=a1efdb9b7a4fb97824330af4bf49170a3deef986#a1efdb9b7a4fb97824330af4bf49170a3deef986" dependencies = [ "anyhow", "chrono", @@ -9720,7 +9720,7 @@ checksum = "29666d0abbfad1e3dc4dcf6144730dd3a3ab225bbbdac83319345b1b44ccfc1b" [[package]] name = "ztracing" version = "0.1.0" -source = "git+https://github.com/zeronsh/zui?rev=d70a4043cb7f7735b4f23c5abe293c2080b031da#d70a4043cb7f7735b4f23c5abe293c2080b031da" +source = "git+https://github.com/zeronsh/zui?rev=a1efdb9b7a4fb97824330af4bf49170a3deef986#a1efdb9b7a4fb97824330af4bf49170a3deef986" dependencies = [ "tracing", "tracing-subscriber", @@ -9731,7 +9731,7 @@ dependencies = [ [[package]] name = "ztracing_macro" version = "0.1.0" -source = "git+https://github.com/zeronsh/zui?rev=d70a4043cb7f7735b4f23c5abe293c2080b031da#d70a4043cb7f7735b4f23c5abe293c2080b031da" +source = "git+https://github.com/zeronsh/zui?rev=a1efdb9b7a4fb97824330af4bf49170a3deef986#a1efdb9b7a4fb97824330af4bf49170a3deef986" [[package]] name = "zune-core" diff --git a/Cargo.toml b/Cargo.toml index d1fe6db30d..e3498b4f17 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -61,14 +61,14 @@ loro-protocol = "0.3" # stopped vending CABackdropLayer for Selection — window blur went dead); # b68970e rasterizes BackdropBlur in the wgpu renderer (frosted floats on # Linux — the Metal path's snapshot/blur/composite, ported). -gpui = { git = "https://github.com/zeronsh/zui", rev = "d70a4043cb7f7735b4f23c5abe293c2080b031da" } -gpui_platform = { git = "https://github.com/zeronsh/zui", rev = "d70a4043cb7f7735b4f23c5abe293c2080b031da", features = [ +gpui = { git = "https://github.com/zeronsh/zui", rev = "a1efdb9b7a4fb97824330af4bf49170a3deef986" } +gpui_platform = { git = "https://github.com/zeronsh/zui", rev = "a1efdb9b7a4fb97824330af4bf49170a3deef986", features = [ "wayland", "x11", "font-kit", "runtime_shaders", ] } -gpui_tokio = { git = "https://github.com/zeronsh/zui", rev = "d70a4043cb7f7735b4f23c5abe293c2080b031da" } +gpui_tokio = { git = "https://github.com/zeronsh/zui", rev = "a1efdb9b7a4fb97824330af4bf49170a3deef986" } # diffs similar = "2" diff --git a/docs/macos-frame-recovery.md b/docs/macos-frame-recovery.md index c3223f5c05..b06e25c565 100644 --- a/docs/macos-frame-recovery.md +++ b/docs/macos-frame-recovery.md @@ -39,10 +39,12 @@ transcript scrolling or runway geometry. The PR's macOS CI job compiles the application and runs the native `frame_source_recovery` integration test in the pinned dependency. Its custom harness runs on the native main thread, creates a real dispatch source, and -checks that stopping it clears the latch. It then forces two real CoreVideo -subscription failures with an invalid display ID and verifies that each failure -allows the next invalidation to request a restart. The old implementation -leaves the latch set. +checks that stopping it clears the latch. The test executable overrides the +imported `CVDisplayLinkStart` symbol to force two start errors while using real +CoreVideo links and dispatch sources. It verifies that each failure allows the +next invalidation to request a restart, and that the retry calls CoreVideo +again. The old implementation leaves the latch set. A null display ID was +initially tried as a fault injector, but CoreVideo accepted it on the CI runner. The existing Linux UI suite covers the transcript changes. Its recordings are linked in [transcript-selection-regressions.md](transcript-selection-regressions.md);