Skip to content

fix(playground): reuse the browser window on OpenPlayground (B-808) - #3968

Merged
hellovai merged 3 commits into
canaryfrom
b-808-open-playground-inline-opens-a-new-playgro
Jul 10, 2026
Merged

hellovai merged 3 commits into
canaryfrom
b-808-open-playground-inline-opens-a-new-playgro

Conversation

@hellovai

@hellovai hellovai commented Jul 10, 2026 •

Copy link
Copy Markdown
Contributor

Problem

B-808: "open playground" inline opens a new playground window.

In baml playground (browser) mode the LSP is served over the /api/lsp WebSocket (lib.rs:223), so an editor's inline ▶ Open Playground lens reaches the server as an OpenPlayground request. The native sender (playground_sender.rs) unconditionally called webbrowser::open() and returned early — it never told the already-open page anything. So every inline click spawned a fresh browser window, at the root URL, losing the function/test target.

The two editor surfaces (VS Code WebviewPanel, Monaco tab) are already singletons, so this only bit the baml playground / browser flow — which is exactly the crowd that runs it without opening a heavy editor.

Fix

Browser-mode OpenPlayground now treats "open" as navigate the one surface, don't spawn one:

  1. Broadcast over the WS so an already-open page navigates in place (pages already handle the openPlayground notification — ExecutionPanel.tsx:1270).
  2. Only webbrowser::open when receiver_count() == 0 (no page connected), debounced 5s so two quick clicks before the first window connects don't double-open.
  3. Record the target and replay it on RequestState (which every page sends on mount), so a freshly opened or reconnected window lands on the right function/test.

Editor (VS Code) mode is untouched: open_in_browser == false keeps the existing LSP-notification path, and the shared target stays None, so the WS replay is a no-op there.

Scope

Single-project only, by design — multi-project is intentionally out of scope for this PR. (For the record: with ≥2 BAML projects there's a separate latent VS Code issue where baml.openBamlPanel is auto-registered per language client and the second collides; that's a distinct fix for later.)

Files

  • playground_sender.rs — reuse logic, OpenPlaygroundTarget/SharedOpenTarget, debounce.
  • playground_server.rs — WsState.current_open_target + RequestState replay, threaded through run/build_router.
  • lib.rs — creates the shared target, passes it to the sender and server.

Testing

cargo check, cargo check --tests, and cargo clippy all pass for baml_lsp_server. Manual verification of the baml playground reuse flow to follow.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features
    • Browser-based Playground pages now open to the correct project, function, test, or test set.
    • Already-open browser pages update instantly when a new Playground target is opened.
    • Newly connected browser pages automatically restore the most recently opened target.
    • Added safeguards to prevent multiple browser windows from opening during page load.
  • Tests
    • Added coverage for target replay to existing/new browser pages and for the browser-open debounce behavior.

In `baml playground` (browser) mode the LSP is served over the /api/lsp
WebSocket, so an editor's inline "Open Playground" lens reaches the server
as an OpenPlayground request. The native sender unconditionally called
`webbrowser::open()` and returned early, so every inline click spawned a
fresh browser window at the root URL, losing the function/test target.

Browser-mode OpenPlayground now:
- broadcasts over the WS so an already-open page navigates in place;
- only opens a browser window when no page is connected
  (`receiver_count() == 0`), debounced so two quick clicks before the first
  window connects don't double-open;
- records the target and replays it to a page on RequestState, so a freshly
  opened or reconnected window lands on the right function/test.

Editor (VS Code) mode is unchanged: the LSP-notification path stays, and the
shared target stays None so the WS replay is a no-op there.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
@linear

linear Bot commented Jul 10, 2026 •

Copy link
Copy Markdown

B-808

B-835

@cursor

cursor Bot commented Jul 10, 2026

Copy link
Copy Markdown

Bugbot is not enabled for your account, so this pull request was not reviewed.

Enable Bugbot in the Cursor dashboard to get automatic reviews on future PRs.

@vercel

vercel Bot commented Jul 10, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
beps Ready Ready Preview, Comment Jul 10, 2026 8:13pm
promptfiddle Ready Ready Preview, Comment Jul 10, 2026 8:13pm
promptfiddle2 Ready Ready Preview, Comment Jul 10, 2026 8:13pm

Request Review

@coderabbitai

coderabbitai Bot commented Jul 10, 2026 •

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 9d45009c-c11c-4836-a75b-1c76dc610a78

📥 Commits

Reviewing files that changed from the base of the PR and between 8ca1640 and b9918c5.

📒 Files selected for processing (1)
  • baml_language/crates/baml_lsp_server/src/playground_sender.rs
🚧 Files skipped from review as they are similar to previous changes (1)
  • baml_language/crates/baml_lsp_server/src/playground_sender.rs

📝 Walkthrough

Walkthrough

Browser-mode playground openings now share the latest target, broadcast it to connected pages, debounce new browser launches, and replay it when a page requests state.

Changes

Playground target replay

Layer / File(s) Summary
Record and dispatch playground targets
baml_language/crates/baml_lsp_server/src/playground_sender.rs
NativePlaygroundSender stores target metadata, broadcasts OpenPlayground, and limits system-browser launches with a five-second debounce.
Share target state across server components
baml_language/crates/baml_lsp_server/src/lib.rs, baml_language/crates/baml_lsp_server/src/playground_server.rs
Server startup creates shared target state and passes it to the sender and playground server instances.
Replay targets on WebSocket state requests
baml_language/crates/baml_lsp_server/src/playground_server.rs
RequestState reads the latest target and sends a reconstructed OpenPlayground notification to the connected page.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant NativePlaygroundSender
  participant WebSocketServer
  participant BrowserPage
  participant SystemBrowser
  NativePlaygroundSender->>WebSocketServer: broadcast OpenPlayground
  WebSocketServer->>BrowserPage: deliver notification to connected page
  BrowserPage->>WebSocketServer: RequestState
  WebSocketServer->>BrowserPage: replay latest target
  NativePlaygroundSender->>SystemBrowser: open URL when no page is connected
