Skip to content

Sidebar chrome, navigation, windows and permissions - #18

Merged
siyabendoezdemir merged 79 commits into
mainfrom
lane/chrome
Oct 10, 2026
Merged

siyabendoezdemir merged 79 commits into
mainfrom
lane/chrome

Conversation

@siyabendoezdemir

@siyabendoezdemir siyabendoezdemir commented Oct 10, 2026 •

Copy link
Copy Markdown
Contributor

The chrome lane covers the sidebar's top row and show/hide, the page frame, navigation, find in page, zoom, links, windows, private windows, permissions and site controls. It's at 944ac36: 108 files, +15,697/−412 against main, with main (PRs #12 and #14) merged in.

Built

  • Yes:
    • Sidebar: SB-01, SB-02, SB-03, SB-04, SB-05, SB-11, SB-12, SB-14, SB-19, SB-21, SB-25, SB-27, SB-28.
    • Navigation: NV-01, NV-03, NV-04, NV-05, NV-06, NV-09, NV-11, NV-12, NV-15, NV-18.
    • Feel: FL-02, FL-03, FL-23. Appearance: AP-05.
  • Partial:
    • SB-20: only the macOS traffic lights follow the hidden sidebar.
    • NV-08 and SV-08: no link dragging.
    • NV-10: no Ctrl+Cmd+N blank window.
    • NV-16 and NV-17: parts wait on later features.
    • KS-08: the print, save and reader keys come with their NV items.
    • FL-15: top row only.
  • Left for later lanes: SB-08, SB-10, SB-13, SB-15, SB-17, the rest of SB-20, NV-02, NV-07, NV-13, NV-14, NV-19, NV-20 and NV-24.

FEATURES.md has every row's status and notes.

Decisions where the spec was open

  • Top row:
    • Cmd+Left/Right navigate only outside text fields. Esc stops a load only from the page, and only while it's loading.
    • Cmd- or middle-click on Back/Forward opens a background tab with that history; on Reload it duplicates the tab.
    • The history menu holds up to 15 entries and opens on a 400 ms long-press.
  • Address: host only, no padlock, and a warning only for plain http off loopback.
  • Hidden sidebar: the floating card is its own view and never resizes the page. It shows from a 6 px edge with a moving pointer, and goes 400 ms after the pointer leaves.
  • Find and zoom live in a "page tools" view at the page's top-right. Zoom is per host per profile, and persisted.
  • Private windows: one in-memory session shared by the open private windows, wiped when the last one closes. Each has its own Space outside the Space order. No Peek or Mini Window comes from a private window, and links from other apps never land in one.
  • Permissions:
    • Prompts show for the shown tab or pane; Peek and Mini Window use a native dialog. Device grants persist per profile and origin.
    • Web pages get exactly one preload, on D40's terms.
  • Small views over pages (windows.createUiView): the loading indicator, the link pill, the permission prompt and the tools view. Each window can hold up to five such renderers, most created on first use.

Review first

  1. src/main/windows.ts: the floating sidebar, the tools, prompt and small views, how they're stacked (and setPageCover under the Library), and the closed handler.
  2. Private windows: persist.ts withoutPrivate, the sessions.get fallback, and the cleanup in nav/private.ts.
  3. Permissions: the hand-off in sessions.ts, nav/permissions.ts, and the page preload src/preload/page.ts (D40).

Review rounds

  • Review A (sidebar top, show/hide, resize, frame, find, zoom; 84126ef): request changes, 4 must-fixes and 12 follow-ups.
  • Review B (links, windows, private windows, permissions, site controls): request changes, 4 must-fixes and 11 follow-ups. Three of its must-fixes were security holes:
    • pages could fake notification clicks;
    • a prompt over the page took clicks at once;
    • a cross-origin frame could ask in the top site's name.
  • Fix round (8647efe):
    • All 8 must-fixes and 14 follow-ups (B-F11 in part). Each code fix has a test that fails without it, except A-F12, which has no visible effect.
    • The fixes include the loading view left over the pages, hover reveal with a real pointer, find in a Peek, the page covering the sidebar during a toggle, and the three security fixes. B-F3 also routes a split pane's permission requests through the prompt.
    • Full e2e: 348 passed, 1 failed (CB-22, a known load flake), 4 skipped.
  • Archive step (6f34029):
    • main with archive merged in.
    • Archive review M2/M3's tests with private windows. They pass on the merged build, and fail with archive's fixes taken out.
    • A fix for a problem the merge caused: the find bar was drawn over the Library. The Library now sets setPageCover. Its new test failed first.
    • Full e2e: 372 passed, 1 failed (a race in the lane's own test, fixed in 6f34029; then 3/3), 4 skipped.
  • Fix checks (on 944ac36, the fix round with archive and main):
    • Part B (07:32): approve with 2 must-fixes. B-M1 to B-M4 were fixed, each test failing without its fix. The two new must-fixes:
      • when the closing Library uncovered a permission prompt, the prompt took a click at once;
      • a web page could make Orbit open a file: URL, through the page menu and ⌥-click (D49: a web page can't make Orbit open or save a file: URL; a file: page can).
    • Part A (08:17): approve with 2 must-fixes. A-M1 to A-M4 were fixed, each test failing without its fix. The two new must-fixes:
      • a real pointer leaving the window to the left brought the floating sidebar in and left it out (caused by the A-M2 fix);
      • with the Library open, F3, Ctrl+G and the zoom keys acted on the tab hidden under it.
  • Fix round 2 (d8a7924..0db0d6f, 12 commits, report in the lane's reviews/lane-chrome-fix2-report.md):
    • All four must-fixes, each with a test that fails without it.
      • D49: one check, pageMayOpen, in the page menu (links and images: open, split, Peek, Mini Window, new and private windows, Save As), both ⌥-click branches and the sidebar's link drop.
      • The engine itself refuses plain, ⌘-, middle- and ⇧-clicks, target=_blank, window.open, pop-ups and redirects to file:.
    • B-F1: ⌥-click now takes the link at the click point, not the page's hovered link. The nav-links flake it caused passes 20/20.
    • B-F3: closing a private window drops only a private Space.
    • A-F1: a window's first loading indicator, and its first permission prompt, no longer take the keyboard.
    • A-F3: a Mini Window zoom test. A-F4: PK-11's note.
    • The placeholder Settings action is gone; settings' real one had silently replaced it.
    • The builder's checks: unit 512/512; e2e on 13 specs, 302 passed, 2 skipped, 2 failed (both CB-22 timing tests at load 50–70, which passed alone; PR test(commandbar): judge CB-22's budgets on the best of up to three rounds #17 fixes those budgets).
    • Left to other lanes: A-F2 (look), A-F5 (keys: a loading page takes the keyboard from the command bar, older than this PR) and B-F2 (finish-tabs).
  • Round 2's fix check (on 0db0d6f): approve with 4 must-fixes:
    • Save Page As… on a web page copied the file: resources it named;
    • ⌥-click in a frame from another site opened the wrong link;
    • the zoom indicator's view took the keyboard in a new window's first 1.5 s;
    • the floating sidebar stayed out when the pointer left the window from inside it.
  • Fix round 3 (1e543f8..f10e519; main with PRs test(e2e): ask Orbit to quit from a task of its own #16, test(commandbar): judge CB-22's budgets on the best of up to three rounds #17 and test: harness follow-ups from PR #10's review (no DNS lookups, deferred evaluateHandle, SV-25/SV-26, perf) #19 merged in 1e543f8): all four must-fixes and six follow-ups, each with a test that fails without it.
    • One frame-aware check, framesMayOpen, now runs on every route where a page opens a window (D49).
    • A window's loading view is made at ready-to-show, so a load in its first second shows the indicator after about 150 ms.
    • Keys typed right after ⌘F wait for the find bar.
    • Full e2e on f10e519: 433 passed, 0 failed, 4 skipped.
  • Round 3's fix check: request changes, 2 must-fixes:
    • a menu Chromium refuses (two right-clicks close together) kept the floating sidebar out for good;
    • nav-links :247 could fail on a correct build, because it counted requests a correct build makes twice.
  • Fix round 4 (f10e519..e98439a; main at 5e79fc5 merged in f0711e8): both must-fixes, plus three follow-ups, each with a test that fails without it.
    • A refused open now says so: "Local file blocked: ".
    • Keys held after ⌘F are replayed as text, and the bar takes the keyboard even when it shows late.
    • A crashed find bar gives the keyboard back to the page.
  • Round 4's fix check: approve, no must-fix. Each item is fixed. Taking out M1's change fails chrome :997; taking out F1's toast fails nav-links :956 and :899. The checker agreed that round 3's "aBz" came from xdotool, not from Orbit. The merge f0711e8 loses nothing from main or from the lane. Its spec runs: 104 passed, 2 skipped, and 1 failure (nav :508, zoom hover) under the gate's load, which passed 3 of 3 alone; round 4 doesn't touch that code.
  • Left to other lanes:
    • the loading view's cost at launch: feel;
    • letting the engine judge which frame opened a window (Electron's createWindow route), so a local page with a web frame can open its local links again: pages;
    • a unit test of the ⌥-click frame branch's own check;
    • from earlier rounds: A-F2 (look), A-F5 (keys) and B-F2 (finish-tabs).

Tests

  • 67 unit tests in tests/unit/nav-*.test.ts, and 109 e2e tests in chrome.spec.ts and the six nav* specs.
  • At 1024×768 on 6f34029: chrome.spec.ts, every nav* spec and core.spec.ts passed 126 with 2 skipped.
  • No run logged an uncaught exception in main or a killed quit.

Merging

  • Conflicts with main so far were unions: rpc.ts, main.tsx, runtime.ts and feature-status.json. FEATURES.md was regenerated each time.
  • With settings (PR Settings window, appearance, onboarding and updates #15, merged first): cd87da0 merges it. Five small code conflicts each kept both sides; feature-status.json was merged per key, and FEATURES.md regenerated.
    • d8a7924 fixes the twin of settings' F6: the page menu's search item now names and uses the tab's profile's engine. Its test fails without the fix, offering "Search Google for" in a Bing profile's tab.
    • Settings' ST-06 test reads the page menu through chrome's test hooks. Settings and onboarding specs: 36/36 on the merge.
  • main merges in cleanly at 0db0d6f (checked with git merge-tree).

Merge gate (D42)

Run locally on the Linux VPS on this PR's head, e98439a, which has main (5e79fc5) merged in:

npm run build; npm run typecheck; npm run lint; ./node_modules/.bin/vitest run
xvfb-run -a -s "-screen 0 1440x900x24" ./node_modules/.bin/playwright test --workers 1 --retries 0 --reporter=line
  • Build, typecheck and lint: exit 0. Unit: 515 of 515 in 56 files.
  • E2E: 438 passed, 0 failed, 4 skipped, in 20.3 min at load 10–12. The line reporter doesn't name skips. The builder's run at f10e519 had the same 4:
    • MD-01 and MD-09 are fixme in the media lane;
    • nav :83 and :194 run only on macOS.
  • macOS: unverified. Nothing runs the tests there now (D42). The macOS-only keys and M4's top exit through the drag region are untested.
  • Tests that drive the real X pointer and keyboard need xdotool (and xmodmap for one), and skip without them. They ran here.

🤖 Generated with Claude Code


Note

High Risk
Touches permission prompts, a new page preload/IPC surface, private-session lifecycle, and link/open security rules (including local file D49)—all security- and data-handling-critical paths.

Overview
Adds a registerNav chrome lane that wires sidebar top-row navigation, pending address display, page overlays (find bar, zoom, loading indicator, link pill, permission prompts), link/window/pop-up handling, private windows (--private-window), and site controls—replacing core placeholders like “find coming soon” and routing command-bar clicks through the floating sidebar when hidden.

Architecture shifts: builds a separate page preload (src/preload/page.ts) with orbit-page:* IPC for permission state and notification clicks; documents stacking of small UI views and setPageCover so Library and prompts sit in the right z-order; watchMenus() keeps the hover-reveal sidebar open while native menus are up.

Docs/status: FEATURES.md / feature-status.json mark many SB/NV/FL/AP items built or tested; ARCHITECTURE.md security section updated to reflect pages no longer being preload-free.

Reviewed by Cursor Bugbot for commit e98439a. Bugbot is set up for automated code reviews on this repo. Configure here.

siyabendoezdemir and others added 30 commits October 9, 2026 19:44
SB-03: Back, Forward and Reload act on the active tab (focused pane), grey
out when unavailable and turn Reload into Stop while loading. Long-press or
right-click opens a native history menu (nearest first, favicons, SVG icons
drawn by the shell) that closes on tab switch. Cmd/middle-click opens the
entry in a background tab with its history; Cmd-click Reload duplicates.
Esc in a loading page and double-click on Reload stop. The sidebar toggle
gives way first at narrow widths. Press animations per FL-15.

SB-04: the address shows the host only, edits the full URL in the command
bar on click, warns on plain http to other machines (never a padlock), and
has site-controls (placeholder until NV-16) and copy-link buttons.

NV-01/NV-03/KS-08: Cmd+Left/Right go back/forward outside text fields on
macOS; Shift+F5 hard reload; focus returns to the page after a reload.
NV-17: one-time shortcut tip when the URL is copied from the command bar.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…02, FL-03)

- Cmd+S slides the sidebar out as the page card widens (one 260 ms curve on
  the column and the card margins), reversible mid-way; it returns at the
  same width, scroll and selected tab; state is per window and persisted.
- With the sidebar hidden, the left edge brings it in as a floating card in
  its own view over the page (the page never moves). It stays while hovered,
  while a field in it is edited or the command bar is open, goes 400 ms after
  the pointer moves on and at once on a click into the page (the click is
  passed on). macOS traffic lights follow the card.
- Dragging the sidebar's edge resizes it 1:1 within 180-420 px with no drift;
  double-click resets; 40 px past the minimum hides it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… SB-01, SB-02, SB-05)

- Native full screen drops the frame: no margins, square corners; leaving
  brings it back. Corners stay at 10 px while loading, with the sidebar
  hidden or floating.
- Each page's background colour is kept on its tab and fills the page's view
  and the shell's card under it, so a window resize uncovers no white on dark
  pages; a tab that wakes starts from its last colour.
- Tests for the layout, the sidebar's regions in order (the middle scrolls,
  the rest stays) and Favorites staying put across Spaces of one profile.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Find bar in a small view over the active page's top-right corner: finds
  as you type with an n/m count or No matches, Enter/Cmd+G/F3 and
  Shift+Enter/Shift+Cmd+G/Shift+F3 move between matches, Esc closes and
  keeps the match selected. Per tab: another tab hides it, coming back
  brings it with its text and place. Starts from the page's selection,
  else the tab's last search; input methods search once they commit.
  Cmd+J jumps to the selection (macOS); Edit > Find menu.
- Zoom steps through Chromium's levels (25-500%) on Cmd+=/-/0 and
  Ctrl+wheel; Chromium zooms every tab of the host in the profile and
  Orbit remembers it per profile across launches. A zoom indicator with
  Reset shows for 1.5 s (longer while hovered). Pinch is visual zoom;
  Cmd+0 undoes it too.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… growing in

A pane added a moment ago springs open from zero width (SV-26), so its first
bounding box can be empty. The poll now retries instead of failing at once.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…(in progress)

Shift-click opens a new window, Alt-click opens split view instead of the
engine's download, Shift-Cmd-click no longer hangs (no-guest foreground tab).
Pop-up windows keep the host as their title; pop-ups blocked for want of a
click show in the address display and can be opened or allowed per site.
Tabs opened only to download close again. The page menu gains New Window,
Private Window, Save Link/Page As, Print, View Page Source, Search [engine],
Look Up and Add to Dictionary. Private windows: an in-memory profile shared
by all private windows, a Space of their own per window, a near-black look,
and nothing written to the state file, history, favicon cache or site prefs.
Tests follow in the next commits.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

# Conflicts:
#	FEATURES.md
…ing Direction

e2e: Cmd, Shift+Cmd, Shift and Alt clicks; target=_blank; pop-up windows
titled with the host and closed by their page; blocked pop-ups offered and
allowed from the address display; download-only tabs leave nothing; the
page, link and text field menus; Cmd+N; private windows (look, own tabs,
shared in-memory session wiped after the last closes, and a scan of every
file in the profile folder for the private page after quitting).
Unit: withoutPrivate, moves, reopening, the private tab menu, pop-up titles.
The text field menu gains Writing Direction (sets the field's dir).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A profile on an in-memory session that isn't a private window's (the
command bar's history test makes one) shows in normal windows again, and
the state file keeps it. Private windows' own profiles stay apart and out
of the state file as before.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ess)

NV-15: a site's permission requests (camera, microphone, location,
notifications, clipboard, MIDI, device use, window management, fonts,
local network), device choosers (serial, USB, HID), screen sharing and
file saving wait as a prompt under the address display (in a view over
the page's corner while the sidebar is hidden). Choices are kept per
profile and site; private windows keep them in memory.

NV-16: the site controls panel in the overlay, opened from the button at
the address display's right end or the Site Controls action, with the
site's connection, permission switches, pop-ups, devices, zoom, reset and
clearing cookies and site data.

Not tested yet; committed before merging main.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Conflicts resolved, keeping both sides:
- rpc.ts: NavCommands next to PeekCommands and PeekEvents.
- state.ts: blockedPopups and permissionPrompt (nav) next to floating (peek).
- main.tsx: the mini view next to the sidebar, tools and prompt views.
- OverlayApp.tsx: SiteControls panel and PeekLayer.
- windows.ts: both import lists; floating owners and the Peek overlay fit next to
  the page tools and prompt views.
- context-menu.ts: Peek's link items next to the nav link items.
- TopBar.tsx: the chrome lane's top bar. Main's Peek-aware tab choice (PK-11)
  moves into shared/nav.ts shownTabId(), used by the address display, the
  Back/Forward/Reload buttons and the permission prompt, and by the nav.press,
  nav.history, nav.goToIndex and nav.historyMenu commands, so the buttons act
  on the Peek page while one shows.
- feature-status.json: union; FEATURES.md regenerated.

While a Peek shows, the site controls button and its action are off (the
panel would replace the Peek), and the prompt view stays hidden.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Peek's HeldModifiers becomes the app's one tracker (heldModifiers() in
  peek/input.ts). ⌥-click split view reads it too, and also the mouse event's
  own modifiers: only ⌥ alone splits, so ⌘⌥ / Ctrl+Alt stays the Mini Window
  click (MW-07).
- Private windows (NV-11): Peek's link hook and page navigation handling skip
  private tabs, the link menu has no Peek or Mini Window items there, and
  mini.fromTab refuses a private tab. A Mini Window is a normal-profile window,
  so it would carry the private page's link out of the private session.
- Link menu in the order NV-12 lists: New Tab, Split View, Peek, Mini Window,
  New Window, Private Window, Copy, Save.
- e2e: ⇧-click with Peek's Shift-click setting off opens a new window and no
  Peek; ⌥-click and ⌘⌥-click on page links and sidebar rows don't take each
  other's click; a private window keeps its links and links from other apps
  never open in it (NV-18). The nav fixture server takes any free port.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…; nav-site e2e; fixture ports 3330-3399

Pages read a permission the user hasn't decided as "default"/"prompt" (Electron's
check can only say yes or no), and a click on a page's notification brings its tab
forward. The site controls buttons toggle the panel and Esc closes it wherever focus
is. Nav fixtures walk up from 3330 within 3300-3399.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ge is on its way, dimmed sidebar when inactive

SB-28/FL-23: a small theme-tinted pill at the top centre of the shown page card, in its
own view above the pages (windows.createUiView), after 150 ms; fills and fades on load end.
NV-04: a navigation Orbit started shows its address at once and keeps it when stopped;
an error page shows the address that failed. SB-21: the sidebar fades to 84% on blur.
waitLoaded also checks the tab's live page (a just-woken tab passed too early).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A view's webContents is undefined once its page is gone, so a timer or
handler that touched view.webContents.isDestroyed() after its window closed
threw, and the error box blocked quitting. One guarded accessor,
viewContents(view), now stands in for every such access in the window
manager and the loading indicator.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Hovering or focusing a link shows where it goes in a small theme-styled
pill in the card's bottom-left corner, drawn in a view of its own sized to
the pill. After 1.5 s on one link it shows the full URL, and keeps doing so
for the next links while it stays up. It moves to the other corner when the
pointer comes within 24 px, steps aside when near both, and goes 120 ms
after the link is left. The quick-load indicator test now waits for the
first load's indicator to go before it starts.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A view-source: page commits with the address of the page it shows, so the
tab's address became the page's own and it woke up as the page. The tab now
keeps the view-source: address it shows. The NV-12 test checks the address
after the source has loaded instead of only before.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Every conflict is two additions side by side, kept together:
- state.ts: chrome's runtime tab fields (blockedPopups, permissionPrompt,
  pendingUrl) and media's tab media state; persist.ts lists all of them as
  runtime-only.
- rpc.ts: NavCommands and MediaCommands/MediaEvents; renderer views keep
  chrome's (sidebar, tools, prompt, loading, link) and add media's 'pip'.
- model.ts, ops.ts, persist.ts: both lanes' imports.
- Sidebar.tsx: chrome's structure (private sidebar, floating card, ResizeHandle
  taking the window) with media's tray above the bottom bar in normal windows.
- helpers.ts: media's launchOrbit (reducedMotion and args), with chrome's
  example of args in its comment.
- feature-status.json: union (no ID on both sides); FEATURES.md regenerated.

Typecheck and lint exit 0.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
With chrome's private windows merged, trayTabIds listed every tab with a
tray controller, so a normal window's sidebar showed the title of media
playing in a private window. The tray now takes its window and filters with
chrome's windowMayShow: no private tab in a normal window, and only its own
tabs in a private one. Private windows don't show a tray yet (their sidebar
has no bottom bar); that's a follow-up.

