Skip to content

feat(browser): new tab page with a workspace card and suggested sites - #31656

Merged
AmoabaKelvin merged 13 commits into
mainfrom
rework-new-browser-page
Oct 21, 2026
Merged

AmoabaKelvin merged 13 commits into
mainfrom
rework-new-browser-page

Conversation

@AmoabaKelvin

@AmoabaKelvin AmoabaKelvin commented Oct 21, 2026 •

Copy link
Copy Markdown
Collaborator
Files Added Deleted Net
Test 11 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​524 $\color{#cf222e}{\Huge{\mathbf{−}}}$​19 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​505
Prod 51 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​2155 $\color{#cf222e}{\Huge{\mathbf{−}}}$​265 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​1890

Stacked on #31604: review the commits after bdaf5a5.

ELI5

A new browser tab used to be empty. Now it opens on a card for the workspace you're in: the dev servers it's running, and its PR, issue and ticket with their full titles and state. Under the card, a collapsed line lists servers running in your other workspaces. Below that are tiles for the sites you visit most. Click anything to open it in this tab. Hover a site and click × to forget it, with Undo in the toast.

What Changed

  • Workspace card (rows):
    • Dev servers from the existing port scan, kept only when the port answers GET / with a 2xx text/html page. A row shows the page's <title> and favicon over localhost:5173 · vite. APIs, databases and Cannot GET / 404s stay off.
    • The worktree's linked PR/MR, issue and Linear/Jira ticket, resolved by the same sidebar hooks and getWorkspaceLinkedItemRows, now shared by the sidebar's linked-items list and this page. A review row shows its full title, #number · Checks passing/failing/pending, and a state pill. Issue and ticket rows show their state.
    • The PR found from the branch shows too, even when no saved link names it, matching the sidebar card's "Linked PR".
    • "N running in other workspaces": servers of other worktrees on the same host, collapsed by default. Only the same host, so opening one in this tab reaches the right localhost. Folder workspaces show servers only.
  • Suggested tiles: up to 8 pages from browser history, ranked by the address bar's frecency score. Loopback and *.localhost pages are left out because live servers have their own rows. Removing a tile deletes that history entry, so it leaves the address bar too. Undo puts it back with its visit count. Tiles are one per page, as in Chrome, so removing one never reveals another page from the same site.
  • Address bar: on a blank tab, focus no longer opens the history dropdown, because the page already shows it. Typing or clicking opens it as before. The empty bar and its placeholder come from feat(browser): Chrome-style new tab and address bar suggestions #31604.
  • Probe: workspacePorts:probePage (local IPC) and workspacePorts.probePage (runtime RPC) run on the machine that owns the port.
    • Loopback hosts only, same-origin redirects only, a 1.5s timeout, the first 256 KB of the page, and a 64 KB favicon returned as a data URL so remote clients can draw it.
    • A server without the method returns method_not_found, and the page then falls back to ports a terminal printed as a URL.
  • Panes:
    • Local: replaces the old "New Tab" overlay.
    • Server-streamed: draws over the frame.
    • Client-hosted: the retained host is hidden on a blank load-commit. It sits above the pane as a click target, so the page under it would otherwise be covered.
    • Server rows: open in place in local and server-streamed panes. In client-hosted panes they go through openWorkspacePortInBrowser, as the sidebar chip does, because the page runs on this client while the port is on the server.
  • The status-bar popover's "rescan, keep last-good ports on failure" logic moved to refreshWorkspacePortScanForTarget so the page can reuse it. Opening a new tab rescans so a server started seconds ago shows up.

Why

The blank page was dead space in an app built around running servers and PRs. The design was picked from three HTML directions. The workspace card won over an all-tiles version because issues and PRs read as their full titles, so more fits at a glance. Suggested sites stay as tiles, and there is no separate Recent list.

Not in this PR:

  • Opening a port in a runtime workspace, from the sidebar chip or this page, creates two tabs. Same on main, in openWorkspacePortInBrowser.
  • A folder workspace added through a symlinked path (/tmp/x on macOS) gets no port attribution, because the process cwd resolves to /private/tmp/x. Same in the sidebar.
  • Per-item details for linked items beyond the card's own review, issue and ticket are not fetched, so extra links show their saved title without a state. Their lookup routes by the focused server, which the focus-read ratchet rejects for new callers.
  • Mobile has no browser new-tab page yet.

Linked Issue

None.

Visual Proof

Before (main) After
before after

Same profile, history, linked issue and running server in both.

Other workspaces expanded:
others

Hover on a suggested tile (remove control and tooltip):
hover

Dark:
dark

Split pane:
split

Client-hosted pane on a paired server. The server row was probed on the host over RPC:
client-hosted

Remove and Undo, typing opens the dropdown, other workspaces expand, then a server row opens the dev server in place:

after-interactions.mp4

Testing

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

On macOS in hidden dev builds:

  • Local pane: a scratch repo running an HTML server and a JSON server. Only the HTML one shows. A second workspace's server shows under "other workspaces".
  • Folder workspace.
  • A second build paired as a runtime server, which showed the client-hosted pane and a server-streamed tab.
  • Dark mode and narrow panes.

Not run live: a server on an older Orca without the probe. That path is the same method_not_found handling the port scan already uses. Linux and Windows aren't affected, since the probe is Node http against loopback.

New tests:

  • The probe against real local servers: title and icon, redirect, JSON, 404, non-loopback refused.
  • Top-site ranking.
  • Remove then Undo.
  • The client-hosted host hiding on a blank commit.

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

Ensure no issues in: Security, Cross-platoform support (Linux, Windows, Mac), Remote SSH, Mobile, general backwards compatibility, performance

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)

…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.
@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: dac1e7bb-7eaf-40d2-b24a-8514f7174c5d
📥 Commits

Reviewing files that changed from the base of the PR and between 088e05a and af8e8cf.

⛔ Files ignored due to path filters: 1
  • src/shared/rpc-contract/rpc-params-catalog.generated.ts is excluded by !**/*.generated.*
📒 Files selected for processing: 20
  • 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/components/browser-pane/new-tab/BrowserNewTabPage.tsx
  • src/renderer/src/components/browser-pane/new-tab/browser-new-tab-row.tsx
  • src/renderer/src/components/browser-pane/new-tab/browser-new-tab-server-row.tsx
  • src/renderer/src/components/browser-pane/new-tab/browser-new-tab-tile.tsx
  • src/renderer/src/components/browser-pane/new-tab/browser-new-tab-workspace-card.tsx
  • src/renderer/src/components/browser-pane/new-tab/use-browser-new-tab-linked-items.ts
  • src/renderer/src/components/browser-pane/new-tab/use-browser-new-tab-servers.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/pt.json
  • src/renderer/src/i18n/locales/zh.json
💤 Files with no reviewable changes: 1
  • src/renderer/src/i18n/locales/pt.json
🚧 Files skipped from review as they are similar to previous changes: 1
  • src/renderer/src/i18n/locales/fr.json

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

📝 Walkthrough

The change adds workspace port page probing and uses probe results to populate workspace servers on blank browser pages. Blank pages now show workspace-linked items and ranked history suggestions. The address bar adds inline completion, open-tab switching, and edit-session handoff. Browser history gains top-site ranking and remove/restore actions. The change also updates blank-URL display and retained-page visibility.

Priority: ➖ Normal

Merge Risk: 🔵 Low · up to af8e8

Typing a short prefix like "h" in the address bar can inline-complete to an unrelated visited site's origin, so Enter may open a site the user did not intend. This is a narrow, recoverable behavior issue, so the change is mergeable with a follow-up fix to the completion logic.

Pre-merge checks: Passed 4 · Failed 1

Failed Failed checks: 1 warning

Check name Status Explanation Resolution
Docstring Coverage Warning Docstring coverage is 32.98% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 94 functions across 53 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 and concisely describes the primary change: a browser new-tab page with workspace content and suggested sites.
Description check Passed The description is detailed and covers the required user impact, implementation, rationale, visual proof, testing, scope, and checklist. The Linked Issue section says "None," which is acceptable only …
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, scope, and checklist. The Linked Issue section says "None," which is acceptable only under the template's maintainer exception.

Full details: Docstring Coverage

Explanation

Docstring coverage is 32.98% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 94 functions across 53 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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1


ℹ️ Review info
⚙️ Run configuration
  • Configuration used: Repository UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: bb706684-6e1d-444c-a3d9-722dd03955a4
📥 Commits

Reviewing files that changed from the base of the PR and between 0b82f88 and 088e05a.

⛔ Files ignored due to path filters: 1
  • src/shared/rpc-contract/rpc-params-catalog.generated.ts is excluded by !**/*.generated.*
📒 Files selected for processing: 58
  • src/main/ipc/workspace-ports.ts
  • src/main/ports/workspace-port-page-probe.test.ts
  • src/main/ports/workspace-port-page-probe.ts
  • src/main/runtime/rpc/methods/workspace-ports.ts
  • src/preload/api/workspace-port-api.ts
  • src/preload/api/workspace-ports-bridge.ts
  • src/renderer/src/components/browser-pane/ClientHostedBrowserPagePane.tsx
  • 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-expansion.ts
  • 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/browser-navigation-control-row.test.tsx
  • src/renderer/src/components/browser-pane/assemble-chrome/browser-page-toolbar.tsx
  • src/renderer/src/components/browser-pane/assemble-chrome/browser-page-viewport-overlays.tsx
  • src/renderer/src/components/browser-pane/assemble-chrome/browser-workspace-pane.adoption-chrome.test.tsx
  • src/renderer/src/components/browser-pane/assemble-chrome/use-browser-address-bar-edit-handoff.ts
  • src/renderer/src/components/browser-pane/assemble-chrome/use-browser-address-bar-inline-completion.ts
  • src/renderer/src/components/browser-pane/assemble-chrome/use-browser-address-bar-switch-to-tab.ts
  • src/renderer/src/components/browser-pane/browser-client-page-retained-elements.ts
  • src/renderer/src/components/browser-pane/browser-client-page-retained-registry.test.ts
  • src/renderer/src/components/browser-pane/browser-client-page-retained-registry.ts
  • src/renderer/src/components/browser-pane/browser-client-page-retained-state.ts
  • src/renderer/src/components/browser-pane/describe-page/browser-page-url-display.test.ts
  • src/renderer/src/components/browser-pane/describe-page/browser-page-url-display.ts
  • src/renderer/src/components/browser-pane/new-tab/BrowserNewTabPage.tsx
  • src/renderer/src/components/browser-pane/new-tab/browser-new-tab-server-tile.tsx
  • src/renderer/src/components/browser-pane/new-tab/browser-new-tab-tile.tsx
  • src/renderer/src/components/browser-pane/new-tab/use-browser-new-tab-linked-items.ts
  • src/renderer/src/components/browser-pane/new-tab/use-browser-new-tab-servers.ts
  • src/renderer/src/components/browser-pane/stream-remote/remote-browser-page-pane.address-bar.test.tsx
  • src/renderer/src/components/browser-pane/stream-remote/remote-browser-page-pane.tsx
  • src/renderer/src/components/browser-pane/stream-remote/remote-browser-page-toolbar.tsx
  • src/renderer/src/components/browser-pane/stream-remote/remote-browser-page-viewport.tsx
  • src/renderer/src/components/browser-pane/workspace-doc/HtmlDocPreview.toolbar.test.tsx
  • src/renderer/src/components/browser-pane/workspace-doc/doc-preview-address-edit.tsx
  • src/renderer/src/components/sidebar/WorktreeCardLinkedItemsSection.tsx
  • src/renderer/src/components/sidebar/workspace-hover-attachments.ts
  • src/renderer/src/components/status-bar/PortsStatusSegment.tsx
  • src/renderer/src/components/tab-bar/open-tab-search-entries.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
  • src/renderer/src/lib/browser-history-match.test.ts
  • src/renderer/src/lib/browser-history-match.ts
  • src/renderer/src/lib/workspace-port-actions.ts
  • src/renderer/src/lib/workspace-port-scan-client.ts
  • src/renderer/src/lib/workspace-port-scan-refresh.ts
  • src/renderer/src/store/slices/browser.test.ts
  • src/renderer/src/store/slices/browser/browser-history-actions.ts
  • src/renderer/src/store/slices/browser/browser-slice-contract.ts
  • src/renderer/src/web/preload-api/web-workspace-ports-api.ts
  • src/shared/rpc-contract/workspace-ports-params.ts
  • src/shared/workspace-ports.ts

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

Comment on lines +217 to +220
const origin = `${parsed.protocol}//${parsed.host}`
const forms = withinHost
? [historyHost(url), parsed.host, origin]
: [formatBrowserHistoryUrl(url), formatBrowserPaletteUrl(url), url]

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Use the origin form only when the typed text has a scheme.

When the user types inside the host, forms always includes origin (for example https://github.com). The prefix check then matches scheme-less input against the scheme. As a result, typing h, ht, htt, http or https completes inline to the origin of the first matching history row.

Example: the user types h to reach hn.example. The bar fills in https://github.com and selects it. Enter then opens GitHub. This contradicts the comment at Line 194, which says the completion keeps what the user typed. Chrome does not complete scheme prefixes this way.

Keep the origin form only when schemeLength > 0. Add a test case like ['h', 'https://github.com/x'] to the does not complete table.

🐛 Proposed fix
--- "a/src/renderer/src/components/browser-pane/assemble-chrome/browser-address-bar-suggestions.ts"
+++ "b/src/renderer/src/components/browser-pane/assemble-chrome/browser-address-bar-suggestions.ts"
@@ -214,10 +214,12 @@
   if (withinHost && parsed.host === '') {
     return null
   }
   const origin = `${parsed.protocol}//${parsed.host}`
   const forms = withinHost
-    ? [historyHost(url), parsed.host, origin]
+    ? schemeLength > 0
+      ? [origin]
+      : [historyHost(url), parsed.host]
     : [formatBrowserHistoryUrl(url), formatBrowserPaletteUrl(url), url]
   // Why case only counts from the path on: hosts are case-insensitive, paths and queries aren't.
   const prefixEnd = withinHost ? typed.length : pathStart
   const form = forms.find(
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const origin = `${parsed.protocol}//${parsed.host}`
const forms = withinHost
? [historyHost(url), parsed.host, origin]
: [formatBrowserHistoryUrl(url), formatBrowserPaletteUrl(url), url]
const origin = `${parsed.protocol}//${parsed.host}`
const forms = withinHost
? schemeLength > 0
? [origin]
: [historyHost(url), parsed.host]
: [formatBrowserHistoryUrl(url), formatBrowserPaletteUrl(url), url]

@AmoabaKelvin
AmoabaKelvin force-pushed the rework-new-browser-page branch from 088e05a to 483878f Compare October 21, 2026 23:03
…ed sites

A blank tab shows this workspace's running dev servers (only ports that serve
an HTML page), its linked PR, issue and ticket, and the most visited sites,
removable with Undo. Works in local, client-hosted and server-streamed panes.
@AmoabaKelvin
AmoabaKelvin force-pushed the rework-new-browser-page branch from 483878f to af8e8cf Compare October 21, 2026 23:03
@AmoabaKelvin AmoabaKelvin changed the title feat(browser): new tab page with workspace servers, links and suggested sites feat(browser): new tab page with a workspace card and suggested sites Oct 21, 2026
@AmoabaKelvin
AmoabaKelvin merged commit e34b370 into main Oct 21, 2026
47 checks passed
@AmoabaKelvin
AmoabaKelvin deleted the rework-new-browser-page branch October 21, 2026 23:50
Jinwoo-H added a commit that referenced this pull request Oct 22, 2026
…1727)

#31656 left BrowserAddressBar.tsx one counted line over max-lines, which fails
Lint for every PR. The six copies of clear-a-timeout-ref become one helper.
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