Fix Meet room tile layout and show account avatars when camera is off.
Deploy Ladill Meet / deploy (push) Successful in 50s
Deploy Ladill Meet / deploy (push) Successful in 50s
Move the participant name strip below the speaker area instead of overlaying it, and use Ladill profile photos on no-video tiles before falling back to initials. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+32
-9
@@ -59,16 +59,46 @@ html.meet-room-page {
|
||||
}
|
||||
|
||||
.meet-room-page .meet-stage--plenary .meet-stage-spotlight {
|
||||
flex: 1 1 auto;
|
||||
min-height: min(70vh, 100%);
|
||||
flex: 1 1 0;
|
||||
min-height: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.meet-room-page .meet-stage--plenary .meet-stage-spotlight-tile {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
max-height: 100%;
|
||||
aspect-ratio: auto;
|
||||
}
|
||||
|
||||
.meet-room-page [data-participant-tile] {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
.meet-room-page [data-tile-body] {
|
||||
position: relative;
|
||||
min-height: 0;
|
||||
flex: 1 1 0;
|
||||
}
|
||||
|
||||
.meet-room-page [data-tile-footer] {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.meet-room-page .meet-audience-strip {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-shrink: 0;
|
||||
align-self: stretch;
|
||||
min-height: 2.75rem;
|
||||
margin-top: 0.25rem;
|
||||
padding: 0.5rem 0 0.25rem;
|
||||
border-top: 1px solid rgb(30 41 59 / 0.8);
|
||||
}
|
||||
|
||||
.meet-room-page .meet-stage--panel #meet-stage-spotlight,
|
||||
.meet-room-page .meet-stage--panel #meet-audience-strip {
|
||||
display: none;
|
||||
@@ -87,13 +117,6 @@ html.meet-room-page {
|
||||
min-height: 12rem;
|
||||
}
|
||||
|
||||
.meet-room-page .meet-audience-strip {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
min-height: 2.75rem;
|
||||
padding: 0.25rem 0;
|
||||
}
|
||||
|
||||
.meet-room-page .meet-audience-avatar {
|
||||
position: relative;
|
||||
display: flex;
|
||||
|
||||
+89
-12
@@ -540,6 +540,82 @@ function meetRoom() {
|
||||
};
|
||||
},
|
||||
|
||||
participantForIdentity(identity) {
|
||||
return this.sessionParticipants.find((entry) => entry.uuid === identity);
|
||||
},
|
||||
|
||||
displayNameForParticipant(participant) {
|
||||
const person = this.participantForIdentity(participant.identity);
|
||||
|
||||
return person?.display_name || participant.name || participant.identity;
|
||||
},
|
||||
|
||||
avatarUrlForParticipant(participant) {
|
||||
const person = this.participantForIdentity(participant.identity);
|
||||
|
||||
if (person?.avatar_url) {
|
||||
return person.avatar_url;
|
||||
}
|
||||
|
||||
if (person && ['host', 'co_host'].includes(person.role) && this.roomHost?.avatar_url) {
|
||||
return this.roomHost.avatar_url;
|
||||
}
|
||||
|
||||
if (this.roomHost?.user_ref && person?.user_ref === this.roomHost.user_ref && this.roomHost.avatar_url) {
|
||||
return this.roomHost.avatar_url;
|
||||
}
|
||||
|
||||
return null;
|
||||
},
|
||||
|
||||
updateTileAvatarCircle(tile, participant) {
|
||||
const circle = tile.querySelector('[data-tile-avatar-circle]');
|
||||
if (!circle) {
|
||||
return;
|
||||
}
|
||||
|
||||
const displayName = this.displayNameForParticipant(participant);
|
||||
const avatarUrl = this.avatarUrlForParticipant(participant);
|
||||
const isAudioTile = tile.classList.contains('meet-audio-tile');
|
||||
const sizeClass = isAudioTile ? 'h-[4.5rem] w-[4.5rem] text-xl' : 'h-20 w-20 text-2xl';
|
||||
|
||||
circle.replaceChildren();
|
||||
|
||||
if (avatarUrl) {
|
||||
circle.className = `flex ${sizeClass} shrink-0 items-center justify-center overflow-hidden rounded-full shadow-lg`;
|
||||
circle.style.background = '';
|
||||
|
||||
const img = document.createElement('img');
|
||||
img.src = avatarUrl;
|
||||
img.alt = '';
|
||||
img.className = 'h-full w-full object-cover';
|
||||
circle.appendChild(img);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const hue = this.avatarHue(displayName);
|
||||
circle.className = `flex ${sizeClass} shrink-0 items-center justify-center rounded-full font-semibold text-white shadow-lg`;
|
||||
circle.style.background = `linear-gradient(135deg, hsl(${hue} 45% 42%), hsl(${(hue + 40) % 360} 50% 28%))`;
|
||||
circle.textContent = this.participantInitials(displayName);
|
||||
},
|
||||
|
||||
refreshParticipantTileAvatars() {
|
||||
if (!room) {
|
||||
return;
|
||||
}
|
||||
|
||||
[room.localParticipant, ...room.remoteParticipants.values()].forEach((participant) => {
|
||||
const tile = this.findParticipantTile(participant.identity);
|
||||
if (tile) {
|
||||
this.updateTileAvatarCircle(tile, participant);
|
||||
tile.querySelector('[data-tile-name]')?.replaceChildren(
|
||||
document.createTextNode(this.displayNameForParticipant(participant)),
|
||||
);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
isSpeakerPerson(person) {
|
||||
if (!person) return false;
|
||||
|
||||
@@ -1020,14 +1096,16 @@ function meetRoom() {
|
||||
tile = document.createElement('div');
|
||||
tile.dataset.participantTile = identity;
|
||||
tile.className = this.audioOnly
|
||||
? 'meet-audio-tile relative flex aspect-square w-full max-w-[9rem] flex-col items-center justify-center overflow-hidden rounded-2xl bg-slate-900'
|
||||
: 'relative aspect-video w-full overflow-hidden rounded-xl bg-slate-900';
|
||||
? 'meet-audio-tile relative flex aspect-square w-full max-w-[9rem] flex-col overflow-hidden rounded-2xl bg-slate-900'
|
||||
: 'relative flex aspect-video w-full flex-col overflow-hidden rounded-xl bg-slate-900 min-h-0';
|
||||
tile.innerHTML = `
|
||||
<div data-tile-stage class="absolute inset-0 flex items-center justify-center"></div>
|
||||
<div data-tile-avatar class="absolute inset-0 flex flex-col items-center justify-center gap-3 px-4">
|
||||
<div data-tile-avatar-circle class="flex h-20 w-20 items-center justify-center rounded-full text-2xl font-semibold text-white shadow-lg"></div>
|
||||
<div data-tile-body class="relative min-h-0 flex-1">
|
||||
<div data-tile-stage class="absolute inset-0 flex items-center justify-center"></div>
|
||||
<div data-tile-avatar class="absolute inset-0 flex items-center justify-center">
|
||||
<div data-tile-avatar-circle class="flex h-20 w-20 shrink-0 items-center justify-center rounded-full text-2xl font-semibold text-white shadow-lg"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="absolute inset-x-0 bottom-0 flex items-center justify-between gap-2 bg-gradient-to-t from-black/80 via-black/40 to-transparent px-3 pb-2 pt-8">
|
||||
<div data-tile-footer class="relative flex shrink-0 items-center justify-between gap-2 border-t border-slate-800/80 bg-slate-950/95 px-3 py-2">
|
||||
<span data-tile-name class="truncate text-xs font-medium text-white"></span>
|
||||
<div class="flex shrink-0 items-center gap-1.5">
|
||||
<span data-tile-speaking class="hidden meet-speaking-badge rounded-full bg-emerald-500/90 p-1 text-white" title="Speaking">
|
||||
@@ -1053,13 +1131,10 @@ function meetRoom() {
|
||||
|
||||
tile.classList.toggle('meet-stage-spotlight-tile', container.id === 'meet-stage-spotlight');
|
||||
tile.classList.toggle('meet-stage-panel-tile', container.id === 'video-grid' && this.stageLayout === 'panel');
|
||||
tile.classList.toggle('aspect-video', container.id !== 'meet-stage-spotlight' && !this.audioOnly);
|
||||
|
||||
const displayName = participant.name || participant.identity;
|
||||
const hue = this.avatarHue(displayName);
|
||||
const circle = tile.querySelector('[data-tile-avatar-circle]');
|
||||
circle.style.background = `linear-gradient(135deg, hsl(${hue} 45% 42%), hsl(${(hue + 40) % 360} 50% 28%))`;
|
||||
circle.textContent = this.participantInitials(displayName);
|
||||
tile.querySelector('[data-tile-name]').textContent = displayName;
|
||||
this.updateTileAvatarCircle(tile, participant);
|
||||
tile.querySelector('[data-tile-name]').textContent = this.displayNameForParticipant(participant);
|
||||
|
||||
const speaking = this.activeSpeakerIds.includes(identity);
|
||||
tile.classList.toggle('meet-tile-speaking', speaking);
|
||||
@@ -1100,6 +1175,7 @@ function meetRoom() {
|
||||
}
|
||||
|
||||
avatar.classList.remove('hidden');
|
||||
this.updateTileAvatarCircle(tile, participant);
|
||||
stage.innerHTML = '';
|
||||
},
|
||||
|
||||
@@ -1727,6 +1803,7 @@ function meetRoom() {
|
||||
if (data.participants) {
|
||||
this.sessionParticipants = data.participants;
|
||||
this.pruneTilesFromSession();
|
||||
this.refreshParticipantTileAvatars();
|
||||
}
|
||||
if (typeof data.uses_stage_layout === 'boolean' || typeof data.stage_layout === 'string') {
|
||||
this.mergeStageConfig(data);
|
||||
|
||||
@@ -217,8 +217,8 @@
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<main class="relative min-h-0 min-w-0 flex-1 p-4">
|
||||
<div id="meet-stage" class="flex h-full min-h-0 flex-col gap-3" :class="usesStageLayout ? 'meet-stage meet-stage--' + stageLayout : ''">
|
||||
<main class="relative flex min-h-0 min-w-0 flex-1 flex-col p-4 pb-2">
|
||||
<div id="meet-stage" class="flex h-full min-h-0 flex-1 flex-col gap-3" :class="usesStageLayout ? 'meet-stage meet-stage--' + stageLayout : ''">
|
||||
<div id="meet-stage-spotlight" class="meet-stage-spotlight relative min-h-0 flex-1 overflow-hidden rounded-xl bg-slate-900"></div>
|
||||
<div id="video-grid" class="grid min-h-0 gap-2 sm:grid-cols-2 lg:grid-cols-3" :class="audioOnly ? 'meet-audio-grid' : (usesStageLayout ? 'meet-stage-panel-grid flex-1' : 'h-full')"></div>
|
||||
<div id="meet-audience-strip" class="meet-audience-strip shrink-0"></div>
|
||||
|
||||
Reference in New Issue
Block a user