The new unit test fails without the fix:
expected [ 't-p', 't-q', 't-a' ] to deeply equal [ 't-a' ].

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The page preload called its captured built-ins through their own `.call`,
so a page that replaced Function.prototype.call while making a
Notification received Orbit's click listener and could call it with
{ isTrusted: true }: its tab came forward, on its Space, with no
permission, and a flood froze the shell.

- The preload captures Reflect.apply (and String) before the page runs
  and calls everything through it.
- Main acts on a notification click only from a frame whose own origin
  is allowed to show notifications, and at most once per 500 ms per tab
  (or pop-up page), so even a granted page can't flood activations.

Test: nav-site.spec.ts [NV-15] notification click now fakes a trusted
click through a hijacked Function.prototype.call / apply / Reflect.apply
and sends a click from a site without the permission; the window must
stay on the other Space. Unit test for the per-tab gate.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…otifications (review B-M3)

Every frame's request was asked in the top page's name, and the check
used the embedding origin, so an ad or widget frame could prompt as the
first party, and once the first party allowed notifications every
embedded third party could show them too.

As in Chromium: a cross-origin frame's notification request is refused
without a prompt, and the notification check uses the frame's own
origin. Camera, microphone and location still follow the page (they pass
Permissions Policy first).

Test: nav-site.spec.ts [NV-15] "a frame from another site can't ask for
notifications in the page's name, nor use the page's": the frame's
request resolves "denied" with no prompt, and after the page allows
notifications the frame still reads "denied".

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…they keep coming (review B-M2)

