Skip to content

Let plugin panels open native Browser tabs - #1497

Closed
brsbl wants to merge 11 commits into
get-bb:mainfrom
brsbl:bb/plugin-panel-browser-api
Closed

Let plugin panels open native Browser tabs#1497
brsbl wants to merge 11 commits into
get-bb:mainfrom
brsbl:bb/plugin-panel-browser-api

Conversation

@brsbl

@brsbl brsbl commented Aug 13, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • add useBbNavigate().openBrowserTab({ url }) for plugin nav panels
  • render native BB Browser tabs beside a plugin panel, including compact-view drawer behavior
  • align plugin and Browser top chrome through the shared page-header surface
  • extend the SDK testing harness and bundled declarations

Verification

  • pnpm --filter @bb/plugin-sdk typecheck
  • pnpm --filter @bb/plugin-sdk test
  • pnpm --filter @bb/app typecheck
  • focused app tests: 60 passed
  • focused ESLint on changed host/API files
  • visually verified in the standalone BB desktop dev app

Stack

Base API layer. A child PR will add GitHub Activity as an official bundled plugin without mixing that catalog/package work into this API review.

BB-Thread-ID: thr_ehwczw5k36

@bb-slop-cop

bb-slop-cop Bot commented Aug 13, 2026

Copy link
Copy Markdown

🚨 SLOP COP 🚨 · review

I am the Slop Cop. I am reviewing this PR for security, code quality, performance, architecture, and user behavior.

I will post one final review after the parallel checks finish.

})),
[activateTab, activeBrowserTab?.id, browserTabs, closeTab],
);
const browserPanel = browserTabs.length > 0 ? (

@bb-slop-cop bb-slop-cop Bot Aug 13, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🚨 slopcop/review — Closing the final tab can leak its native browser view.

This condition unmounts BrowserTabDeck when the tab list becomes empty. The deck cannot observe the removed ID and call destroyPersistedBrowserView. The content cleanup only releases visibility and does not detach the view. Please keep the deck alive for empty-list cleanup, or destroy the view before removal.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Fixed in a72a514. The BrowserTabDeck now stays mounted when the list becomes empty so it observes the removed tab and detaches the final native view; the new ordering regression proves the detach.


const openBrowserTab = useCallback(
({ url }: { url: string }) => {
if (!isDesktopBrowserAvailable() || !isHttpOrHttpsUrl(url)) return false;

@bb-slop-cop bb-slop-cop Bot Aug 13, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🚨 slopcop/review — The boundary accepts malformed and oversized URLs.

isHttpOrHttpsUrl checks only the scheme prefix, so https:// returns true. The later new URL(tab.url) call then throws outside the plugin error boundary. This also bypasses the native URL length limit. Please parse and limit the URL here, return false on failure, and add malformed-input tests.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Fixed in a72a514. Plugin Browser URLs are now parsed once, restricted to HTTP(S), and capped at the desktop IPC limit before any tab is created; malformed and oversized inputs are covered by focused regressions.

</div>
);

return content.kind === "plugin-panel" ? (

@bb-slop-cop bb-slop-cop Bot Aug 13, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🚨 slopcop/review — This host does not cover the standalone plugin-panel path.

SplitThreadArea returns StandalonePaneContent when the viewport is compact or splits are off. That path renders PluginPanelView without this host. Therefore, openBrowserTab() always returns false on those normal surfaces. Please wrap both plugin-panel render paths and add compact and non-split tests.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Fixed in a72a514. Standalone plugin panels are now wrapped by PluginPanelBrowserHost, covering compact and non-split surfaces; SplitThreadArea regressions verify both paths.

browserTabs={browserTabs}
activeBrowserTabId={activeBrowserTab?.id ?? null}
environmentId={null}
canShowNativeBrowserView={

@bb-slop-cop bb-slop-cop Bot Aug 13, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🚨 slopcop/review — The native view can remain visible above an inactive split pane.

Wide mode gates visibility with isOpen only. A native WebContentsView ignores the DOM visibility used for inactive or maximized sibling panes. The thread host also requires pane focus for this reason. Please include pane focus and visibility in this gate, and test focus and maximize changes.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Fixed in a72a514. Wide native-view visibility is now gated by the owning split pane's focus state, with focused coverage for focus/maximize transitions.

Comment thread packages/plugin-sdk/src/app-contract.ts Outdated
* Returns false when the current surface cannot host a Browser tab or the
* URL is not HTTP(S), so callers can preserve an ordinary-link fallback.
*/
openBrowserTab(options: { url: string }): boolean;

@bb-slop-cop bb-slop-cop Bot Aug 13, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🚨 slopcop/review — This public API does not follow the required experimental process.

The repository rules require an experimental_ prefix and an entry in docs/api_to_audit.md for each new plugin API member. This change adds neither. Please rename this method project-wide and add its audit criteria before release.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Fixed in a72a514. The method is renamed project-wide to experimental_openBrowserTab, bundled/testing declarations are updated, and docs/api_to_audit.md now records the stabilization audit.

@bb-slop-cop bb-slop-cop Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🚨 SLOP COP 🚨 · review

ELI5

This adds a web page beside a plugin, like one book beside another book. Some doors do not connect yet. One closed page can also stay hidden and use memory.

Findings

I found five concrete defects:

  • The host does not mount for compact or non-split plugin panels. The method returns false on those standard surfaces.
  • A native Browser view can remain above an inactive or maximized split pane. Native views ignore the DOM visibility rule.
  • Closing the final tab can leak its live native Browser view.
  • The URL boundary accepts malformed and oversized HTTP(S) strings. A value such as https:// can crash the host.
  • The new public SDK method lacks the required experimental_ prefix and the required audit entry.

The drawer settle callback also lacks the stale-work guards used by the thread and compose hosts. A close and quick reopen can show the native view before the drawer settles.

The new host repeats Browser deck, panel, and drawer-settle logic from the thread and compose hosts. A shared settle hook and Browser deck adapter would reduce this drift.

I found no other security problem. The existing native browser sandbox, permission limits, URL policy, and IPC checks remain active.

Checks

  • @bb/app and @bb/plugin-sdk type checks passed.
  • All 343 app test files passed, with 2,672 tests.
  • All 76 plugin SDK tests passed.
  • git diff --check passed.
  • The dev app, server, host daemon, and Electron shell started.
  • A browser smoke test opened the Automations plugin panel.
  • I could not complete the new native action flow because no installed plugin invokes this method.

I left line comments for each primary defect. I did not approve this PR or request changes.

@brsbl

brsbl commented Aug 13, 2026

Copy link
Copy Markdown
Collaborator Author

Superseded by upstream-origin PR #1546. The exact reviewed commit history is preserved there; this fork-origin PR remains available for its review history.

@brsbl brsbl closed this Aug 13, 2026
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