Compare commits
4
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c4f6193888 | ||
|
|
7855a0c22f | ||
|
|
aeb72c22b9 | ||
|
|
6fa34911ef |
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@lotusguild/element-call-embedded",
|
||||
"version": "0.25.0-lotus.16",
|
||||
"version": "0.25.0-lotus.18",
|
||||
"files": [
|
||||
"README.md",
|
||||
"LICENSE-AGPL-3.0",
|
||||
|
||||
+14
-1
@@ -90,16 +90,29 @@ body.lotus-transparent[data-background="gradient"]::before {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
/* [cinny #43] lotusHostControls=1: the host renders its own call bar, so EC's
|
||||
footer is hidden but stays in the DOM and in layout-independent position. */
|
||||
body.lotus-host-controls [data-testid="footer-container"] {
|
||||
position: absolute !important;
|
||||
visibility: hidden !important;
|
||||
}
|
||||
|
||||
/* [lotus] Native Lotus/TDS theme, applied when lotusTheme=1, instead of the
|
||||
host injecting CSS into the iframe after load. Overrides Compound design tokens
|
||||
with Lotus values at the source so theming is complete and flash-free. Extend
|
||||
this block with the full Lotus token map from the design system; the canvas
|
||||
override below is a safe starting point that matches the Lotus dark surface. */
|
||||
body.lotus-theme {
|
||||
--cpd-color-bg-canvas-default: #0c0d10;
|
||||
--video-tile-background: var(--cpd-color-bg-subtle-secondary);
|
||||
}
|
||||
|
||||
/* Dark themes only: in the light theme this near-black canvas sat behind
|
||||
light-theme (dark) text, e.g. the tile name tags were unreadable. */
|
||||
body.lotus-theme.cpd-theme-dark,
|
||||
body.lotus-theme.cpd-theme-dark-hc {
|
||||
--cpd-color-bg-canvas-default: #0c0d10;
|
||||
}
|
||||
|
||||
/* [lotus #21] Subtle contrast guard for elements that sit directly on the
|
||||
transparent canvas (no opaque tile background behind them) when
|
||||
lotusTransparent is set: the host's real wallpaper is unknown to us, so a
|
||||
|
||||
@@ -30,6 +30,7 @@ describe("LotusWidgetActions", () => {
|
||||
LotusWidgetActions.SetLayout,
|
||||
LotusWidgetActions.OpenSettings,
|
||||
LotusWidgetActions.ToggleReactions,
|
||||
LotusWidgetActions.SetScreenshare,
|
||||
];
|
||||
|
||||
expect(new Set(LOTUS_TO_WIDGET_ACTIONS)).toEqual(new Set(expectedToWidget));
|
||||
|
||||
@@ -75,7 +75,15 @@ export enum LotusWidgetActions {
|
||||
/** toWidget: toggle the reactions / raise-hand menu (cinny #43). */
|
||||
ToggleReactions = "io.lotus.toggle_reactions",
|
||||
/**
|
||||
* fromWidget: `{ screensharing: boolean, layout: "grid" | "spotlight" | null }`
|
||||
* 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",
|
||||
/**
|
||||
* fromWidget: `{ screensharing: boolean, layout: "grid" | "spotlight" | null,
|
||||
* screenshareAction: true }`
|
||||
* 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.
|
||||
*/
|
||||
@@ -98,4 +106,5 @@ export const LOTUS_TO_WIDGET_ACTIONS: LotusWidgetActions[] = [
|
||||
LotusWidgetActions.SetLayout,
|
||||
LotusWidgetActions.OpenSettings,
|
||||
LotusWidgetActions.ToggleReactions,
|
||||
LotusWidgetActions.SetScreenshare,
|
||||
];
|
||||
|
||||
@@ -7,7 +7,11 @@ Please see LICENSE in the repository root for full details.
|
||||
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
import { parseLayoutPayload, parseSettingsPayload } from "./lotusControls";
|
||||
import {
|
||||
parseLayoutPayload,
|
||||
parseSettingsPayload,
|
||||
shouldToggleScreenshare,
|
||||
} from "./lotusControls";
|
||||
|
||||
describe("parseLayoutPayload", () => {
|
||||
it("accepts grid and spotlight", () => {
|
||||
@@ -33,3 +37,17 @@ describe("parseSettingsPayload", () => {
|
||||
expect(parseSettingsPayload({ open: "yes" }, true)).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe("shouldToggleScreenshare", () => {
|
||||
it("toggles only when an explicit on differs from the current state", () => {
|
||||
expect(shouldToggleScreenshare({ on: true }, false)).toBe(true);
|
||||
expect(shouldToggleScreenshare({ on: true }, true)).toBe(false);
|
||||
expect(shouldToggleScreenshare({ on: false }, true)).toBe(true);
|
||||
expect(shouldToggleScreenshare({ on: false }, false)).toBe(false);
|
||||
});
|
||||
it("toggles when on is missing or not a boolean", () => {
|
||||
expect(shouldToggleScreenshare({}, false)).toBe(true);
|
||||
expect(shouldToggleScreenshare({ on: "yes" }, true)).toBe(true);
|
||||
expect(shouldToggleScreenshare(null, true)).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -21,6 +21,8 @@ export interface LotusControlsState {
|
||||
screensharing: boolean;
|
||||
/** Null while EC offers no layout switch (e.g. PiP or a 1:1 layout). */
|
||||
layout: LayoutMode | null;
|
||||
/** This fork handles `io.lotus.set_screenshare` (always true). */
|
||||
screenshareAction: true;
|
||||
}
|
||||
|
||||
/** `{ layout }` payload → a layout mode, or undefined if invalid. Exported for tests. */
|
||||
@@ -39,13 +41,28 @@ export function parseSettingsPayload(data: unknown, current: boolean): boolean {
|
||||
return !current;
|
||||
}
|
||||
|
||||
/**
|
||||
* `{ on? }` payload + current sharing state → whether to call the toggle.
|
||||
* Exported for tests.
|
||||
*/
|
||||
export function shouldToggleScreenshare(
|
||||
data: unknown,
|
||||
sharing: boolean,
|
||||
): boolean {
|
||||
if (typeof data === "object" && data !== null && "on" in data) {
|
||||
const { on } = data as { on?: unknown };
|
||||
if (typeof on === "boolean") return on !== sharing;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* [cinny #43] Widget-API replacements for the host's DOM access to EC's
|
||||
* controls: layout switch, settings modal and reactions menu, plus a
|
||||
* `controls_state` report (screensharing + layout) so the host no longer reads
|
||||
* EC's DOM for them. Screensharing itself stays host-DOM driven for now:
|
||||
* `getDisplayMedia` needs the user's click to reach this frame (Capability
|
||||
* Delegation), which a plain widget message doesn't carry.
|
||||
* EC's DOM for them. Screenshare start/stop too: `getDisplayMedia` needs the
|
||||
* user's click in this frame, so the host sends `set_screenshare` with
|
||||
* Capability Delegation (Chromium). Elsewhere it keeps clicking EC's button.
|
||||
*
|
||||
* No effect unless the host sends the actions; registering is safe whenever
|
||||
* we're a widget. Returns a teardown function.
|
||||
@@ -69,10 +86,19 @@ export function startLotusControls(vm: CallViewModel): () => void {
|
||||
w.api.transport.reply(ev.detail, {});
|
||||
window.dispatchEvent(new Event(LOTUS_TOGGLE_REACTIONS_EVENT));
|
||||
};
|
||||
const onSetScreenshare = (ev: CustomEvent<IWidgetApiRequest>): void => {
|
||||
w.api.transport.reply(ev.detail, {});
|
||||
// Call synchronously: the delegated activation is only good for a few
|
||||
// seconds after the message arrived.
|
||||
if (shouldToggleScreenshare(ev.detail.data, vm.sharingScreen$.value)) {
|
||||
vm.toggleScreenSharing?.();
|
||||
}
|
||||
};
|
||||
|
||||
w.lazyActions.on(LotusWidgetActions.SetLayout, onSetLayout);
|
||||
w.lazyActions.on(LotusWidgetActions.OpenSettings, onOpenSettings);
|
||||
w.lazyActions.on(LotusWidgetActions.ToggleReactions, onToggleReactions);
|
||||
w.lazyActions.on(LotusWidgetActions.SetScreenshare, onSetScreenshare);
|
||||
|
||||
const sub: Subscription = combineLatest([
|
||||
vm.sharingScreen$,
|
||||
@@ -85,6 +111,7 @@ export function startLotusControls(vm: CallViewModel): () => void {
|
||||
([screensharing, layout]): LotusControlsState => ({
|
||||
screensharing,
|
||||
layout,
|
||||
screenshareAction: true,
|
||||
}),
|
||||
),
|
||||
distinctUntilChanged(
|
||||
@@ -100,5 +127,6 @@ export function startLotusControls(vm: CallViewModel): () => void {
|
||||
w.lazyActions.off(LotusWidgetActions.SetLayout, onSetLayout);
|
||||
w.lazyActions.off(LotusWidgetActions.OpenSettings, onOpenSettings);
|
||||
w.lazyActions.off(LotusWidgetActions.ToggleReactions, onToggleReactions);
|
||||
w.lazyActions.off(LotusWidgetActions.SetScreenshare, onSetScreenshare);
|
||||
};
|
||||
}
|
||||
|
||||
+11
-1
@@ -76,7 +76,17 @@ export const useTheme = (): void => {
|
||||
"lotusTheme anyway since the two flags are only meaningful together.",
|
||||
);
|
||||
}
|
||||
if (lotusTheme || lotusTransparent)
|
||||
if (lotusTheme || lotusTransparent) {
|
||||
document.body.classList.add("lotus-theme");
|
||||
// [cinny #43] The frame's root colour scheme must match the host's, or
|
||||
// the browser paints an opaque backdrop behind the transparent frame.
|
||||
// (The host used to inject `:root { color-scheme }` for this.)
|
||||
document.documentElement.style.colorScheme = theme;
|
||||
}
|
||||
// [cinny #43] The host draws its own call bar: hide EC's footer, but keep
|
||||
// it in the DOM (the host still clicks its screenshare button on engines
|
||||
// without Capability Delegation). Replaces the host's injected styles.
|
||||
if (lotusFlag("lotusHostControls"))
|
||||
document.body.classList.add("lotus-host-controls");
|
||||
}, [previousTheme, requestedTheme]);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user