With the sidebar hidden, the prompt view appears at the page's top-left
corner, a spot the page knows, and took clicks at once: a page that got
the user clicking quickly there could turn one of those clicks into
"Allow while visiting the site" for the camera.

As Chromium's prompts do, a press (pointer, Enter or Space) now takes a
choice only once the prompt has been on screen for 500 ms and when the
press before it was at least 500 ms ago, so quick clicks never answer it
however long they keep coming. The guard (PromptGuard, shared) restarts
for each new prompt and each time main puts the prompt view on screen
(`prompt:placed`); while it holds, the choices say aria-disabled.
Dismissing stays immediate. Covers the sidebar card and the prompt view.

Test: nav-site.spec.ts [NV-15] "a prompt takes no clicks as it shows,
nor while they keep coming": with the sidebar hidden, eight real clicks
(sendInputEvent) 150 ms apart on Allow from the moment it shows leave
the camera request waiting; a click after a pause grants it. Unit tests
for the guard.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…n click (review B-F1)

⌘-, middle-, ⇧- and ⇧⌘-click leave the link to the browser, which loaded it
as if typed: no Referer, Sec-Fetch-Site none, SameSite=Strict cookies sent.
Orbit now asks the page to open the link itself (a detached link clicked in
an isolated world, with the gesture and the link's referrer policy) and puts
the page that comes back in the new tab or window. Requests carry the
page's Referer, Sec-Fetch-Site cross-site and no Strict cookie, as a click
in the page would. A link that isn't http(s), an opaque origin, a page that
went away or no answer in 2 s loads the URL with the referrer instead.

The page menu's Open Link in New Tab and New Window, ⌥-click split view and
a blocked pop-up's Open now send the page as Referer too.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…stays off HTML full screen (review A-M1)

hideUiView only made the view invisible, so the loading indicator (and the
link pill) stayed a child above every page, a full-screen one included, and
the core TB-19 and NV-20 tests read it as the top view. A hidden UI view now
leaves the window, as the page tools and the prompt do, and showUiView puts
it back above the pages.

The indicator no longer shows over a page in HTML full screen (an iframe
loading under a video showed it); it comes back when full screen ends if the
page is still loading. Native full screen keeps it (SB-28).

TB-19 now waits for the indicator to go with the load before it reads the
top view: a page that takes over 150 ms to load shows it, as it should.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…stays in it (review A-M3)

Find acted on the tab under the Peek: its bar opened under the overlay,
unseen, and took the keyboard from the Peek. Finding in the Peek itself is
PK-11's, with a find session for its page.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ge (review A-M2, A-F12)

The edge dropped every move without movement, and the first move the shell
hears from a pointer coming off the page has none, so a flick to the edge
often did nothing. On Linux the window's left 5 px are its resize border and
nothing in the window hears the pointer there at all.

Main now decides whether the pointer moved: it notes where the pointer was
when the sidebar hid, and the edge brings the card in once the pointer is
anywhere else, so ⌘S with the pointer at the edge still doesn't. When the
pointer leaves a page or the shell, main reads it for 300 ms and brings the
card in if it reached the window's left edge. Under ORBIT_E2E the pointer is
where the last mouse event put it, as test input never moves the system's.

A closed window's grace timer is cleared with its hold (A-F12).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…-M4)

The native page views can't move in step with the shell's frames, so
following the frame during a sidebar show or hide let them cover the
sidebar for a few frames. While the sidebar's column slides, the pane
engine now reports the panes in the part of the frame the sidebar never
covers: showing, where they end (measured with the transitions seeked to
their end and put back before the frame is drawn); hiding, where they
were. The page-colour card fills the rest in step with the sidebar. The
slide ends with the column's transition (a reversal runs on) or the
follow cap.

Test: the page view's setBounds calls during a toggle. Without the fix,
hiding moved it to 174, 40, 16, 10, 6 before the sidebar had gone, and
showing first put it at 149, not 248.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…e counts as a web page (round 2 fix check M2, F2)

A press in a frame from another site comes in the frame's own coordinates, so
elementFromPoint in the main frame named whatever of the page's sat at that
point: ⌥-click in the frame could open the page's link instead. When the main
frame's last :hover element is a frame, the press now takes the frame branch
(the hovered link, confirmed by the engine's download); elementFromPoint is
used only otherwise.

The frame branch checks the hovered link as a web page's (pageMayOpen with no
page origin), so an ⌥-click on a local file's link in a frame never opens it
beside the page, even in a local page; the engine's download goes ahead.

Tests: nav-links M2 (real pointer through xdotool under xvfb; skipped without
it) and F2.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@cursor

cursor Bot commented Oct 10, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: 4413d7ca-148b-4790-bf83-99a10bc209b1)

