Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
f12c5e6
fix(desktop): keep settings choice menus inside the zoomed viewport
DoodleBears Oct 19, 2026
6467872
test(desktop): retain scaled choice-menu regression evidence
DoodleBears Oct 19, 2026
6c59373
Merge branch 'codex/2268-macos-zoom-delivery' into codex/2269-macos-z…
DoodleBears Oct 19, 2026
9ba3a59
Merge branch 'codex/2268-macos-zoom-delivery' into codex/2269-macos-z…
DoodleBears Oct 19, 2026
8143945
test(browser): query portaled settings choices through their document
DoodleBears Oct 19, 2026
8aa6e3a
Merge branch 'codex/2268-macos-zoom-delivery' into codex/2269-macos-z…
DoodleBears Oct 19, 2026
b10490b
build(client): refresh Browser and Computer choice-menu bundles
DoodleBears Oct 19, 2026
f807fd8
Merge branch 'codex/2268-macos-zoom-delivery' into codex/2269-macos-z…
DoodleBears Oct 19, 2026
fc23e4f
Merge branch 'codex/2268-macos-zoom-delivery' into codex/2269-macos-z…
DoodleBears Oct 19, 2026
924f26d
Merge branch 'codex/2268-macos-zoom-delivery' into codex/2269-macos-z…
DoodleBears Oct 19, 2026
0d7ad1e
Merge branch 'codex/2268-macos-zoom-delivery' into codex/2269-macos-z…
DoodleBears Oct 19, 2026
80efc42
Merge branch 'codex/2268-macos-zoom-delivery' into codex/2269-macos-z…
DoodleBears Oct 19, 2026
6bca097
test(ui): record native extreme-zoom menus and focus-proof limits
DoodleBears Oct 19, 2026
9c632d8
test(ui): qualify final embedded focus and native popup samples
DoodleBears Oct 19, 2026
503aa84
Merge branch 'codex/2268-macos-zoom-delivery' into codex/2269-macos-z…
DoodleBears Oct 19, 2026
b6275fc
fix: fit native tooltips under desktop window zoom
DoodleBears Oct 19, 2026
d56e3a7
docs: qualify final macOS tooltip and hidden guest recovery
DoodleBears Oct 19, 2026
aa023f7
fix(client): restore native tooltip side when zoom releases
DoodleBears Oct 19, 2026
9c59cc3
Merge branch 'codex/2268-macos-zoom-delivery' into codex/2269-macos-z…
DoodleBears Oct 19, 2026
d66147e
docs(zoom): retain outstanding default shortcut qualification
DoodleBears Oct 19, 2026
9dd2c7c
Merge branch 'codex/2268-macos-zoom-delivery' into codex/2269-macos-z…
DoodleBears Oct 19, 2026
79ff75d
docs(zoom): update merged Expo dependency provenance
DoodleBears Oct 19, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions .agents/skills/dsh-dev/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -364,3 +364,15 @@ lock, disables scripts and preserves the native `ui-theme` row and preference Pa
app closed until setup returns. Refuse other owners, a busy Profile, an unsupported app version
or a running target; do not edit ASARs, silently replay uncertain writes or relabel failed
native-manager attempts as installation proof. Reopen the same Profile/user-data for acceptance.

### Desktop plugin-event stream churn (#2269)

On the actual 0.2.1-rc.2 Desktop, a passive Network observer recorded `/plugins/events`
returning HTTP 200 plus a graph frame, failing with `net::ERR_FAILED` after about 301 seconds,
and reconnecting three seconds later with HTTP 200 and a fresh graph. The shipped Desktop
forwards this scheme route through Node fetch; the shipped HMR Client uses EventSource.
This identifies native plugin-event transport churn and observed recovery, not the exact
underlying timeout source. Do not report that cohort as console-error-free, infer webview
painting from the stream, or add guest reload/recreation to conceal it. Preserve the full
request/failure/reconnect sequence without auth data and distinguish a page exception or
failed reconnection from this measured sequence.
2 changes: 1 addition & 1 deletion .agents/skills/dsh-ui/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ The in-harness UI is part of the shell, not a standalone app (ADR-0028): style w
- **Baseline externals only** in the client bundle; adding one updates the bundle contract test in the same commit.
- **Modes unwind**: shadows and listeners reconcile from a single store and dispose cleanly.

