From 3cb44b96eb7eb0588838dcd7a187953f80db069d Mon Sep 17 00:00:00 2001 From: Codex Bot Date: Sat, 24 Oct 2026 19:50:31 +0800 Subject: [PATCH] fix: preserve source focus across native chrome activation --- docs/ui-workspace-audit.md | 13 ++++++++++++- src/app.rs | 14 ++++++++++---- src/developer_tools.rs | 2 +- 3 files changed, 23 insertions(+), 6 deletions(-) diff --git a/docs/ui-workspace-audit.md b/docs/ui-workspace-audit.md index 43f6465..bab9f0b 100644 --- a/docs/ui-workspace-audit.md +++ b/docs/ui-workspace-audit.md @@ -13,7 +13,7 @@ This audit follows the full conversation, including later corrections. Implement | Close/activate/reorder/migrate/detach | Ordinary managed Page methods retained; animated hit bounds and drag anchors use current visual bounds | Native close and drag-out to Endor Window 2 succeeded. API detach/move-back preserved Page 1:135:0. Native cross-window drop, dwell and mixed-DPI were not re-run in this final build | | Four developer tools separated | Independent Page instances; Console/Sources/Network/Settings modules and separate per-view state | Earlier native top-level Sources/Console activation and read-only copy/paste verified; no nested navigation remains | | Four tools not default or permanently placed | No Tools menu, rail buttons or new-page tool directory; existing API/shortcut retained | Final default surfaces viewed without tool entries | -| Source reading/selection/copy | Grapheme-boundary selection, no gutter in copy, select-all/copy, Shift navigation, drag edge scrolling | Native single-line drag/copy/paste verified earlier in this change. Final added keyboard/edge behavior compiled; not all combinations manually exercised | +| Source reading/selection/copy | Grapheme-boundary selection, no gutter in copy, select-all/copy, Shift navigation, drag edge scrolling | Native single-line drag/copy/paste verified earlier in this change. Final keyboard copy was re-run after the chrome-focus fix: drag-copy matched the complete selected line; four Shift+Right operations copied the expected four characters; select-all copied multiline source without a gutter. Drag edge behavior compiled; exhaustive combinations remain untested | | Clicking Console output must not jump to prompt/end | Input hit region restricted to actual prompt rows | Native reading/copy path verified earlier; no input hit over transcript-only area | | Default page reflects Endor | Objects, code and UI share identity; persistence/model connections explicitly described as work in progress | Referenced chats read: 01a16819-28c3-7ce0-9423-0ffaa19402ce and 01a1689b-77d8-75a2-ba9e-b2fa3dce654e; actual native start surface viewed | | Sensible source organization | Runtime/API tests separated; four page view modules; shared painter/selection; native tool Page factory; motion module | All-target Clippy compiled production and test sources. Runtime host reduced from ~7000 to ~1380 lines | @@ -33,3 +33,14 @@ One placement probe used unsupported == syntax and returned a parse error before ## Remaining verification Isolated Chrome timing/shape comparison, frame-by-frame animation capture, fresh native cross-window drop/dwell on the final build, mixed-DPI cross-screen interaction and exhaustive keyboard/source-selection combinations remain without new runtime evidence. These are not silently marked passed. The shipped Page lifetime/GC model was preserved; no new persistence or AI backend is claimed. + + +## Final focus regression fix + +Final native retesting found a real regression: title-strip mouse events cleared the active editor focus. Window activation performed before automated key events exposed the same behavior. Chrome mouse presses now preserve content focus; developer pane hit testing ignores points outside the pane; frame publication updates the keyboard route according to the active Page. No source, tab geometry or selection state is logged by the shipped build. + +After the fix, direct system clipboard comparisons (without printing clipboard contents) confirmed selected-line equality (28 characters), Shift+Right selection equality (4 characters), and multiline select-all with no line-number gutter. The temporary Sources Page was closed afterwards. All-target Clippy, formatting and release building passed again. + +## Local delivery state + +Implementation commits: 3220544 (runtime test-source separation), 656522e (managed workspace/chrome implementation), d3165b1 (behavior and requirement audit), followed by the chrome-focus fix commit. A push to origin/main was attempted once and rejected by automatic approval review because explicit authorization for this default-branch remote mutation was required. No alternative push route was attempted. The remote remains unchanged pending the user's approval. diff --git a/src/app.rs b/src/app.rs index 2124752..7b1a8d3 100644 --- a/src/app.rs +++ b/src/app.rs @@ -325,6 +325,10 @@ impl TesseraApp { if let Ok(namespace) = self.namespace.read() { self.developer_tools.document_updated(&namespace.document); let frame = &namespace.document.frame; + self.guest_keyboard = !frame + .tabs + .iter() + .any(|tab| tab.active && tab.developer_tool.is_some()); if self.tab_pressed.as_ref().is_some_and(|pressed| { frame.session != pressed.session || frame.fault.is_some() @@ -1397,7 +1401,12 @@ impl ApplicationHandler for TesseraApp { { let _ = window.drag_window(); } - self.guest_keyboard = !self.guest_covered(self.pointer); + if self.pointer.y >= crate::tabs::height(&[]) { + self.guest_keyboard = !self.guest_covered(self.pointer); + if self.guest_keyboard { + self.developer_tools.blur(); + } + } self.tab_pressed = self.tab_hit(); self.tab_drag_grab = self .tab_pressed @@ -1406,9 +1415,6 @@ impl ApplicationHandler for TesseraApp { .and_then(|input| self.drag_anchor(input)); self.tab_drag_origin = self.tab_drag_grab.map(|_| self.pointer); self.guest_pressed = self.guest_hit(); - if self.guest_keyboard { - self.developer_tools.blur(); - } self.developer_tools.press( &self.layout.developer_tools, self.pointer, diff --git a/src/developer_tools.rs b/src/developer_tools.rs index 8d46e25..7b9fd5c 100644 --- a/src/developer_tools.rs +++ b/src/developer_tools.rs @@ -296,7 +296,7 @@ impl DeveloperToolsState { self.preedit.clear(); } pub fn press(&mut self, layout: &DeveloperToolsLayout, p: Point, activity: &Activity) { - if !self.open { + if !self.open || !layout.panel.contains(p) { return; } self.preedit.clear();