…ile they load; keys typed after ⌘F wait for the bar (round 2 fix check M3, F4)

M3: the page tools' view went into the window as soon as it was made, and a
view placed while its page loads takes the keyboard. In a new window's first
1.5 s (before the prewarm), ⌘+ handed the keyboard to the zoom indicator, and
it never came back. As 4adf916 and 3c4e532 did for the loading indicator and
the prompt, the view now goes into the window only once its page has loaded
(windows.onToolsReady); the nav module then shows it, the zoom indicator for
its full time from then. hideTools also gives the keyboard to the active page
when nothing has it in the focused window. When find opens, the bar takes the
keyboard once it is in the window.

F4: from ⌘F until the bar's field says it has the keyboard (find.focused),
plain keys typed in the page or the page tools (text, Backspace, Enter,
Escape; never shortcuts) are held, then typed into the field in order. So
keys typed right after ⌘F in a window's first second reach the bar.

Tests: nav.spec M3 (zoom in a new window's first moments; keys reach the
page's field before, while and after the indicator shows; the tools view
never takes the keyboard) and F4 (keys typed right after ⌘F reach the bar,
not the page).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@cursor

cursor Bot commented Oct 10, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: 15f6b69b-2107-4cf6-913c-b3135f4a547c)

…ng card lets it go; the card stays while a menu is up (round 2 fix check M4)

