In-frame "Share your screen?" prompt replaces the corner button (cinny #43) #42

Merged
jared merged 1 commits from lotus-screenshare-prompt into lotus 2026-09-27 13:05:01 -04:00
Owner

Follow-up to cinny #43. On engines where the host can't delegate the user's click, screensharing moves back to the host's call bar, with the confirmation shown inside the call frame.

Changes

  • LotusScreensharePrompt replaces LotusFrameScreenshare (the corner button). It's a small "Share your screen?" card (Share / Cancel) fixed at the bottom-centre of the frame, just above the host's bar.
    • Share calls toggleScreenSharing synchronously in its own click, so getDisplayMedia runs with this frame's activation.
    • Share is focused on open. Cancel and Escape close it.
    • It closes if a share starts another way, or if the room policy stops allowing it.
    • Under 240 px tall (picture-in-picture) it drops the description line and tightens its padding.
  • New toWidget io.lotus.prompt_screenshare {} (allow-listed). It opens the card only in prompt mode (lotusFrameScreenshare), where the policy allows sharing (set_frame_screenshare), EC can share, and nothing is being shared: shouldPromptScreenshare, unit-tested.
  • controls_state changes:
    • frameScreenshare is now always false: there's no corner button any more.
    • New screensharePrompt tells the host to use the card.
    • New screensharePromptOpen lets the host stop covering the frame (its picture-in-picture overlay) while the card is open.

What changes for users

  • Firefox, Safari and the Linux desktop app: the screenshare button is back on the call bar and the status bar, where the camera and speaker buttons are. It replaces the lone button in the call's bottom-right corner.
    • Clicking it shows "Share your screen?" inside the call, just above the bar. Share starts the share; Cancel or Escape closes the card.
    • That's the same two clicks as the "Share your screen?" confirm Chrome already had.
    • The card has to be inside the call: these browsers only start a share after a click in the call frame itself. That's the reason the corner button existed.
  • Picture-in-picture: starting a share from the status bar while you're in another room shows a compact version of the card inside the small call window.
  • Chrome, Edge and the Windows app: unchanged.
  • Stopping a share: works from the bar everywhere, and needs no card.

Test evidence

Local Synapse + LiveKit. "No delegation" means Chromium with navigator.userAgentData removed, which takes exactly the Firefox/Safari code path (no Capability Delegation).

Scenario Result
No delegation, same-origin and cross-origin (call page on :5174, like prod) Bar button present, no corner button. Click → card in frame, Share focused, host confirm not shown. Cancel → no share. Share → LiveKit shows VIDEO/SCREEN_SHARE + AUDIO/SCREEN_SHARE_AUDIO. Bar Stop → tracks gone.
Chromium, same-origin and cross-origin Unchanged: host confirm → share → bar stop.
Picture-in-picture (278×156 window) Compact card, 210×83, fully inside the window, description hidden. Share starts the share. "Return to call" works again afterwards.
Room policy forbids screensharing Bar button hidden. A forced prompt_screenshare request is refused (no card). Restoring the policy brings the button back.
Regression, cross-origin with production headers Join/isolation, PTT/deafen in the frame, layout/reactions/settings, speaking indicator, foreign-frame spoof blocked, soundboard, avatars, muted-speech warning: all pass.

Also: element-call lotus tests 116 (8 new for the card), cinny unit tests 1,268, Playwright e2e 20/20, tsc/eslint/oxlint/knip clean.

Not automated: real Firefox. Playwright's headless Firefox 155 stalls at "Heating up" when setting up the encryption store, before any call code runs, in both dev and production builds. So the Firefox-specific part (a click in the frame counts as activation for getDisplayMedia) is covered by the no-delegation run and by the HTML spec, not by a real Firefox run. Please try it in your Firefox after step 2. Rollback is quick.

Rollout

  1. Merge element-call lotus-screenshare-prompt → tag v0.25.0-lotus.22 → CI publishes. No effect on its own.
  2. In cinny screenshare-prompt, bump the pin to 0.25.0-lotus.22, then merge (deploys).

Order and compatibility:

  • This cinny code with the current fork (lotus.21): the fork still reports its corner button, so the host keeps hiding its own. It behaves exactly like today, so merging cinny early is harmless.
  • The new fork with the current host: never happens, because the fork only reaches production through cinny's pin.

Rollback: set the pin back to 0.25.0-lotus.21 and redeploy. You get the corner button again.

Review checklist

  • The Share click calls toggleScreenSharing synchronously (no await before it).
  • The card can't open where the room policy forbids sharing, or while already sharing.
  • frameScreenshare: false doesn't hide the button on any host (checked against the current and new host code).

🤖 Generated with Claude Code

https://claude.ai/code/session_01PPmy3tPq869XDW4njjVaKA

Follow-up to cinny **#43**. On engines where the host can't delegate the user's click, screensharing moves back to the host's call bar, with the confirmation shown inside the call frame. ## Changes - **`LotusScreensharePrompt`** replaces `LotusFrameScreenshare` (the corner button). It's a small "Share your screen?" card (Share / Cancel) fixed at the bottom-centre of the frame, just above the host's bar. - **Share** calls `toggleScreenSharing` synchronously in its own click, so `getDisplayMedia` runs with this frame's activation. - Share is focused on open. Cancel and Escape close it. - It closes if a share starts another way, or if the room policy stops allowing it. - Under 240 px tall (picture-in-picture) it drops the description line and tightens its padding. - **New toWidget `io.lotus.prompt_screenshare {}`** (allow-listed). It opens the card only in prompt mode (`lotusFrameScreenshare`), where the policy allows sharing (`set_frame_screenshare`), EC can share, and nothing is being shared: `shouldPromptScreenshare`, unit-tested. - **`controls_state` changes:** - `frameScreenshare` is now always `false`: there's no corner button any more. - New `screensharePrompt` tells the host to use the card. - New `screensharePromptOpen` lets the host stop covering the frame (its picture-in-picture overlay) while the card is open. ## What changes for users - **Firefox, Safari and the Linux desktop app:** the screenshare button is back **on the call bar and the status bar**, where the camera and speaker buttons are. It replaces the lone button in the call's bottom-right corner. - Clicking it shows **"Share your screen?"** inside the call, just above the bar. **Share** starts the share; **Cancel** or **Escape** closes the card. - That's the same two clicks as the "Share your screen?" confirm Chrome already had. - The card has to be *inside* the call: these browsers only start a share after a click in the call frame itself. That's the reason the corner button existed. - **Picture-in-picture:** starting a share from the status bar while you're in another room shows a compact version of the card inside the small call window. - **Chrome, Edge and the Windows app:** unchanged. - **Stopping a share:** works from the bar everywhere, and needs no card. ## Test evidence Local Synapse + LiveKit. "No delegation" means Chromium with `navigator.userAgentData` removed, which takes exactly the Firefox/Safari code path (no Capability Delegation). | Scenario | Result | |---|---| | No delegation, same-origin and cross-origin (call page on `:5174`, like prod) | Bar button present, no corner button. Click → card in frame, **Share focused**, host confirm not shown. Cancel → no share. Share → LiveKit shows `VIDEO/SCREEN_SHARE` + `AUDIO/SCREEN_SHARE_AUDIO`. Bar Stop → tracks gone. | | Chromium, same-origin and cross-origin | Unchanged: host confirm → share → bar stop. | | Picture-in-picture (278×156 window) | Compact card, 210×83, fully inside the window, description hidden. Share starts the share. "Return to call" works again afterwards. | | Room policy forbids screensharing | Bar button hidden. A forced `prompt_screenshare` request is refused (no card). Restoring the policy brings the button back. | | Regression, cross-origin with production headers | Join/isolation, PTT/deafen in the frame, layout/reactions/settings, speaking indicator, foreign-frame spoof blocked, soundboard, avatars, muted-speech warning: all pass. | Also: element-call lotus tests 116 (8 new for the card), cinny unit tests 1,268, Playwright e2e 20/20, tsc/eslint/oxlint/knip clean. **Not automated: real Firefox.** Playwright's headless Firefox 155 stalls at "Heating up" when setting up the encryption store, before any call code runs, in both dev and production builds. So the Firefox-specific part (a click in the frame counts as activation for `getDisplayMedia`) is covered by the no-delegation run and by the HTML spec, not by a real Firefox run. **Please try it in your Firefox after step 2.** Rollback is quick. ## Rollout 1. Merge **element-call** `lotus-screenshare-prompt` → tag `v0.25.0-lotus.22` → CI publishes. No effect on its own. 2. In **cinny** `screenshare-prompt`, bump the pin to `0.25.0-lotus.22`, then merge (deploys). **Order and compatibility:** - **This cinny code with the current fork (lotus.21):** the fork still reports its corner button, so the host keeps hiding its own. It behaves exactly like today, so merging cinny early is harmless. - **The new fork with the current host:** never happens, because the fork only reaches production through cinny's pin. **Rollback:** set the pin back to `0.25.0-lotus.21` and redeploy. You get the corner button again. ## Review checklist - [ ] The Share click calls `toggleScreenSharing` synchronously (no await before it). - [ ] The card can't open where the room policy forbids sharing, or while already sharing. - [ ] `frameScreenshare: false` doesn't hide the button on any host (checked against the current and new host code). 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01PPmy3tPq869XDW4njjVaKA
jared added 1 commit 2026-09-27 13:01:04 -04:00
feat(lotus): in-frame "Share your screen?" prompt replaces the corner button (cinny #43)
CI / Publish to Gitea npm registry (pull_request) Skipped
CI / Build embedded bundle (pull_request) Successful in 3m25s
bb675fa50e
Where the host can't delegate the user's click (Firefox, Safari, WebKitGTK),
lotus.20 showed EC's own screenshare button floating in the frame's corner,
apart from the host's call bar. Now the host keeps its bar button and sends
the new toWidget io.lotus.prompt_screenshare; the fork shows a small
"Share your screen?" card inside the frame, just above the bar, and its
Share button starts the share with the frame's own click. Same two clicks as
the host's confirm on Chromium.

- The card focuses Share; Cancel and Escape close it; it closes if a share
  starts some other way or the room's call policy stops allowing sharing,
  and never opens where the policy forbids it (set_frame_screenshare).
- Picture-in-picture (~158 px tall): compact card without the description.
- controls_state: frameScreenshare is now always false (the corner button is
  gone), screensharePrompt says the host should use the prompt, and
  screensharePromptOpen lets the host stop covering the frame meanwhile.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PPmy3tPq869XDW4njjVaKA
jared merged commit 41b45bef25 into lotus 2026-09-27 13:05:01 -04:00
Sign in to join this conversation.