lotus(#6): render avatar decorations on in-call tiles
CI / Build embedded bundle (push) Failing after 12m11s
CI / Publish to Gitea npm registry (push) Has been skipped

Adds io.lotus.decorations (toWidget): the host pushes a userId->image-URL
map and EC overlays the profile decoration on each tile avatar
(TileAvatar), keyed by userId, with a useSyncExternalStore-backed store.
Makes A6 first-class in-call instead of absent. URLs are validated
https/blob. Additive: no-op unless the host sends decorations.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Lotus CI
2026-06-29 23:38:45 -04:00
co-authored by Claude Opus 4.8
parent 7d792c33bf
commit 70358d442b
5 changed files with 118 additions and 1 deletions
+17
View File
@@ -18,3 +18,20 @@ Please see LICENSE in the repository root for full details.
/* TODO: make this --cpd-color-fg-primary when available. */
color: var(--cpd-color-text-primary);
}
/* [lotus #6] Wrap so the decoration can overlay the avatar precisely. */
.container {
position: relative;
display: inline-flex;
}
/* [lotus #6] APNG/PNG profile decoration drawn over the in-call tile avatar.
The decoration art shares the avatar's box (transparent padding, ring at the
edges), matching how the host renders it elsewhere. */
.lotusDecoration {
position: absolute;
inset: 0;
margin: auto;
pointer-events: none;
object-fit: contain;
}
+14 -1
View File
@@ -10,6 +10,7 @@ import { InlineSpinner } from "@vector-im/compound-web";
import styles from "./TileAvatar.module.css";
import { Avatar, type Props as AvatarProps } from "../Avatar";
import { useLotusDecoration } from "../lotus/lotusDecorations";
interface Props extends AvatarProps {
size: number;
@@ -17,14 +18,26 @@ interface Props extends AvatarProps {
}
export const TileAvatar: FC<Props> = ({ size, loading, ...props }) => {
// [lotus #6] Profile decoration overlay for this participant, pushed by the
// host via io.lotus.decorations. undefined unless the host opted in.
const decoration = useLotusDecoration(props.id);
return (
<div>
<div className={styles.container}>
{loading && (
<div className={styles.loading}>
<InlineSpinner size={size / 3} />
</div>
)}
<Avatar size={size} {...props} />
{decoration && (
<img
className={styles.lotusDecoration}
src={decoration}
alt=""
aria-hidden
style={{ width: size, height: size }}
/>
)}
</div>
);
};