From 24687a09fbbad90c2b6bc1d16f4022d751689314 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=9B=B7=E7=94=B5=E8=8A=BD=E8=A1=A3?= Date: Thu, 1 Oct 2026 00:12:40 -0400 Subject: [PATCH] =?UTF-8?q?T45c=20Site=20=C2=B7=20Home:=20full=20screen=20?= =?UTF-8?q?stops=20the=20glide=20at=20once,=20a=20starting=20gallery=20lea?= =?UTF-8?q?ves=20the=20host's=20focus=20alone,=20a=20focused=20frame=20sta?= =?UTF-8?q?ys=20mounted?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- AGENTS.md | 1 + TASKS.md | 6 +++++- examples/gallery/index.html | 10 +++++----- frontend/src/Live.tsx | 8 ++++++-- frontend/src/Shell.tsx | 8 +++----- frontend/src/pages/Home.tsx | 21 +++++++++++++++++---- frontend/src/scroll.ts | 18 ++++++++++++++++++ 7 files changed, 55 insertions(+), 17 deletions(-) create mode 100644 frontend/src/scroll.ts diff --git a/AGENTS.md b/AGENTS.md index ea82fe4..af13671 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -224,6 +224,7 @@ Ely GPUI Component. A component library for GPUI, in light and dark. - A wall clock reads `Timestamp::now` and wakes its view at each whole second through a keyed task, only while the owner passes no moment; a stopwatch reads the executor's clock, so tests advance it (`misc::Clock`, `misc::Stopwatch`). - A number shown to so many significant digits goes through `typography::format::significant`, which rounds through Rust's exponent form, so no binary noise shows at any size (`misc::Calculator`, `misc::UnitConverter`). - A component that reads the host's frames reads one at a time off the main thread and passes over frames that come meanwhile; when a read ends it redraws, so the newest frame is read next (`misc::QrCodeScanner`). +- Embedded, the web gallery ignores its own `focus()` calls until a press or key reaches it, so a frame that starts never takes the host page's focus; the site keeps a focused frame mounted (`frontend/src/pages/Home.tsx`'s `Near`). - A native view laid over the window takes AppKit's first responder on a press, and gpui's view never asks for it back, so typed text would go to the native view for good. The component hands the keyboard back on every press gpui gets and when the view leaves (`misc::WebView`, through wry's `focus_parent`); `Step::NativeKey` proves it, since gpui's own dispatch skips AppKit. - A map keeps places in `f64`: at zoom 19 the world is 134 million pixels across. Its wheel pans and Command or Control with the wheel zooms about the pointer, as the canvas's does. Its tiles stay in gpui's asset cache, as every `img()` picture does. Its popup is an overlay that is not modal: it takes focus when it shows, and the frame it stops showing, by any way out, hands focus back. Its layers, lines and areas unwrap across the date line and repeat on every world in view. Clustered pins gather by squares of the world, not of the view, so a pan never regroups them; a zoom may, so it hands a focused pin's or cluster's focus to the map. diff --git a/TASKS.md b/TASKS.md index 91355d9..2126984 100644 --- a/TASKS.md +++ b/TASKS.md @@ -157,7 +157,7 @@ Per-component lines live in `tasks/`. Tags there: - [ ] T45 Website: `frontend/` rebuilt after `frontend-reference/` (serro.ai's language: heavy titles, thin faint text, hairlines, one accent), Vite 8, pnpm, React 19, TanStack Router, light and dark; isometric line miniatures that grow cell by cell on a marimba beat; every story live, a native capture where a browser cannot run it; no box inside a box, no subheadings; run locally, not published - [x] T45a Site · Stack: the old `frontend/` replaced by Vite 8, React 19 and TanStack Router under pnpm; routes `/`, `/components/`, `/components/$page/$story`; theme, fonts, the stories manifest - [x] T45b Site · Iso: isometric line miniatures on a canvas, no perspective, hidden lines removed, cells growing on a sixteenth-note marimba beat, playable under the pointer, light and dark, still under reduced motion -- [ ] T45c Site · Home: a full-screen hero and full-screen sections, each a miniature beside its live component, counts, the chapters as a city of plinths, a closing call; motion from scroll and route changes +- [x] T45c Site · Home: a full-screen hero and full-screen sections, each a miniature beside its live component, counts, the chapters as a city of plinths, a closing call; motion from scroll and route changes - [ ] T45d Site · Components: the chapter index and the story page redesigned; live stories without frames, native captures for what a browser cannot run, search, keys - [ ] T45e Site · Local: the gallery bundle built for localhost and the site served for the owner to see; not published - [ ] T46 E2E: Playwright against the built site @@ -494,3 +494,7 @@ Per-component lines live in `tasks/`. Tags there: | T45a | 3 | PASS | | | T45b | 1 | FAIL | diagonal cells painted over the cells before them; the pointer struck cells not yet grown; reduced motion read once; replay by pointer only; the header ran 2px past 280 | | T45b | 2 | PASS | | +| T45c | 1 | FAIL | a glide outlived its route; a wheel closed full screen; skip links shared one id | +| T45c | 2 | FAIL | full screen opened mid-glide unmounted as the glide ran on | +| T45c | 3 | FAIL | the same, with Enter 30 ms into the glide: Lenis stopped a frame late | +| T45c | fix | — | after the cap: full screen stops Lenis at once, the gallery frame no longer takes host focus at start, a focused frame stays mounted; round 3's own repro passes 20 of 20 at Enter delays 0 to 250 ms | diff --git a/examples/gallery/index.html b/examples/gallery/index.html index c055388..6195fdd 100644 --- a/examples/gallery/index.html +++ b/examples/gallery/index.html @@ -18,6 +18,11 @@ // Embedded, gpui's hidden field waits for a press or key. if (window.parent !== window) { let touched = false; + // Until then its focus calls leave the host's focus alone. + const focus = HTMLElement.prototype.focus; + HTMLElement.prototype.focus = function (options) { + if (touched) focus.call(this, options); + }; const touch = () => { if (touched) return; touched = true; @@ -25,11 +30,6 @@ }; addEventListener("pointerdown", touch, true); addEventListener("keydown", touch, true); - document.addEventListener("focusin", (event) => { - if (touched) return; - event.target.blur(); - window.parent.focus(); - }, true); }