Over the frameless window's resize border or the card's drag region nothing
hears the pointer, so the card's last leave came short of the edge and the
card told main nothing: it stayed out with the pointer gone. Now a leave the
card can't place says so (sidebar.peekState { over: null }), and main reads
the pointer (watchOut): the card stays while the pointer is in the window,
and goes once it is out of the window with no button down.

The card also stays while a native menu is up in its window, as a menu can
reach out of the window: menu-watch counts the menus up in each window for
every Menu.popup(), and the card's grace delay waits for the last to close.

Test: chrome.spec M4 (a leave to nothing at x = 2, then the pointer out of
the window: gone within a second; kept in the window: stays; a menu up: stays
until it closes).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@cursor

cursor Bot commented Oct 10, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: 6cd6aec9-52c8-44e0-b2e7-7276cd705e82)

…oads what a page chose (round 2 fix check F1)

- A tab's window-open handler refuses a local file before anything else, for a web page or a
  page with a frame from elsewhere (main can't tell which frame asked): no tab, window, Peek, Mini
  Window or pop-up, and no blocked pop-up to open later. A pop-up's own opens do the same.
- A blocked pop-up is kept to open later only where the page may open it.
- The frame split from the engine's download opens only what a web page may open; otherwise the
  download goes ahead.
- framesMayOpen (shared) is pageMayOpen for each frame, by origin; Save Page As… uses it too.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@cursor

cursor Bot commented Oct 10, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: e3c6dda3-ffd0-485f-96e2-1f2115f67381)

