lotus(#4): focus-participant spotlight via widget action

Adds io.lotus.focus_participant (toWidget): the host can pin a participant
to the spotlight by Matrix user id (or clear with userId:null), via a
manual override injected into CallViewModel.spotlightSpeaker$. Replaces
cinny's fragile DOM .click() tile-selector focus hack. Extracts the action
enum into lotusActions.ts (no circular import) and allow-lists Lotus
toWidget actions in initializeWidget. Additive: no-op unless the host
sends the action.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Lotus CI
2026-06-29 23:10:16 -04:00
co-authored by Claude Opus 4.8
parent 444af5f7ac
commit 9d7784b9bc
6 changed files with 104 additions and 10 deletions
+4
View File
@@ -30,6 +30,7 @@ import { HeaderStyle, useUrlParams } from "../UrlParams";
import { useCallViewKeyboardShortcuts } from "../useCallViewKeyboardShortcuts";
import { widget } from "../widget";
import { startLotusCallState } from "../lotus/lotusCallState";
import { startLotusFocus } from "../lotus/lotusFocus";
import styles from "./InCallView.module.css";
import { GridTile } from "../tile/GridTile";
import { SettingsModal, defaultSettingsTab } from "../settings/SettingsModal";
@@ -283,6 +284,9 @@ export const InCallView: FC<InCallViewProps> = ({
// [lotus] Stream per-participant speaking/mute state to the host (cinny) over
// the widget API when opted in via lotusCallState=1. No-op otherwise.
useEffect(() => startLotusCallState(vm), [vm]);
// [lotus] Handle the host's io.lotus.focus_participant action to pin a
// participant to the spotlight (#4). No-op unless the host sends it.
useEffect(() => startLotusFocus(vm), [vm]);
const fatalCallError = useBehavior(vm.fatalError$);
// Stop the rendering and throw for the error boundary