feat(widgets): open a widget in its own window (#205) #441

Merged
jared merged 1 commits from feat/widget-popout into lotus 2026-10-20 21:05:13 -04:00
Owner

Part of #205 (the owner chose: popout + always-on-screen).

  • Each widget in the Widgets panel, and the open widget's header, gets an "Open in a new window" button, so a pad or board can sit beside the chat.
  • It opens the same filled-in URL the frame uses. widgetCompleteUrl() is now shared by the frame and the popout. It opens with noopener,noreferrer, standalone, as in Element.
  • Only cross-origin https: URLs get the button (widgetPopoutUrl). Room state can carry anything: a javascript: URL from another client has origin "null", which the frame's same-origin check alone would let through.

Verified

  • Unit test: https/same-origin/http/javascript:/data:/garbage (11/11 in the file).
  • Browser: a room with an https://example.com/?room=$matrix_room_id widget and a javascript: one, both set via room state. Only the first gets the button. Clicking it opened https://example.com/?room=!bOChz…%3Alocalhost in a new window with window.opener === null, and the open widget's header shows the button too.

Always-on-screen follows in its own PR.

🤖 Generated with Claude Code

https://claude.ai/code/session_017PCb59uY3UKqDqt7B6hzd5

Part of #205 (the owner chose: popout + always-on-screen). - Each widget in the Widgets panel, and the open widget's header, gets an **"Open in a new window"** button, so a pad or board can sit beside the chat. - It opens the same filled-in URL the frame uses. `widgetCompleteUrl()` is now shared by the frame and the popout. It opens with `noopener,noreferrer`, standalone, as in Element. - **Only cross-origin `https:` URLs** get the button (`widgetPopoutUrl`). Room state can carry anything: a `javascript:` URL from another client has origin `"null"`, which the frame's same-origin check alone would let through. **Verified** - Unit test: https/same-origin/http/`javascript:`/`data:`/garbage (11/11 in the file). - Browser: a room with an `https://example.com/?room=$matrix_room_id` widget and a `javascript:` one, both set via room state. Only the first gets the button. Clicking it opened `https://example.com/?room=!bOChz…%3Alocalhost` in a new window with `window.opener === null`, and the open widget's header shows the button too. Always-on-screen follows in its own PR. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_017PCb59uY3UKqDqt7B6hzd5
jared added 1 commit 2026-10-20 20:17:59 -04:00
feat(widgets): open a widget in its own window (#205)
CI / Build & Quality Checks (pull_request) Successful in 3m34s
CI / Trigger Desktop Build (pull_request) Skipped
CI / Docker image build & smoke test (pull_request) Skipped
CI / Secret scan (gitleaks) (pull_request) Successful in 7s
CI / Playwright smoke (e2e) (pull_request) Successful in 17m37s
c3f897b6d1
Each widget in the Widgets panel (and the open widget's header) gets an
"Open in a new window" button, so a pad or board can sit beside the chat.
It opens the same filled-in URL the frame uses (widgetCompleteUrl, now
shared) with noopener,noreferrer, standalone as in Element. Only
cross-origin https URLs get the button: room state can carry anything,
e.g. a script URL from another client.

Refs #205

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017PCb59uY3UKqDqt7B6hzd5
jared merged commit 6300e4170e into lotus 2026-10-20 21:05:13 -04:00
Sign in to join this conversation.
No Reviewers
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: LotusGuild/cinny#441