…ady, as CommandRegistry does (round 2 fix check F5)

A second handler silently replaced the first; now any start of Orbit fails on it, whatever made
the id (a literal, a template, a list).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@cursor

cursor Bot commented Oct 10, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: b5a7a0f3-b4db-4661-94a9-241eae29dc6b)

…ms (round 2 fix check F6)

The engine hands the menu a script link as about:blank#blocked, which opened as such; a data link
opened as a page of its own. Copy Link Address and Save Link As stay.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@cursor

cursor Bot commented Oct 10, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: 6f7039ad-132b-4e7a-a917-80beef2a708a)

…owser window's own (round 2 fix check F3)

Since F3 (d6bcef4) each window's loading view is made as the window shows, so OB-02's list of
pages found it at once and failed 3 of 3 (it passed 3 of 3 with the view made 1 s later, as before).
The check is still that no window but the browser window opens.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@cursor

cursor Bot commented Oct 10, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: 388a8213-7ffd-488d-887d-5b7ae534827a)

siyabendoezdemir and others added 2 commits October 10, 2026 15:27
docs/feature-status.json merged key by key (IM-08, IM-11 and ST-15 from main); FEATURES.md regenerated.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…rd out (round 3 fix check M1)

On Linux Chromium runs one native menu at a time in the app: a popup() while a menu is up, in any
window, closes that one and shows nothing. The refused menu hears no menu-will-show or
menu-will-close, only its popup() callback, at once, so menu-watch's count stayed at 1 and the
card never went by itself in that window again. A menu now comes off the count at its
menu-will-close or its callback (wrapping the caller's), whichever comes first, once.

Test (chrome): two menus asked for in one evaluate, then a menu asked for in a second window while
the first has one up; each time the card goes within 1 s of the pointer leaving the window.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@cursor

cursor Bot commented Oct 10, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: 76a8721a-3064-4659-8a17-9d77e8cc3975)

