Skip to content

feat(browser): Chrome-style new tab and address bar suggestions - #31604

Merged
AmoabaKelvin merged 12 commits into
mainfrom
browser-new-tab-and-omnibox
Oct 21, 2026
Merged

AmoabaKelvin merged 12 commits into
mainfrom
browser-new-tab-and-omnibox

Conversation

@AmoabaKelvin

@AmoabaKelvin AmoabaKelvin commented Oct 21, 2026 •

Copy link
Copy Markdown
Collaborator
Files Added Deleted Net
Test 7 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​365 $\color{#cf222e}{\Huge{\mathbf{−}}}$​14 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​351
Prod 20 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​788 $\color{#cf222e}{\Huge{\mathbf{−}}}$​152 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​636

ELI5

A new browser tab now opens with an empty address bar that says "Search or enter URL". Before, it said about:blank, and clicking into it before typing glued your text onto that, so Enter did nothing. While you type, pages you've visited now come before the web search, and a visited address fills in as you type, like Chrome. If a suggestion is already open in another tab, its row has a "Switch to tab" button that takes you there instead of opening a second copy. Each page suggestion shows that site's icon, like the tab strip, and web searches keep the magnifier.

What Changed

New tab. A new tab still loads the same blank page under the hood. The only change is how the address bar shows it: toDisplayUrl now turns a blank page into an empty string instead of the text about:blank. Every pane that draws the bar goes through that function: local, client-hosted, remote streamed, and the document preview edit. The bar also gets a "Search or enter URL" placeholder.

Root cause of "typing breaks": the bar held the real text about:blank and relied on select-all-on-focus to protect it. A click on the already-focused bar, an arrow key, or Home/End dropped the selection. The input then became about:blankexample.com, which fails validation, and blank tabs hide the error overlay, so Enter looked dead.

Suggestion order. Same rule as Chrome: row 0 is always what Enter does.

  • When something you've visited starts with what you typed, the bar completes inline (git → git|hub.com, with the rest selected) and that page is row 0. Your other history comes next, and the search for your text goes last.
  • While you're still typing the host, it completes only the host. localhost:3000 never jumps to /dashboard because you went there more often.
  • It completes only while you type at the end. Paste, IME composition, Backspace and Delete never complete, so Backspace removes the completion.
  • Enter submits the bar text through the pane's normal submit, so remote panes still refuse file: URLs and so on. Completions that the submit path would treat as a search (intranet) or reach somewhere else aren't offered: path case, a file: URL's host, or a bare host whose default scheme differs from the visited one (localhost:3000 for an https: dev server).
  • Arrowing back to row 0 brings the completion back with its tail selected, as Chrome does, so Escape still restores what you typed. Row 0 follows the completion the bar shows, even if a background tab re-ranks history meanwhile.
  • When nothing visited starts with what you typed, the search for your text stays row 0, as in Chrome, with your matching history right under it.
  • An empty bar highlights nothing, and Enter on it does nothing (it used to reload the page as blank). Typing after Escape reopens the list.

Suggestion icons. On main, page rows (history, the typed address, the inline completion) all show a globe; web search rows already show the magnifier.

  • Page rows now show the site's favicon through the same BrowserFavicon the tab strip and the new-tab omnibox use: 14px box, broken image falls back to the globe, light outline for dark icons on dark themes.
  • The icon comes from history, which has stored each page's favicon since Display favicons for browser website entries #18099. A row with no icon of its own (git → github.com, a typed address, an entry saved without one) takes the icon of a visited page on the same host, ignoring www..
  • The fallback is the globe, because that's the tab strip's fallback. Document rows get the file icon the new-tab omnibox uses.
  • Typing never reaches a site you haven't visited. A row loads the icon URL of a page you already loaded, usually from the renderer's image cache. When the icon isn't cached yet, it's one request to that site, like the new-tab omnibox and Cmd+J today.

Switch to tab.

  • Which rows: a row whose page is open in another browser tab of the same worktree, in any profile, gets an outline "Switch to tab" button on the right.
  • Mouse: clicking the button switches, and clicking the row still opens it here.
  • Keyboard: as in Chrome, Tab moves onto the button (it fills in), Enter switches, and Shift+Tab, arrows or typing step back. Focus never leaves the input.
  • Where the open pages come from: the same builder the new-tab omnibox uses (buildSearchableBrowserPages, split out of buildOpenTabSearchEntries), so host ownership and worktree-id collisions across hosts behave the same. The bar reads them only while its list is open.
  • Activating: goes through activateBrowserPagePaletteResult. If that tab closed in the meantime, the row opens here instead.

BrowserAddressBar.tsx was already at the 400-line cap, so the inline completion, switch to tab, the edit hand-off on remount, and the overlay width measuring each moved into their own small hooks.

Design

Chosen: keep the blank guest and change only what the bar shows. Use Chrome's "row 0 is what Enter does" with host-first inline completion, plus a Switch-to-tab button.

Ruled out:

  • An internal orca://newtab URL, an about:blank src, or no guest until first navigation. The attach policy and SSH route partitions only allow the blank data URL, so any of these means main-process and guest-lifecycle changes for nothing the user can see.
  • Just moving the search row to the bottom. Then the highlighted row isn't what Enter does. Making Enter follow the highlight instead lets a title match hijack a search (react hooks opening a page titled "React hooks").
  • Enter on a matching row switches tabs. Chrome keeps Enter for "open here" and puts the switch on the button. Keeping both means you can still open a second copy on purpose.
  • Searching open tabs across worktrees. Switching worktree from an address bar is a big jump, and Cmd+J already does that.

Two blind design-challenge rounds ran before code. They led to host-first completion, one decision for row order and completion, and reusing the palette's open-page builder.

For suggestion icons: reuse history's stored favicon plus a same-host fallback, rendered with the tab strip's BrowserFavicon. Ruled out: Chrome's favicon database (image bytes per page). It needs main-process capture through each guest's session partition and a bigger saved session (#18161), and every other Orca surface already loads the same icon URLs. Also ruled out: an origin-keyed fallback, which misses youtube.com completing from www.youtube.com history. Two blind design rounds; the first moved the key to the www.-stripped host.

Open questions for you:

  • Typed search on top when nothing matches. When nothing visited starts with what you typed, should history still go above the search? Then Enter would have to open a page whose title merely contains your words. I kept Chrome's behaviour.
  • Close the blank tab on switch. Chrome closes the empty new tab you switch away from (browser_navigator.cc). I left it out: remote pages always report back history, so "nothing to go back to" can't be trusted there. Want it for local blank tabs?
  • Scope. Same worktree, any browser profile. Restrict to the same profile?

Not in this PR (different root cause):

  • orca tab list and the agent bridge report blank local tabs as data:text/html, (url and title), because main reads wc.getURL() raw at about a dozen sites.
  • Invalid input on a blank local tab still fails silently, because showFailureOverlay is gated on !isBlankTab. The remote pane already shows it.
  • cmdk replaces the list's id, so the input's aria-controls="browser-history-listbox" points at nothing.
  • History sometimes saves a page with no favicon. In both seed runs (main and this branch), the first page visited, github.com/stablyai/orca, was saved without one; a traced visit to wikipedia.org backfilled fine, so the cause isn't pinned. The same-host fallback covers it in the address bar. The new-tab omnibox still shows a globe for that entry.
  • Sites that swap their favicon for dark mode store whichever icon the page last chose. GitHub's white favicon-dark.png is faint on a light Orca theme, in the tab strip and now in these rows (see the light git shot).
  • Remote streamed and client-hosted pages never record a favicon, so their rows show the globe unless a local visit to the same host left one. That matches their tabs.

Mobile: displayBrowserUrl in mobile/src/browser/browser-url.ts still shows about:blank, and the field's placeholder is "URL". Parity needs those two lines plus, if wanted, the same row order and switch button in the mobile suggestion UI. Favicons would also need history to reach mobile, which has no browser history of its own today. The mobile app doesn't use this component, so nothing here changes it.

Linked Issue

None. Requested internally. #537 (closed) asked for a custom landing page instead of about:blank; this doesn't add that, but a new tab no longer shows about:blank.

Visual Proof

All recorded on a hidden dev build over CDP, using the same scripted flows before and after. History was seeded by visiting real pages.

1. New tab, click into the bar, type example.com, Enter

Before: the bar shows about:blank. After the click, typing gives about:blankexample.com and Enter does nothing.

before-newtab-light.mp4

After (light, then dark): an empty bar with the placeholder. e completes to example.com, and Enter loads it.

after-newtab-light.mp4
after-newtab-dark.mp4
Before After
before-newtab-light.png after-newtab-light.png

2. Order: type git; 3. Switch to tab: type pulls, arrow to the open page, Tab, Enter

Before: the git search sits above your GitHub history. Picking the open pulls page loads a second copy.

before-omnibox-light.mp4

After (light, then dark): git completes to github.com, history follows, and the search is last. pulls shows "Switch to tab". Tab fills the button, and Enter goes to the existing tab.

after-omnibox-light.mp4
after-omnibox-dark.mp4
Before After
git before-order-light.png after-order-light.png
pulls before-switch-light.png after-switch-light.png
Tab onto the button n/a after-switch-focus-light.png

Dark:

after-order-dark.png
after-switch-focus-dark.png

4. Suggestion icons: type git, news, dev, exa, react hooks

Same history in both builds: GitHub, Hacker News, MDN and example.com (which has no favicon).

Before (origin/main, light then dark): every page row has a globe; only the search row has the magnifier.

before-icons-flow-light.mp4
before-icons-flow-dark.mp4

After (light then dark): GitHub, Hacker News and MDN rows show their icons, including the github.com and developer.mozilla.org completion rows with no history entry of their own. example.com keeps the globe. Searches keep the magnifier.

after-icons-flow-light.mp4
after-icons-flow-dark.mp4
Before After
git before-icons-git-light.png after-icons-git-light.png
dev before-icons-dev-light.png after-icons-dev-light.png
exa (no favicon) before-icons-exa-light.png after-icons-exa-light.png
git, dark before-icons-git-dark.png after-icons-git-dark.png

In the light git shot, GitHub's icon is its white dark-mode variant (see "Not in this PR"); the tab strip shows the same one.

after-icons-news-dark.png

Testing

  • I manually tested these changes locally
  • Automated tests added/updated, or explained why not below

Manual testing (macOS, hidden dev build driven over CDP with real keys):

  • The flows above.
  • Backspace drops the completion and Escape keeps the typed text.
  • Typing after Escape reopens the list.
  • An empty bar has no highlight, and the first ArrowDown selects row 0.
  • Clicking the button switches.
  • One key at a time at 30 ms and 110 ms per key: each keystroke keeps the completion selected.
  • Suggestion icons in light and dark. Every row's icon measures 14×14 in the DOM (favicon, globe, magnifier), so a late or failed load can't shift a row.
  • Type https://github.com/stablyai/orca/pu, then ArrowDown, ArrowUp: the completion comes back with its tail selected, and Escape restores the typed text.

Automated tests (each fails on the code before its fix):

  • Row order while completing.
  • The typed URL's history row on top.
  • A completion table covering host-first, localhost:3000, path case, intranet and file:.
  • Type key by key, Backspace, Enter submits through the pane.
  • An accepted completion survives blur.
  • Tab then Enter switches.
  • Escape clears the button focus.
  • Page rows without their own icon take the site's, across www..
  • Row 0 stays on the shown completion when history changes.
  • Arrowing back to row 0 reselects the completion.
  • The completion table gained UNC file: hosts and scheme changes.

Existing tests that pinned about:blank in the bar now expect ''.

Gates on the rebased head: pnpm lint, pnpm tc, oxfmt --check ., import boundaries, check:code-quality:changed, check:react-doctor:changed, check:zustand-selector-fanout, and the browser-pane, tab-bar, favicon and history tests (1,845). React Doctor reports two non-blocking warnings on lines from the earlier commits.

Not tested:

  • Linux and Windows. Nothing platform-specific changed; Tab, Enter and the arrows are the same everywhere.
  • A real SSH or paired-runtime session. Those panes share this component and toDisplayUrl, and their unit tests pass. Remote and client-hosted pages record no favicon, so their rows can only show an icon learned locally for the same host.

Review: codex ran three rounds against origin/main, with two P2 findings per round, all fixed with a test. The loop hit its three-round cap rather than ending on a clean round.

  • Round 1: path case in the completion; Switch-button focus surviving Escape.
  • Round 2: Enter on a completion skipped the remote pane's file: check; switch matching collapsed URLs that differ only by a trailing / in the query.
  • Round 3: single-label hosts completed but then searched; ArrowRight-accepted completions were lost on blur.

Recording the AFTER video caught one more bug: the selection was lost when a keystroke completed to the same text. It's fixed with a key-by-key test.

Review, suggestion icons follow-up: three more codex rounds. None of the findings were in the icon code; all were about inline completion, and each is fixed with a test that fails without it. The loop hit its cap again.

  • Round 1: a background title update re-ranked history and moved row 0 off the shown completion; UNC file: hosts completed to a website.
  • Round 2: ArrowDown then ArrowUp lost the typed prefix; evicted history still moved row 0.
  • Round 3: a bare host completion could switch the visited scheme (localhost:3000 for https://localhost:3000).
    The round 3 fix itself wasn't reviewed by codex again.

AI Disclosure

Review

Agent skill upstream boundary

  • Not applicable, or this change copies or mechanically translates no upstream skill-installer source, tests, fixtures, registry entries, path tables, comments, or documentation.

Notes

  • Security: inline completions go through the same submit validation as typed text.
  • Privacy: suggestion icons load only favicon URLs of pages you already loaded, in the renderer's own session with no guest cookies. An uncached icon costs one request to a site you visited.
  • SSH/remote: the same component and checks apply. Open-tab matching reuses the palette's host-aware builder.
  • Backwards compatibility: no saved-state or message change. Favicons were already saved on history entries.
  • Performance: the switch-to-tab store read runs only while the list is open, and selects just the browser fields.

Checklist

  • This PR is small and focused
  • I explained what changed and why (ELI5, the user-facing before/after, the mechanism, and why over the alternatives)
  • Before/after screenshots or videos attached for UI changes, or N/A with reason
  • Self-reviewed for correctness, security, and performance
  • Cross-platform, SSH/remote, and path/shortcut impact considered (or N/A)
  • pnpm lint, pnpm typecheck, pnpm test, and pnpm build pass (or CI will cover; local preferred)

@AmoabaKelvin
AmoabaKelvin marked this pull request as ready for review October 21, 2026 18:31
@coderabbitai

coderabbitai Bot commented Oct 21, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration
  • Configuration used: Repository UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 7a29c36a-58b4-4616-b72f-c2377ac35483
📥 Commits

Reviewing files that changed from the base of the PR and between 01377f3 and bdaf5a5.

📒 Files selected for processing: 12
  • src/renderer/src/components/browser-pane/assemble-chrome/BrowserAddressBar.test.tsx
  • src/renderer/src/components/browser-pane/assemble-chrome/BrowserAddressBar.tsx
  • src/renderer/src/components/browser-pane/assemble-chrome/BrowserAddressBarSuggestionList.tsx
  • src/renderer/src/components/browser-pane/assemble-chrome/browser-address-bar-suggestions.test.ts
  • src/renderer/src/components/browser-pane/assemble-chrome/browser-address-bar-suggestions.ts
  • src/renderer/src/components/browser-pane/assemble-chrome/use-browser-address-bar-inline-completion.ts
  • src/renderer/src/i18n/locales/en.json
  • src/renderer/src/i18n/locales/es.json
  • src/renderer/src/i18n/locales/fr.json
  • src/renderer/src/i18n/locales/ja.json
  • src/renderer/src/i18n/locales/ko.json
  • src/renderer/src/i18n/locales/zh.json
🚧 Files skipped from review as they are similar to previous changes: 7
  • src/renderer/src/i18n/locales/en.json
  • src/renderer/src/components/browser-pane/assemble-chrome/browser-address-bar-suggestions.test.ts
  • src/renderer/src/i18n/locales/es.json
  • src/renderer/src/i18n/locales/zh.json
  • src/renderer/src/i18n/locales/fr.json
  • src/renderer/src/i18n/locales/ko.json
  • src/renderer/src/i18n/locales/ja.json

Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 9 remain after this review.

📝 Walkthrough

The address bar now supports inline completion from visited URLs and switching to a matching open browser page. Its interaction logic includes edit handoff, keyboard controls, and overlay measurement. Browser blank URLs display as an empty value. The change also updates open-tab search state construction, address-bar translations, and related tests.

Priority: ⬇️ Low

Merge Risk: ⚪ Minimal · up to bdaf5

This change makes the browser address bar and new tab behave more like Chrome, with inline completion, a blank address bar placeholder and "Switch to tab" buttons. No concrete merge-blocking defect was identified in the supplied context. The author did not test Linux, Windows or remote sessions, which is normal pre-merge validation uncertainty and does not count as risk here.

Pre-merge checks: Passed 4 · Failed 1

Failed Failed checks: 1 warning

Check name Status Explanation Resolution
Docstring Coverage Warning Docstring coverage is 40.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 35 functions across 21 files. (6 skipped:… Write docstrings for the functions missing them to satisfy the coverage threshold.
Passed Passed checks: 4 passed
Check name Status Explanation
Title check Passed The title clearly describes the primary change: Chrome-style new-tab behavior and address-bar suggestions.
Description check Passed The description is detailed and covers the required user impact, implementation, rationale, visual proof, testing, platform limitations, review notes, and checklist. It states that no linked issue exi…
Linked Issues check Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check Passed Check skipped because no linked issues were found for this pull request.
Full details: Description check

Explanation

The description is detailed and covers the required user impact, implementation, rationale, visual proof, testing, platform limitations, review notes, and checklist. It states that no linked issue exists and references closed issue #537, but the rest of the required sections are substantially complete.

Full details: Docstring Coverage

Explanation

Docstring coverage is 40.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 35 functions across 21 files. (6 skipped: 6 unsupported.)

Resolution

Write docstrings for the functions missing them to satisfy the coverage threshold.

✨ Finishing Touches: 💡 1
📝 Generate docstrings: 💡
  • Commit to this branch
  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@AmoabaKelvin
AmoabaKelvin marked this pull request as draft October 21, 2026 18:44
…switch to tab

A new tab's bar held the text "about:blank", so a click before typing merged the
input into it and Enter silently failed.
…en tabs by exact URL

Enter on a completion skipped the remote pane's file-URL check, and the history key
dropped meaningful trailing slashes.
A row without an icon of its own, like a host completion, takes one from history on the same host.
…hosts

A background tab's title update re-ranks history, which could move row 0 off the page the bar shows.
…s row

Row 0 now follows the shown completion text, so evicted history can't move it either.
@AmoabaKelvin
AmoabaKelvin force-pushed the browser-new-tab-and-omnibox branch from 01377f3 to bdaf5a5 Compare October 21, 2026 20:41
@AmoabaKelvin
AmoabaKelvin marked this pull request as ready for review October 21, 2026 22:49
@AmoabaKelvin
AmoabaKelvin merged commit 06861d9 into main Oct 21, 2026
93 of 95 checks passed
@AmoabaKelvin
AmoabaKelvin deleted the browser-new-tab-and-omnibox branch October 21, 2026 23:44
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