Loading

Poem

A bunny hops where playgrounds gleam,
Targets sail through every stream.
New pages wake and find their way,
While browsers pause before they play.
Shared paths guide each fluffy friend—
Open, replay, and hop to the end!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 71.43% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly matches the main change: reusing the browser window for OpenPlayground in playground browser mode.
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.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch b-808-open-playground-inline-opens-a-new-playgro

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.

@github-actions

Copy link
Copy Markdown

⏭️ Performance benchmarks were skipped

Perf benchmarks (CodSpeed) are opt-in on pull requests — they no longer run on every push. They always run automatically after merge to canary/main.

To run them on this PR, do any of the following, then push a commit (or re-run CI):

  • Add RUN_CODSPEED=1 to the PR description, or
  • Include run-perf or /perf in the PR title or any commit message.

@vercel
vercel Bot temporarily deployed to Preview – beps July 10, 2026 19:34 Inactive

@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.

🧹 Nitpick comments (1)
baml_language/crates/baml_lsp_server/src/playground_sender.rs (1)

79-89: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Consider a unit test for the debounce logic.

claim_browser_open encapsulates the double-open prevention (the mutex correctly serializes concurrent callers and the short-circuit && avoids bumping last_browser_open when a page is already connected). This is easily testable in isolation and would lock in the 5s window behavior.

As per coding guidelines: "Prefer writing Rust unit tests over integration tests where possible".

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@baml_language/crates/baml_lsp_server/src/playground_sender.rs` around lines
79 - 89, Add Rust unit tests for `claim_browser_open` covering the first call,
immediate repeated call rejection, and acceptance after the
`BROWSER_SPAWN_DEBOUNCE` interval; verify the timestamp is not refreshed by
rejected calls, using suitable synchronization or a testable time abstraction if
needed.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@baml_language/crates/baml_lsp_server/src/playground_sender.rs`:
- Around line 79-89: Add Rust unit tests for `claim_browser_open` covering the
first call, immediate repeated call rejection, and acceptance after the
`BROWSER_SPAWN_DEBOUNCE` interval; verify the timestamp is not refreshed by
rejected calls, using suitable synchronization or a testable time abstraction if
needed.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: f81f48c6-8dd2-4b64-983e-825b7b2c62da

📥 Commits

Reviewing files that changed from the base of the PR and between 3eaee78 and 8ca1640.

📒 Files selected for processing (3)
  • baml_language/crates/baml_lsp_server/src/lib.rs
  • baml_language/crates/baml_lsp_server/src/playground_sender.rs
  • baml_language/crates/baml_lsp_server/src/playground_server.rs

- browser-mode OpenPlayground broadcasts the openPlayground notification in
  place (correctly serialized for the page) and records the target for
  RequestState replay;
- claim_browser_open debounces rapid spawns so two quick opens before the
  first window connects don't stack windows.

Holding a live broadcast receiver keeps receiver_count() > 0, which exercises
the reuse path and guarantees the test never launches a real browser.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
@vercel
vercel Bot temporarily deployed to Preview – beps July 10, 2026 19:39 Inactive
@github-actions

github-actions Bot commented Jul 10, 2026 •

Copy link
Copy Markdown

Binary size checks passed

✅ 7 passed

Artifact Platform File Gzip Gated on Baseline Delta Status
✅ baml-cli Linux 🔒 22.0 MB 9.4 MB file 21.5 MB +445.2 KB (+2.1%) OK
✅ packed-program Linux 🔒 15.9 MB 6.7 MB file 15.6 MB +288.2 KB (+1.8%) OK
✅ baml-cli macOS 🔒 16.9 MB 8.2 MB file 16.6 MB +364.9 KB (+2.2%) OK
✅ packed-program macOS 🔒 12.3 MB 5.9 MB file 12.1 MB +232.0 KB (+1.9%) OK
✅ baml-cli Windows 🔒 18.5 MB 8.4 MB file 18.1 MB +370.2 KB (+2.0%) OK
✅ packed-program Windows 🔒 13.2 MB 5.9 MB file 13.0 MB +235.9 KB (+1.8%) OK
✅ bridge_wasm WASM 14.7 MB 🔒 4.2 MB gzip 4.1 MB +115.4 KB (+2.8%) OK

🔒 = the size this artifact is GATED on (ceiling + delta). Binaries gate on file size (installed binary); WASM gates on gzip (download size). The other size is shown for information only.


Generated by cargo size-gate · workflow run

@hellovai
hellovai enabled auto-merge July 10, 2026 19:45
@vercel
vercel Bot temporarily deployed to Preview – beps July 10, 2026 19:46 Inactive
@vercel
vercel Bot temporarily deployed to Preview – promptfiddle2 July 10, 2026 19:53 Inactive
@hellovai
hellovai added this pull request to the merge queue Jul 10, 2026
@vercel
vercel Bot temporarily deployed to Preview – promptfiddle July 10, 2026 20:13 Inactive
Merged via the queue into canary with commit 8349784 Jul 10, 2026
53 checks passed
@hellovai
hellovai deleted the b-808-open-playground-inline-opens-a-new-playgro branch July 10, 2026 20:25

This branch was previously deployed

3 inactive deployments
Preview – promptfiddle — b9918c56 Deployed Jul 10, 2026 by vercel[bot]
Preview – promptfiddle2 — b9918c56 Deployed Jul 10, 2026 by vercel[bot]
Preview – beps — b9918c56 Deployed Jul 10, 2026 by vercel[bot]
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