…xactly one request (round 3 fix check M2)

An ⌥-click requests the link twice on a correct build: for the split's page, and for the engine's
own ⌥-click download, which Orbit cancels in will-download once its request has gone out. The test
polled for exactly 1 request, so a sample after the second failed it. It now waits for the split
to show the page, then checks that each request so far had the page as Referer. The nav fixture
keeps what every request said about where it came from (sentFromEach), not only the latest.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@cursor

cursor Bot commented Oct 10, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: ab8fb6ea-5e7d-4753-950f-5436af8d4537)

… (round 3 fix check F1)

The rule stays (D49): a local page with any frame whose origin isn't a local file's, a web page's
or an opaque one (a sandboxed or data: frame, even one the page made), counts as a web page for
local files, as Orbit can't tell which frame asked. That refuses modified clicks, target=_blank
links and window.open alike. The refusal was silent; the tab's and the pop-up's window-open
handlers now show "Local file blocked: <name>", once per tab and file every few seconds.

Tests: nav-links, real clicks on a local page with a web, a sandboxed srcdoc and a data: frame
(target=_blank and Ctrl-click; a plain click still loads; a same-origin srcdoc frame opens both);
the round 2 F1 test checks the toast for the tab's handler and a pop-up's; unit, the file's name.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@cursor

