diff --git a/resources/js/meet-room.js b/resources/js/meet-room.js
index 093f871..c331916 100644
--- a/resources/js/meet-room.js
+++ b/resources/js/meet-room.js
@@ -735,6 +735,11 @@ function meetRoom() {
},
refreshParticipantTileAvatars() {
+ if (this.isSpace && !this.usesStageLayout) {
+ this.renderSpaceSpeakerTiles();
+ return;
+ }
+
if (!room) {
return;
}
@@ -972,19 +977,121 @@ function meetRoom() {
}
},
+ liveKitParticipantForIdentity(identity) {
+ if (!room) {
+ return null;
+ }
+
+ if (room.localParticipant.identity === identity) {
+ return room.localParticipant;
+ }
+
+ return room.remoteParticipants.get(identity) || null;
+ },
+
+ participantTileShellHtml() {
+ return `
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
`;
+ },
+
+ ensureParticipantTileShell(identity) {
+ let tile = this.findParticipantTile(identity);
+
+ if (!tile) {
+ tile = document.createElement('div');
+ tile.dataset.participantTile = identity;
+ tile.className = this.audioOnly
+ ? 'meet-audio-tile relative flex aspect-square w-full 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 = this.participantTileShellHtml();
+ }
+
+ return tile;
+ },
+
+ updateSessionParticipantTile(person, liveParticipant = null, tile = null) {
+ tile = tile || this.findParticipantTile(person.uuid);
+ if (!tile || !person) {
+ return;
+ }
+
+ const pseudoParticipant = { identity: person.uuid, name: person.display_name };
+ this.updateTileAvatarCircle(tile, pseudoParticipant);
+ tile.querySelector('[data-tile-name]').textContent = person.display_name;
+
+ const speaking = this.isSpeaking(person.uuid);
+ tile.classList.toggle('meet-tile-speaking', speaking);
+ tile.querySelector('[data-tile-speaking]')?.classList.toggle('hidden', !speaking);
+
+ const muted = liveParticipant
+ ? !liveParticipant.isMicrophoneEnabled
+ : Boolean(person.is_muted);
+ tile.querySelector('[data-tile-mic]')?.classList.toggle('hidden', !muted);
+ tile.querySelector('[data-tile-cam]')?.classList.add('hidden');
+ },
+
+ renderSpaceSpeakerTiles() {
+ if (!this.isSpace || this.usesStageLayout) {
+ return;
+ }
+
+ const grid = document.getElementById('video-grid');
+ if (!grid) {
+ return;
+ }
+
+ const speakers = this.speakerParticipantsSorted();
+ const speakerIds = new Set(speakers.map((person) => person.uuid));
+
+ speakers.forEach((person) => {
+ const liveParticipant = this.liveKitParticipantForIdentity(person.uuid);
+ const tile = this.ensureParticipantTileShell(person.uuid);
+
+ if (tile.parentElement !== grid) {
+ grid.appendChild(tile);
+ }
+
+ this.updateSessionParticipantTile(person, liveParticipant, tile);
+ });
+
+ grid.querySelectorAll('[data-participant-tile]').forEach((tile) => {
+ if (!speakerIds.has(tile.dataset.participantTile)) {
+ tile.remove();
+ }
+ });
+
+ this.updateAudioGridLayout();
+ },
+
findParticipantTile(identity) {
return document.querySelector(`[data-participant-tile="${identity}"]`);
},
getTileContainerForIdentity(identity) {
if (!this.usesStageLayout) {
- if (this.isSpace) {
- const person = this.sessionParticipants.find((entry) => entry.uuid === identity);
- if (!person || !['host', 'co_host', 'panelist'].includes(person.role)) {
- return null;
- }
- }
-
return document.getElementById('video-grid');
}
@@ -1063,7 +1170,9 @@ function meetRoom() {
document.getElementById('meet-stage-spotlight')?.replaceChildren();
document.getElementById('meet-audience-strip')?.replaceChildren();
- if (room) {
+ if (this.isSpace) {
+ this.renderSpaceSpeakerTiles();
+ } else if (room) {
const all = [room.localParticipant, ...room.remoteParticipants.values()];
all.forEach((participant) => {
const target = this.getTileContainerForIdentity(participant.identity);
@@ -1085,7 +1194,9 @@ function meetRoom() {
});
}
- this.updateAudioGridLayout();
+ if (!this.isSpace) {
+ this.updateAudioGridLayout();
+ }
return;
}
@@ -1395,36 +1506,7 @@ function meetRoom() {
let tile = existing;
if (!tile) {
- tile = document.createElement('div');
- tile.dataset.participantTile = identity;
- tile.className = this.audioOnly
- ? 'meet-audio-tile relative flex aspect-square w-full 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 = `
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
`;
+ tile = this.ensureParticipantTileShell(identity);
}
if (tile.parentElement !== container) {
@@ -2119,6 +2201,12 @@ function meetRoom() {
syncParticipants() {
if (!room) return;
+ if (this.isSpace && !this.usesStageLayout) {
+ this.renderSpaceSpeakerTiles();
+ this.syncActiveSpeakers(room.activeSpeakers ?? []);
+ return;
+ }
+
const all = [room.localParticipant, ...room.remoteParticipants.values()];
const activeIdentities = new Set(all.map((p) => p.identity));
@@ -2179,12 +2267,21 @@ function meetRoom() {
if (room) {
[room.localParticipant, ...room.remoteParticipants.values()].forEach((participant) => {
+ if (this.isSpace && !this.usesStageLayout) {
+ return;
+ }
+
if (!this.getTileContainerForIdentity(participant.identity)) {
this.removeParticipantTile(participant.identity);
}
});
}
+ if (this.isSpace && !this.usesStageLayout) {
+ this.renderSpaceSpeakerTiles();
+ return;
+ }
+
this.applyStageLayout();
},
@@ -2336,6 +2433,10 @@ function meetRoom() {
this.canPublish = ['host', 'co_host', 'panelist'].includes(participant.role);
this.speakRequested = Boolean(participant.speak_requested);
}
+
+ if (this.isSpace && !this.usesStageLayout) {
+ this.renderSpaceSpeakerTiles();
+ }
},
async refreshMediaAccess() {