/* Copyright 2026 Lotus Guild SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial Please see LICENSE in the repository root for full details. */ /** * Custom widget actions exchanged between the Lotus host (cinny) and this fork. * Kept dependency-free so both `widget.ts` and `lotus/*` can import it without * a circular dependency. */ export enum LotusWidgetActions { /** * fromWidget: in-call per-participant speaking / mute state. * NOTE: matrix-widget-api `transport.send` is request/response — the host * MUST register a handler that replies/acks each one (cinny's `listenAction` * does, replying `{}`). If the host has no handler, ClientWidgetApi * immediately error-replies "unsupported from-widget action", so the data is * silently dropped and each throttled send rejects (caught) — functional miss * + log churn, not a hang. */ CallState = "io.lotus.call_state", /** toWidget: pin/spotlight (or clear, with userId=null) a participant. */ FocusParticipant = "io.lotus.focus_participant", /** toWidget: play an audio clip into the call as a separate published track. */ InjectAudio = "io.lotus.inject_audio", /** toWidget: set audio/screenshare encoding quality (bitrate/framerate). */ SetQuality = "io.lotus.set_quality", /** toWidget: per-user avatar-decoration image URLs for in-call tiles. */ Decorations = "io.lotus.decorations", /** toWidget: deafen remote audio (and optionally mute screenshare audio). */ SetDeafen = "io.lotus.set_deafen", /** * fromWidget: sent on (re)registration of a lotus toWidget handler (e.g. * decorations) so the host can re-push state that would otherwise only be * sent on change (decorations roster, the current focus pin) after an * EC-side reconnect/remount. cinny's `resendForkState()` should respond to * this the same way it responds to a fresh join. */ RequestState = "io.lotus.request_state", /** * fromWidget: real state of the in-source denoise engine — * `{ active: boolean, model: string, error?: string }` — sent once the * processor attaches (or after the rnnoise fallback also fails), so the host * toggle can reflect reality rather than the requested state. */ DenoiseState = "io.lotus.denoise_state", /** * fromWidget: one-shot end-of-call readout for the local participant — * `{ durationMs, reconnects, poorMs, verdict }` (#143) — sent on SFU * disconnect or in-call teardown, whichever comes first. */ CallSummary = "io.lotus.call_summary", /** toWidget: select the audio output device `{ deviceId }` (#119). */ SetAudioOutput = "io.lotus.set_audio_output", /** fromWidget: the currently selected output `{ deviceId }` (#119). */ AudioOutputState = "io.lotus.audio_output_state", /** * fromWidget: local screenshare reminder `{ kind: "ended" | "no-frames" | "alone" }` * — window closed, no frames for 15 s, or 30 min of sharing with nobody * else in the call (#39). Each fires at most once per share. */ ScreenshareNotice = "io.lotus.screenshare_notice", /** * toWidget: switch the call layout `{ layout: "grid" | "spotlight" }` * (cinny #43 — replaces the host clicking EC's hidden layout radio). */ SetLayout = "io.lotus.set_layout", /** * toWidget: open EC's settings modal, or close it with `{ open: false }`; * omit `open` to toggle (cinny #43). */ OpenSettings = "io.lotus.open_settings", /** toWidget: toggle the reactions / raise-hand menu (cinny #43). */ ToggleReactions = "io.lotus.toggle_reactions", /** * toWidget: start/stop sharing `{ on?: boolean }` (omit to toggle; cinny #43). * Starting calls getDisplayMedia, which needs the user's click: the host * sends this with Capability Delegation (`delegate: "display-capture"`), and * without it the browser rejects the share. */ SetScreenshare = "io.lotus.set_screenshare", /** * toWidget: `{ codes: string[] }` — KeyboardEvent.code values (PTT, deafen) * to report while focus is inside this frame (cinny #43). `[]` stops. */ SetHotkeys = "io.lotus.set_hotkeys", /** * toWidget: `{ visible: boolean }` — whether the in-frame screenshare button * (`lotusFrameScreenshare`) may show, e.g. false while the room's call * policy forbids screensharing (cinny #43). Defaults to visible. */ SetFrameScreenshare = "io.lotus.set_frame_screenshare", /** * toWidget: `{}` — show the "Share your screen?" prompt inside this frame * (cinny #43). For hosts that can't delegate the user's click: the prompt's * Share button is clicked here, so getDisplayMedia gets its activation. */ PromptScreenshare = "io.lotus.prompt_screenshare", /** * fromWidget: a watched key went down/up here, or this window's focus * changed; see `LotusHotkeyReport` (cinny #43). */ Hotkey = "io.lotus.hotkey", /** * fromWidget: `{ screensharing: boolean, layout: "grid" | "spotlight" | null, * screenshareAction: true, hotkeys: true, frameScreenshare: false, * screensharePrompt: boolean, screensharePromptOpen: boolean }` * whenever either changes (cinny #43), so the host stops reading EC's DOM for * them. Its arrival also tells the host this fork supports the actions above. */ ControlsState = "io.lotus.controls_state", /** * fromWidget: local mic level `{ bars: 0 | 1 | 2 | 3 }` (cinny #146), sent * only when it changes (≤ 10 Hz); 0 while muted or with no mic. */ MicLevel = "io.lotus.mic_level", /** * fromWidget: the call's error page is up (`{ code, recoverable }`) or * cleared (`{ code: null }`) (cinny #318), so the host stops showing the call * as joined after "Connection lost". */ CallError = "io.lotus.call_error", /** toWidget: press the error page's Reconnect / Try again (cinny #318). */ Reconnect = "io.lotus.reconnect", } /** toWidget Lotus actions that must be allow-listed in `initializeWidget`. */ export const LOTUS_TO_WIDGET_ACTIONS: LotusWidgetActions[] = [ LotusWidgetActions.FocusParticipant, LotusWidgetActions.InjectAudio, LotusWidgetActions.SetQuality, LotusWidgetActions.Decorations, LotusWidgetActions.SetDeafen, LotusWidgetActions.SetAudioOutput, LotusWidgetActions.SetLayout, LotusWidgetActions.OpenSettings, LotusWidgetActions.ToggleReactions, LotusWidgetActions.SetScreenshare, LotusWidgetActions.SetHotkeys, LotusWidgetActions.SetFrameScreenshare, LotusWidgetActions.PromptScreenshare, LotusWidgetActions.Reconnect, ];