cursor Bot commented Oct 10, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: fe11cc87-d0af-42ce-bd96-710bf14b3be6)

siyabendoezdemir and others added 2 commits October 10, 2026 16:27
…es the keyboard (round 3 fix check F2)

Keys typed for the find bar before its field has the keyboard are typed
into it in order once it does: text with insertText, so any character
arrives as typed, and only Backspace, Enter and Escape as key events. Text
after one of those waits until the bar's page has had the key (its key
up), as insertText could overtake it; keys typed meanwhile are held and
typed in their turn. A key Chromium names with a control character (as it
names keys xdotool maps on the fly) carries no text and is dropped.

A bar whose page tools load after the 5 s hold has ended still takes the
keyboard when it shows: the hold lets its keys go, but the bar keeps
waiting. Shortcuts and IME composition are still not held.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… it has loaded again (round 3 fix check F6)

A crash of the page tools' renderer sets them back to not ready: they
leave the window, and the keyboard goes back to the page if they had it.
Each load of their page makes them ready again, so the find bar comes back
with its search once the reload is done, and is never placed over a page
that isn't there.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@cursor

cursor Bot commented Oct 10, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: 21affa2e-b0d9-430f-ae0f-684ed33d76e8)

@siyabendoezdemir
siyabendoezdemir merged commit 28cb4ec into main Oct 10, 2026
1 check passed
siyabendoezdemir added a commit that referenced this pull request Oct 10, 2026
Conflicts: src/shared/rpc.ts keeps both sides' views; docs/feature-status.json
is merged key by key (no key was changed on both sides); FEATURES.md is
regenerated from it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
siyabendoezdemir added a commit that referenced this pull request Oct 10, 2026
docs/feature-status.json: SB-11 keeps the keys lane's KS-02 note, SB-12
takes chrome's native-menu note; FEATURES.md regenerated.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
siyabendoezdemir added a commit that referenced this pull request Oct 10, 2026
Conflicts resolved keeping both sides:
- withoutPrivate (src/shared/private.ts) also leaves out a private session's downloads,
  and persist.ts's serialize uses it alone (fix check N4: chrome's filter skipped them).
- Sidebar: chrome's private and floating sidebars, with the downloads tray in both.
- features.ts, rpc.ts, electron.vite.config.ts, ARCHITECTURE.md: both sides' entries.
- docs/feature-status.json merged key by key (no key changed on both sides);
  FEATURES.md regenerated.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
siyabendoezdemir added a commit that referenced this pull request Oct 11, 2026
… task of its own (PR #19 review F7)

- The test hook's flush() writes the state file from a task of its own (later), as the other hooks
  do, so a flush asked through the inspector no longer runs part-way through other main-process code.
  flushNow() writes in the calling task, for an evaluate that checks the state first and must write in
  the same turn (downloads' saveStateWhen).
- scripts/perf/run.mjs and scripts/capture/record.mjs quit Orbit as the e2e harness does (askQuit,
  PR #16): app.quit() from a task of its own, then Playwright's close only cleans up. The capture
  script's setBounds runs from a task of its own too.
- run.mjs takes --cold-only, Orbit's cold start alone, for the loading view's cold-start rounds in
  PERF.md (PR #18 fix check 3, F5).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant