Skip to content

feat: headless API for external UI control#336

Open
myleshorton wants to merge 5 commits intomainfrom
adam/headless-proxy-api
Open

feat: headless API for external UI control#336
myleshorton wants to merge 5 commits intomainfrom
adam/headless-proxy-api

Conversation

@myleshorton
Copy link
Contributor

Summary

  • Adds window.LanternProxy global API (headlessApi.ts) that exposes init(), start(), stop(), event subscriptions (on/off), and getState() for controlling the WASM proxy programmatically
  • When <browsers-unbounded data-headless="true"> is used, React UI rendering is skipped entirely — the WASM proxy runs but the host page controls the look
  • Fully backwards-compatible: existing embeds without data-headless work identically; the global API is registered on all pages as a bonus

Motivation

The Lantern dashboard needs to run the WASM proxy in the background while rendering its own native UI for proxy stats (connections, throughput, session data) on its world map. This avoids embedding the full unbounded widget UI and gives the host page full control.

Test plan

  • Existing <browsers-unbounded> embeds still render and function normally
  • <browsers-unbounded data-headless="true"> skips UI, logs to console
  • window.LanternProxy.init() / .start() / .stop() work in headless mode
  • .on('connections', cb) and other events fire with live data
  • .getState() returns current snapshot

🤖 Generated with Claude Code

myleshorton and others added 2 commits March 9, 2026 11:29
Expose window.LanternProxy global API that allows host pages to
control the proxy without rendering the unbounded UI. When
data-headless="true" is set on the embed element, React rendering
is skipped entirely while the WASM proxy remains fully functional.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Document the data-headless attribute, window.LanternProxy API,
events, methods, and a minimal usage example.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Adds a headless, programmatic control surface for the Unbounded WASM proxy so host pages can run the proxy without rendering the React widget UI.

Changes:

  • Registers a global window.LanternProxy API for initializing/starting/stopping the WASM proxy and subscribing to proxy-state events.
  • Adds a data-headless="true" embed mode that skips React rendering while still loading the proxy engine.
  • Documents the new headless embed option and headless API usage in the README.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 6 comments.

File Description
ui/src/index.tsx Imports headless API unconditionally and skips UI rendering when data-headless="true" is set.
ui/src/headlessApi.ts Implements and registers window.LanternProxy and forwards internal emitters to external listeners.
README.md Documents the headless dataset flag and the window.LanternProxy API/events with examples.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

You can also share your feedback on Copilot code review. Take the survey.

- Fix concurrent init() race: use shared initPromise so duplicate
  calls return the same promise instead of creating multiple
  WasmInterface instances
- Fix usage examples: subscribe to events BEFORE calling init() to
  avoid missing the ready event; use type="module" for top-level await
- Fix JSDoc: start()/stop() are fire-and-forget, not Promise-returning
- Use Object.defineProperty for window.LanternProxy to prevent
  accidental overwrites and make it non-enumerable
- Add unit tests for on/off subscriptions, getState, init idempotency,
  and global immutability

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 4 out of 4 changed files in this pull request and generated 5 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

You can also share your feedback on Copilot code review. Take the survey.

myleshorton and others added 2 commits March 9, 2026 12:08
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Gate start()/stop() on `initialized` flag, not just wasmInterface
  existence, to prevent calls during in-progress init()
- Return shallow copies of arrays from getState() to prevent external
  mutation of internal state
- Fix README: clarify that headless mode requires explicit init() call
  (WASM engine doesn't auto-load)
- Update index.tsx log message to match documentation
- Fix test mock to use class-based MockWasmInterface
- Add test for getState() returning shallow copies

Co-Authored-By: Claude Opus 4.6 <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.

2 participants