Fix participant card layout and add meeting video pop-out.
Deploy Ladill Meet / deploy (push) Successful in 56s
Deploy Ladill Meet / deploy (push) Successful in 56s
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+48
-9
@@ -62,16 +62,28 @@ html.meet-room-page {
|
|||||||
margin-left: auto;
|
margin-left: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.meet-room-page .meet-tile-speaking {
|
.meet-room-page [data-participant-tile].meet-tile-speaking::after {
|
||||||
box-shadow: 0 0 0 2px rgb(52 211 153 / 0.9);
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
inset: 0;
|
||||||
|
border-radius: inherit;
|
||||||
|
box-shadow: inset 0 0 0 2px rgb(52 211 153 / 0.9);
|
||||||
|
pointer-events: none;
|
||||||
|
z-index: 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
.meet-room-page .meet-stage-spotlight:has(.meet-tile-speaking) {
|
.meet-room-page .meet-stage-spotlight:has(.meet-tile-speaking)::after {
|
||||||
box-shadow: 0 0 0 2px rgb(52 211 153 / 0.9);
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
inset: 0;
|
||||||
|
border-radius: inherit;
|
||||||
|
box-shadow: inset 0 0 0 2px rgb(52 211 153 / 0.9);
|
||||||
|
pointer-events: none;
|
||||||
|
z-index: 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
.meet-room-page .meet-stage-spotlight .meet-tile-speaking {
|
.meet-room-page .meet-stage-spotlight .meet-tile-speaking::after {
|
||||||
box-shadow: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.meet-room-page #meet-stage:not(.meet-stage) #meet-stage-spotlight,
|
.meet-room-page #meet-stage:not(.meet-stage) #meet-stage-spotlight,
|
||||||
@@ -123,6 +135,7 @@ html.meet-room-page {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.meet-room-page [data-participant-tile] {
|
.meet-room-page [data-participant-tile] {
|
||||||
|
position: relative;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
min-height: 0;
|
min-height: 0;
|
||||||
@@ -237,6 +250,22 @@ html.meet-room-page {
|
|||||||
position: relative;
|
position: relative;
|
||||||
min-height: 0;
|
min-height: 0;
|
||||||
flex: 1 1 0;
|
flex: 1 1 0;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.meet-room-page [data-tile-stage] {
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.meet-room-page [data-tile-stage] video,
|
||||||
|
.meet-room-page [data-tile-stage] .meet-tile-video {
|
||||||
|
display: block;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
max-width: 100%;
|
||||||
|
max-height: 100%;
|
||||||
|
object-fit: cover;
|
||||||
|
object-position: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.meet-room-page [data-tile-footer] {
|
.meet-room-page [data-tile-footer] {
|
||||||
@@ -396,12 +425,22 @@ html.meet-room-page {
|
|||||||
box-shadow: none;
|
box-shadow: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.meet-room-page .meet-space-tile.meet-tile-speaking {
|
.meet-room-page .meet-space-tile.meet-tile-speaking::after {
|
||||||
box-shadow: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.meet-room-page .meet-space-tile.meet-tile-speaking [data-tile-avatar-circle] {
|
.meet-room-page .meet-space-tile.meet-tile-speaking [data-tile-avatar-circle] {
|
||||||
box-shadow: 0 0 0 2px rgb(56 189 248 / 0.85);
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.meet-room-page .meet-space-tile.meet-tile-speaking [data-tile-avatar-circle]::after {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
inset: 0;
|
||||||
|
border-radius: inherit;
|
||||||
|
box-shadow: inset 0 0 0 2px rgb(56 189 248 / 0.85);
|
||||||
|
pointer-events: none;
|
||||||
|
z-index: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.meet-room-page .meet-space-tile--listener [data-tile-avatar-circle] {
|
.meet-room-page .meet-space-tile--listener [data-tile-avatar-circle] {
|
||||||
|
|||||||
+108
-3
@@ -177,6 +177,8 @@ function meetRoom() {
|
|||||||
activeSpeakerIds: [],
|
activeSpeakerIds: [],
|
||||||
floatingReactions: [],
|
floatingReactions: [],
|
||||||
reactionsInitialized: false,
|
reactionsInitialized: false,
|
||||||
|
pictureInPictureActive: false,
|
||||||
|
pictureInPictureSupported: false,
|
||||||
pollTimer: null,
|
pollTimer: null,
|
||||||
config: {},
|
config: {},
|
||||||
|
|
||||||
@@ -313,6 +315,8 @@ function meetRoom() {
|
|||||||
this.syncToolbarConditionalVisibility();
|
this.syncToolbarConditionalVisibility();
|
||||||
document.querySelector('.meet-toolbar__track')?.classList.add('is-ready');
|
document.querySelector('.meet-toolbar__track')?.classList.add('is-ready');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.setupPictureInPicture();
|
||||||
},
|
},
|
||||||
|
|
||||||
connectionErrorMessage(error) {
|
connectionErrorMessage(error) {
|
||||||
@@ -1054,8 +1058,8 @@ function meetRoom() {
|
|||||||
|
|
||||||
participantTileShellHtml() {
|
participantTileShellHtml() {
|
||||||
return `
|
return `
|
||||||
<div data-tile-body class="relative min-h-0 flex-1">
|
<div data-tile-body class="relative min-h-0 flex-1 overflow-hidden">
|
||||||
<div data-tile-stage class="absolute inset-0 flex items-center justify-center"></div>
|
<div data-tile-stage class="absolute inset-0 overflow-hidden"></div>
|
||||||
<div data-tile-avatar class="absolute inset-0 flex items-center justify-center">
|
<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 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>
|
||||||
@@ -1682,7 +1686,7 @@ function meetRoom() {
|
|||||||
stage.innerHTML = '';
|
stage.innerHTML = '';
|
||||||
|
|
||||||
const el = track.attach();
|
const el = track.attach();
|
||||||
el.className = 'h-full w-full object-cover';
|
el.className = 'meet-tile-video';
|
||||||
el.dataset.participant = participant.identity;
|
el.dataset.participant = participant.identity;
|
||||||
stage.appendChild(el);
|
stage.appendChild(el);
|
||||||
},
|
},
|
||||||
@@ -2488,6 +2492,107 @@ function meetRoom() {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
setupPictureInPicture() {
|
||||||
|
this.pictureInPictureSupported = typeof document !== 'undefined'
|
||||||
|
&& 'pictureInPictureEnabled' in document
|
||||||
|
&& document.pictureInPictureEnabled !== false;
|
||||||
|
|
||||||
|
if (!this.pictureInPictureSupported) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
document.addEventListener('visibilitychange', () => {
|
||||||
|
if (document.hidden) {
|
||||||
|
this.maybeAutoEnterPictureInPicture();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
document.addEventListener('leavepictureinpicture', () => {
|
||||||
|
this.pictureInPictureActive = false;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
showPictureInPictureControl() {
|
||||||
|
return this.pictureInPictureSupported && !this.audioOnly;
|
||||||
|
},
|
||||||
|
|
||||||
|
findPictureInPictureVideo() {
|
||||||
|
if (this.isScreenSharing) {
|
||||||
|
const screenVideo = document.querySelector('#screen-share video');
|
||||||
|
if (screenVideo) {
|
||||||
|
return screenVideo;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const localId = this.config.participantUuid;
|
||||||
|
const priorities = [];
|
||||||
|
|
||||||
|
if (room?.localParticipant?.isCameraEnabled) {
|
||||||
|
priorities.push(localId);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.activeSpeakerIds.forEach((id) => {
|
||||||
|
if (!priorities.includes(id)) {
|
||||||
|
priorities.push(id);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
for (const id of priorities) {
|
||||||
|
const video = this.findParticipantTile(id)?.querySelector('[data-tile-stage] video');
|
||||||
|
if (video) {
|
||||||
|
return video;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return document.querySelector('[data-participant-tile] [data-tile-stage] video');
|
||||||
|
},
|
||||||
|
|
||||||
|
async maybeAutoEnterPictureInPicture() {
|
||||||
|
if (!this.pictureInPictureSupported || this.pictureInPictureActive || this.audioOnly) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (document.pictureInPictureElement) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await this.enterPictureInPicture({ silent: true });
|
||||||
|
},
|
||||||
|
|
||||||
|
async togglePictureInPicture() {
|
||||||
|
if (!this.pictureInPictureSupported) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (document.pictureInPictureElement) {
|
||||||
|
await document.exitPictureInPicture();
|
||||||
|
this.pictureInPictureActive = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await this.enterPictureInPicture();
|
||||||
|
},
|
||||||
|
|
||||||
|
async enterPictureInPicture({ silent = false } = {}) {
|
||||||
|
const video = this.findPictureInPictureVideo();
|
||||||
|
if (!video) {
|
||||||
|
if (!silent) {
|
||||||
|
this.connectionStatus = 'No video available to pop out';
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await video.requestPictureInPicture();
|
||||||
|
this.pictureInPictureActive = true;
|
||||||
|
} catch {
|
||||||
|
if (!silent) {
|
||||||
|
this.connectionStatus = 'Pop-out blocked — try again from the toolbar';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
async raiseHand() {
|
async raiseHand() {
|
||||||
await fetch(this.config.raiseHandUrl, {
|
await fetch(this.config.raiseHandUrl, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
|
|||||||
@@ -427,6 +427,16 @@
|
|||||||
title="Share screen">
|
title="Share screen">
|
||||||
@include('meet.room.partials.meet-icon', ['icon' => 'screen-share'])
|
@include('meet.room.partials.meet-icon', ['icon' => 'screen-share'])
|
||||||
</button>
|
</button>
|
||||||
|
<button @click="togglePictureInPicture()" type="button"
|
||||||
|
x-show="showPictureInPictureControl()"
|
||||||
|
data-alpine-toolbar
|
||||||
|
class="rounded-full p-3 transition-colors"
|
||||||
|
:class="pictureInPictureActive ? 'bg-emerald-600 hover:bg-emerald-500' : 'bg-slate-700 hover:bg-slate-600'"
|
||||||
|
title="Pop out meeting video">
|
||||||
|
<svg class="h-5 w-5" fill="none" stroke="currentColor" stroke-width="1.5" viewBox="0 0 24 24" aria-hidden="true">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" d="M7.5 4.5h9a3 3 0 0 1 3 3v9m-12-12v12a3 3 0 0 0 3 3h9M15.75 9.75h4.5v4.5"/>
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
<button @click="raiseHand()" type="button"
|
<button @click="raiseHand()" type="button"
|
||||||
x-show="showPrimaryToolbarAudienceControls()"
|
x-show="showPrimaryToolbarAudienceControls()"
|
||||||
data-toolbar-conditional
|
data-toolbar-conditional
|
||||||
|
|||||||
Reference in New Issue
Block a user