- **Pointer menus have one positioning owner**: use `ds/PointerMenu` for every application right-click menu. Pass unmodified `clientX/clientY`; never add feature-local offsets or construct a coordinate proxy anchor. See the coordinate contract in `references/native-contract.md`. Native anchored dropdowns continue to use `Menu`, except the qualified compact Settings picker under whole-window zoom; its owning-Modal container contract is recorded in the native-contract reference.
- **Pointer menus have one positioning owner**: use `ds/PointerMenu` for every application right-click menu. Pass unmodified `clientX/clientY`; never add feature-local offsets or construct a coordinate proxy anchor. See the coordinate contract in `references/native-contract.md`. Native anchored dropdowns continue to use `Menu`, except the measured compact Settings picker and shared `ds/Select` under whole-window zoom; its owning-Modal container contract is recorded in the native-contract reference.

## Reference

Expand Down
43 changes: 42 additions & 1 deletion .agents/skills/dsh-ui/references/native-contract.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ Use the Puppeteer install from `node_modules/.pnpm/puppeteer@*/node_modules/pupp

## Pointer-anchored context menus

Application-defined context menus use `packages/client/src/client/ds/pointer-menu.tsx`. A mouse caller supplies raw viewport `clientX/clientY`; a keyboard caller supplies the trigger's `getBoundingClientRect()` left/bottom. Do not use page/offset coordinates, feature-local nudges, or a transformed panel as a positioning container. Ordinary button-anchored dropdowns keep the native `Menu`.
Application-defined context menus use `packages/client/src/client/ds/pointer-menu.tsx`. A mouse caller supplies raw viewport `clientX/clientY`; a keyboard caller supplies the trigger's `getBoundingClientRect()` left/bottom. Do not use page/offset coordinates, feature-local nudges, or a transformed panel as a positioning container. Ordinary button-anchored dropdowns keep the native `Menu`, except the measured shared `ds/Select` contract below.

`PointerMenu` portals to `document.body`, measures its rendered coordinate scale and the actual native menu rectangle, and positions the menu at the requested viewport point. It alone owns window-edge clamping, native traffic-light clearance, available menu size and resize/scroll repositioning. A menu moves away from the pointer only to remain inside the safe viewport; native scrolling and submenu behavior remain intact. This keeps panel transforms, clipping ancestors and CSS zoom out of feature code.

Expand All @@ -85,3 +85,44 @@ Only the shared Panel title button (`data-panel-hide`, icon + label) is a Panel
Verified against Dockview 8.4.1 and the shipped DSH 0.2.1-rc.2 frontend: Dockview adds a draggable, focusable `role=tab` wrapper outside our React header. A drag starting on that wrapper never traverses the inner header's capture handler. Reject non-title header drag starts at the outer Dock capture boundary, before Dockview's native listener. Regression tests must dispatch on the real `.dv-tab` and `.dv-tabs-and-actions-container` as well as the inner header, and separately prove title drags remain allowed.

DSH marks `[tabindex]` and `[role=tab]` as native `no-drag` regions. On macOS, explicitly restore `drag` on these Dock header wrappers while keeping title, context, tabs and actions `no-drag`. A passing DOM drag test does not prove native window movement: check both blank-header window displacement and title-only Panel drops in a non-fullscreen Desktop.

### Shared Select under whole-window zoom

The shared `ds/Select` also uses `PointerMenu` after the native body-portaled menu was measured
outside the viewport at 144% and 430%, and detached from its trigger below 100%. Its trigger
supplies the measured left/bottom or right/bottom anchor, and the menu owner applies start/end
alignment, native frame clearance and viewport clamping. The owning dialog is the portal
container so native Modal keyboard ownership still applies. Zoom/style, resize and scroll
changes update the anchor; closing restores focus only if the next action did not move it.
Native Menu still owns option rendering, composition guards and keyboard traversal. Other
button-anchored native Menu callers are unchanged and require their own qualification.

The coordinate owner uses CSSOM `currentCSSZoom` when available, with its existing rendered
probe fallback. A 1px probe is quantized at small zoom factors and cannot reliably convert a
long viewport distance in one placement. The qualified Desktop exposes `currentCSSZoom`;
[CSSOM View](https://www.w3.org/TR/cssom-view/#dom-element-currentcsszoom) defines it as the
effective CSS zoom. Available width also caps the native minimum so a choice menu can fit a
small viewport. This changes popup geometry, not the owning Panel's minimum constraints.

Run `node scripts/check-select-zoom.mjs --output <report.json>` for the real production
Select/SettingsDialog/PointerMenu and unmodified reachable native Menu/Modal code and CSS.
The isolated source fixture covers all 17 levels, start/end alignment, page/dialog placement,
two viewport sizes, keyboard selection, menu-only Escape, focus return, outside clicks and
zoom changes while open. `--baseline <ref>` runs the old Select and must fail on the reported
coordinate bug. It does not qualify a running DSH Host, native window controls or guest painting;
macOS acceptance remains a separate #2269 check.

### Native Tooltip under qualified desktop zoom

The 0.2.1-rc.2 native Tooltip also writes viewport anchor coordinates into a zoomed fixed
position element. At 144%, the Home name tooltip was measured around x907 while its button
was at x659–705. The downstream zoom lease owns the conversion for the pinned
`role=tooltip` / `data-side` / `data-align` contract. It records the focus/hover trigger,
measures the actual bubble, fits its screen rectangle and converts only the corrective
translation through `currentCSSZoom`; native visibility, delays, keycaps, dismissal and
composition remain native. It restores its translation/width lease on disposal and does not
run in ordinary browsers or unsupported desktop compositions. An existing independent
translation is not taken over. Source checks include all 17 levels, three sides, two alignments,
two viewport sizes and retained pinned bubbles while zoom changes. Native window
pixels remain a separate acceptance check; programmatic/unmapped third-party tooltip
triggers are not qualified by these representative button paths.
13 changes: 13 additions & 0 deletions .agents/skills/verify-deepseekbot/features/desktop-window-zoom.md
Original file line number Diff line number Diff line change
Expand Up @@ -110,3 +110,16 @@ The opt-in installer therefore uses the explicitly selected app's bundled pnpm w
is closed, under the native Profile writer lock. Verify refusal while running, then closed-app
enable, native cold boot, closed-app disable and unchanged `ui-theme` values. Do not replace
this qualification with a headless Profile or the failed native installation attempt.


## Native Tooltip placement

At144%, focus the Home name button through Tab/Shift+Tab: its tooltip must be adjacent to
the button and remain within the viewport. Repeat after a zoom action and retain the original
composer draft. `node scripts/check-select-zoom.mjs --output <report.json>` covers the shipped
Tooltip code and CSS through all17 levels, three sides, two alignments and two viewport sizes,
plus pinned bubbles during live zoom changes. `--tooltip-baseline 1` excludes the downstream
adapter and must fail the coordinate regression. Source fixtures are separate from actual
native pixels and never qualify guest painting. The adapter is owned by the qualified zoom
lease and restores its styles/listeners on disposal; ordinary browsers and unknown Desktop
versions do not acquire it.
109 changes: 109 additions & 0 deletions .github/pr-screenshots/2269-choice-menus/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
# Shared Select zoom regression

Source commit: `f12c5e658a79655090d9e0d3b08fc880b4a86012`. Both images use the same 1261×820 viewport, 144% CSS zoom and synthetic fixture state. They show the production Select/SettingsDialog/PointerMenu and unmodified reachable DSH Menu/Modal code and CSS. This is an isolated source-component fixture, not a running DSH/native-window screenshot.

- Before: the native body-portaled choice menu is outside the viewport.
- After: the menu is aligned with the trigger inside its owning Settings Modal.
- Baseline: 132/136 cases fail across 17 levels, two viewport sizes, start/end alignment and page/dialog containers.
- Fix: 136/136 pass with no page errors. Keyboard selection, menu-only Escape, focus return, toggle/outside dismissal, retained draft/menu identity and open-menu zoom changes are also asserted.
- 2035 Client tests pass (one existing skipped test); 29 related tests, lint, formatting, root/mobile types and a production Client build to a separate temporary output pass.

Run `node scripts/check-select-zoom.mjs --output <report.json>`; add `--baseline e17154081` to reproduce the failure and `--screenshots <directory>` for the matched images. The fixture blocks requests outside its own loopback origin and uses no Host, credentials, user wallpaper, messages or guest browser.

## Native follow-up — 2026-10-19

The [native menu probe](native-menu-probe.json) records actual installed Desktop checks at
144%, 23% and 430%. At maximum zoom, keyboard focus scrolls the recovery button into view;
the default picker stays inside the viewport and can select the minimum level. These three
captures are actual Desktop output from the .12 artifact, with its version stated explicitly.
They are additional evidence, not a relabeling of the source fixture before/after pair.

![Native Settings at 144%](native-settings-144.png)
![Native recovery reachable at 430%](native-settings-430-recovery.png)
![Native default menu at 430%](native-settings-430-menu.png)

The final .14 artifact passed closed-app enable/restart, original font preservation, A→B→A
local Host switch and exact-target CLI read/set/reset. The Human accepted final wallpaper,
header/corner, panels and zoom Settings in native windowed/fullscreen modes. Real IME was
explicitly accepted earlier in this task. Current full Client regression passes 2035 tests
with one existing skip; Browser 407/8skip and Computer/selected workspace 234/3skip passed.

The first .14 embedded automation attempt did not establish guest focus: a nested AX click left the real
parent focus on Bot Settings, so its root zoom event is excluded from webview proof. The
fixture was disposed, and prior Human guest foreground/once-only records retain their own
scope. The later foreground follow-up below supersedes that guest-focus gap. #2269 remains open for its other required gates.
No new PR has been merged or released.

## Final-package focused embedded and popup follow-up

The Human foregrounded the same existing Desktop. The [final native embedding record](final-native-embedding.json)
now establishes real guest input focus, same guest identity/navigation/input, one minus and
one plus whole-window action, and unchanged guest-native zoom. Focused same-origin and
cross-origin iframes plus the main input were also driven through CUA native keys, with
per-document readback. Equal delivery may include Shift; it is not relabeled physical plain Equal.

The earlier failed nested-AX attempt is retained above. The new accepted guest rows explicitly
have parent WEBVIEW focus and guest `hasFocus=true`. The task fixture was removed through its
owned release, leaving the same formal window at144%. No production guest recovery hack exists.

Final .14 Browser/Computer target menus and Home context-menu screenshots additionally show
correct viewport placement and native Escape/focus return. No target, permission or message was changed.

![Final installed Desktop embedded rulers](final-embedded-focus.png)
![Final Browser settings menu](final-browser-menu-144.png)
![Final Computer settings menu](final-computer-menu-144.png)
![Final Panel context menu](final-panel-menu-144.png)

Further .14 follow-up established native Cmd+H hide/restore at unchanged geometry, guest identity, URL, input and internal zoom. The complete-window capture is linked below. Native Minimize actions still did not establish hidden state.
Tool Minimize actions did not establish hidden state, so they are not counted as a pass. The
late console cohort has native `/plugins/events` network failures; no whole-cohort
error-free claim is made. #2269 remains open, and prior Human webview-restoration proof keeps
its original scope. Windows/Linux remain deferred.

![Final .14 fixed-geometry hide/restore](final-hide-restore-144.png)

A passive native Network observer captured `/plugins/events` HTTP200 + graph, failure after
301.1s, and automatic HTTP200 + full-graph recovery three seconds later. This attributes the
route and observes recovery; the underlying timeout source is not confirmed. No guest reload
or renderer restart was used.

The native Tooltip check found a distinct geometry regression: at144%, the Home-name tooltip
was nearx907 while its trigger was atx659–705. The downstream pinned zoom lease now fits native
bubble geometry without replacing its visibility, delay, keycap or dismissal owner. The
expanded source fixture passes136 Select cases and204 Tooltip cases;36 additional zoom
changes preserve an already-pinned bubble. Final updated-package Tooltip pixels remain a
separate acceptance gate. Earlier .14 native proof remains versioned as .14.

## Final updated package — .16

The [versioned final record](final-native-16.json) adds actual corrected Tooltip pixels at144%
and120%, plus Human/native hidden-created, hidden-resized webview recovery after ordinary
minimize/restore. The recorded hidden and restored viewport is1261×820 at144%. Both initial
navigation-ready events occurred while hidden; the full-window native image after restore
shows the guest ruler and input. The fixture was released afterward. The .14 id-preserving
hide/restore and focus rows above remain separate evidence, not relabeled .16 results.

![Actual native Tooltip after the fix](native-tooltip-after-144.png)
![Final .16 hidden-created webview restored](final-hidden-created-restored-16.png)

Current source:2037 Client tests pass,1 existing skip. The earlier avatar-editor timeout is
retained and the full rerun passed. Select136/136, Tooltip204/204 and36 retained pinned-bubble
zoom changes pass; unadapted native Tooltip fails192/204 cases. The complete
[source Tooltip matrix](tooltip-source-matrix.json) is separate from native pixels.

The original broad native/input, installer and Host-switch records retain their version and
scope. macOS Human/native behavior qualification covers these representative supported paths.
Default reset and numberpad bindings have not shipped: the pinned native protocol rejects
numberpad codes, and reset remains Settings/CLI/custom-bindable without a default. This
outstanding original requirement is tracked explicitly in #2318; it has not been waived or
counted as passing S2 acceptance. #2299 closes only the bounded #2269 delivery after review
and merge. The separate mobile Expo-doctor failure is fixed on main by merged #2319,
which superseded the closed dependency PR #2317 for #2314. Fresh CI for the updated stack
remains a separate gate.
Unknown app versions, arbitrary third-party pages and Windows/Linux remain outside this
qualification.

The review follow-up restores the native tooltip's original `data-side` together with its
translation and maximum width on disposal. A source regression forces a bottom-to-top flip
and verifies restoration plus observer shutdown. Existing .16 native screenshots predate
this cleanup-only change and are not relabeled as a new native run.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Loading