Replace meet toolbar icons and move host tools into More menu.
Deploy Ladill Meet / deploy (push) Successful in 1m53s

Use custom meet-icons assets, custom breakout/end modals instead of native prompts, and relocate share files, recording, and lock controls under the More dropdown.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
isaacclad
2026-07-01 08:40:30 +00:00
co-authored by Cursor
parent a546d2f0c0
commit 9c29325a56
10 changed files with 138 additions and 42 deletions
+41 -2
View File
@@ -31,6 +31,9 @@ function meetRoom() {
chatInput: '',
shareOpen: false,
shareCopied: false,
breakoutModalOpen: false,
breakoutCount: '4',
endMeetingConfirmOpen: false,
joinUrl: '',
passcode: '',
roomTitle: '',
@@ -446,18 +449,54 @@ function meetRoom() {
async createBreakouts() {
if (!this.isHost || !this.config.breakoutsCreateUrl) return;
const count = parseInt(prompt('Number of breakout rooms?', '4') || '0', 10);
if (!count) return;
this.breakoutCount = '4';
this.breakoutModalOpen = true;
this.featuresOpen = false;
},
async submitBreakouts() {
if (!this.isHost || !this.config.breakoutsCreateUrl) return;
const count = parseInt(this.breakoutCount, 10);
if (!count || count < 1) return;
await fetch(this.config.breakoutsCreateUrl, {
method: 'POST',
headers: this.headers(),
body: JSON.stringify({ count }),
});
this.breakoutModalOpen = false;
},
async closeBreakouts() {
if (!this.isHost || !this.config.breakoutsCloseUrl) return;
await fetch(this.config.breakoutsCloseUrl, { method: 'POST', headers: this.headers() });
this.featuresOpen = false;
},
openUploadPicker() {
document.getElementById('meet-file-input')?.click();
this.featuresOpen = false;
},
async toggleRecordingFromMenu() {
await this.toggleRecording();
this.featuresOpen = false;
},
async toggleLockFromMenu() {
await this.toggleLock();
this.featuresOpen = false;
},
openEndMeetingConfirm() {
this.endMeetingConfirmOpen = true;
},
submitEndMeeting() {
document.getElementById('meet-end-form')?.requestSubmit();
this.endMeetingConfirmOpen = false;
},
async uploadFile(event) {
@@ -0,0 +1,17 @@
@props(['icon' => null, 'title', 'subtitle' => null])
<button type="button" {{ $attributes->merge(['class' => 'flex w-full items-center gap-3 rounded-xl px-3 py-2.5 text-left text-sm text-slate-200 transition-colors hover:bg-slate-800']) }}>
<span class="flex h-9 w-9 shrink-0 items-center justify-center rounded-lg bg-slate-800/80">
@if ($icon)
@include('meet.room.partials.meet-icon', ['icon' => $icon, 'class' => 'h-5 w-5'])
@else
{{ $slot }}
@endif
</span>
<span class="min-w-0">
<span class="block font-medium">{{ $title }}</span>
@if ($subtitle)
<span class="block text-xs text-slate-400">{{ $subtitle }}</span>
@endif
</span>
</button>
+67 -37
View File
@@ -63,7 +63,7 @@
<button @click="openShare()" type="button"
class="inline-flex items-center gap-1.5 rounded-lg border border-slate-700 bg-slate-900 px-2.5 py-1.5 text-xs font-medium text-slate-200 transition-colors hover:bg-slate-800"
title="Share meeting link">
<svg class="h-4 w-4" fill="none" stroke="currentColor" stroke-width="1.5" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" d="M7.217 10.907a2.25 2.25 0 1 0 0 2.186m0-2.186c.18.324.283.696.283 1.093s-.103.77-.283 1.093m0-2.186 9.566-5.314m-9.566 7.5 9.566 5.314m0 0a2.25 2.25 0 1 0 3.935 2.186 2.25 2.25 0 0 0-3.935-2.186Zm0-12.814a2.25 2.25 0 1 0 3.935-2.186 2.25 2.25 0 0 0-3.935 2.186Z"/></svg>
@include('meet.room.partials.meet-icon', ['icon' => 'share', 'class' => 'h-4 w-4'])
Share
</button>
<span x-show="isRecording" class="rounded bg-red-600 px-2 py-0.5 font-medium text-white">REC</span>
@@ -110,7 +110,7 @@
<template x-for="reaction in floatingReactions" :key="reaction.id">
<span class="flex h-10 w-10 animate-bounce items-center justify-center rounded-full bg-slate-800/90 ring-1 ring-white/20">
<img x-show="reaction.kind === 'thumbs'" src="{{ asset('images/meet-icons/applause.svg') }}" alt="" class="meet-toolbar-icon h-5 w-5">
<svg x-show="reaction.kind === 'heart'" class="h-5 w-5 text-rose-400" fill="currentColor" viewBox="0 0 24 24"><path d="M11.645 20.91l-.007-.003-.022-.012a15.247 15.247 0 0 1-.383-.218 25.18 25.18 0 0 1-4.244-3.17C4.688 15.36 2.25 12.174 2.25 8.25 2.25 5.322 4.714 3 7.688 3A5.5 5.5 0 0 1 12 5.052 5.5 5.5 0 0 1 16.313 3c2.973 0 5.437 2.322 5.437 5.25 0 3.925-2.438 7.111-4.739 9.256a25.175 25.175 0 0 1-4.244 3.17 15.247 15.247 0 0 1-.383.219l-.022.012-.007.004-.003.001a.752.752 0 0 1-.704 0l-.003-.001Z"/></svg>
<img x-show="reaction.kind === 'heart'" src="{{ asset('images/meet-icons/react.svg') }}" alt="" class="meet-toolbar-icon h-5 w-5">
</span>
</template>
</div>
@@ -187,7 +187,7 @@
<button @click="openShare()" type="button"
class="rounded-full bg-slate-700 p-3 hover:bg-slate-600"
title="Share meeting link">
<svg class="h-5 w-5" fill="none" stroke="currentColor" stroke-width="1.5" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" d="M7.217 10.907a2.25 2.25 0 1 0 0 2.186m0-2.186c.18.324.283.696.283 1.093s-.103.77-.283 1.093m0-2.186 9.566-5.314m-9.566 7.5 9.566 5.314m0 0a2.25 2.25 0 1 0 3.935 2.186 2.25 2.25 0 0 0-3.935-2.186Zm0-12.814a2.25 2.25 0 1 0 3.935-2.186 2.25 2.25 0 0 0-3.935 2.186Z"/></svg>
@include('meet.room.partials.meet-icon', ['icon' => 'share'])
</button>
<button @click="raiseHand()" type="button" class="rounded-full bg-slate-700 p-3 hover:bg-slate-600" title="Raise hand">
@include('meet.room.partials.meet-icon', ['icon' => 'raise-hand'])
@@ -196,33 +196,15 @@
@include('meet.room.partials.meet-icon', ['icon' => 'applause'])
</button>
<button @click="sendReaction('heart')" type="button" class="rounded-full bg-slate-700 p-3 hover:bg-slate-600" title="React">
<svg class="h-5 w-5" fill="none" stroke="currentColor" stroke-width="1.5" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" d="M21 8.25c0-2.485-2.099-4.5-4.688-4.5-1.935 0-3.597 1.126-4.312 2.733-.715-1.607-2.377-2.733-4.313-2.733C5.1 3.75 3 5.765 3 8.25c0 7.22 9 12 9 12s9-4.78 9-12Z"/></svg>
@include('meet.room.partials.meet-icon', ['icon' => 'react'])
</button>
<button @click="toggleChat()" type="button"
class="rounded-full p-3 transition-colors"
:class="chatOpen ? 'bg-indigo-600 hover:bg-indigo-500' : 'bg-slate-700 hover:bg-slate-600'"
title="Chat">
<svg class="h-5 w-5" fill="none" stroke="currentColor" stroke-width="1.5" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" d="M8.625 9.75a.375.375 0 1 1-.75 0 .375.375 0 0 1 .75 0Zm3.75 0a.375.375 0 1 1-.75 0 .375.375 0 0 1 .75 0Zm3.75 0a.375.375 0 1 1-.75 0 .375.375 0 0 1 .75 0Zm-8.25 3.75a.375.375 0 1 1-.75 0 .375.375 0 0 1 .75 0Zm3.75 0a.375.375 0 1 1-.75 0 .375.375 0 0 1 .75 0Zm3.75 0a.375.375 0 1 1-.75 0 .375.375 0 0 1 .75 0Z"/><path stroke-linecap="round" stroke-linejoin="round" d="M2.25 12.76c0 1.6 1.123 2.994 2.707 3.227 1.087.16 2.185.283 3.293.369V21l4.184-4.183a1.14 1.14 0 0 1 .778-.332 48.294 48.294 0 0 0 5.83-.498c1.585-.233 2.708-1.626 2.708-3.228V6.741c0-1.602-1.123-2.995-2.707-3.228A48.394 48.394 0 0 0 12 3c-2.392 0-4.744.175-7.043.513C3.373 3.746 2.25 5.14 2.25 6.741v6.018Z"/></svg>
@include('meet.room.partials.meet-icon', ['icon' => 'chat'])
</button>
<input type="file" id="meet-file-input" class="hidden" @change="uploadFile">
<button @click="document.getElementById('meet-file-input').click()" type="button" class="rounded-full bg-slate-700 p-3 hover:bg-slate-600" title="Upload file">
@include('meet.room.partials.meet-icon', ['icon' => 'upload-file'])
</button>
@if ($participant->isHost())
<button @click="toggleRecording()" type="button"
class="rounded-full p-3 transition-colors"
:class="isRecording ? 'bg-red-600 hover:bg-red-500' : 'bg-slate-700 hover:bg-slate-600'"
:title="isRecording ? 'Stop recording' : 'Start recording'">
<svg class="h-5 w-5" fill="currentColor" viewBox="0 0 24 24"><circle cx="12" cy="12" r="5"/></svg>
</button>
<button @click="toggleLock()" type="button"
class="rounded-full p-3 transition-colors"
:class="isLocked ? 'bg-amber-600 hover:bg-amber-500' : 'bg-slate-700 hover:bg-slate-600'"
:title="isLocked ? 'Unlock meeting' : 'Lock meeting'">
<svg x-show="!isLocked" class="h-5 w-5" fill="none" stroke="currentColor" stroke-width="1.5" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" d="M16.5 10.5V6.75a4.5 4.5 0 0 0-9 0v3.75m-.75 11.25h10.5a2.25 2.25 0 0 0 2.25-2.25v-6.75a2.25 2.25 0 0 0-2.25-2.25H6.75a2.25 2.25 0 0 0-2.25 2.25v6.75a2.25 2.25 0 0 0 2.25 2.25Z"/></svg>
<svg x-show="isLocked" x-cloak class="h-5 w-5" fill="none" stroke="currentColor" stroke-width="1.5" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" d="M16.5 10.5V6.75a4.5 4.5 0 1 0-9 0v3.75M3.75 21h16.5A2.25 2.25 0 0 0 22.5 18.75v-6.75A2.25 2.25 0 0 0 20.25 9.75H3.75A2.25 2.25 0 0 0 1.5 12.75v6.75A2.25 2.25 0 0 0 3.75 21Z"/></svg>
</button>
@endif
<button @click="toggleFeatures()" type="button"
class="inline-flex items-center gap-1.5 rounded-full px-3.5 py-3 text-xs font-medium transition-colors"
:class="featuresOpen ? 'bg-indigo-600 hover:bg-indigo-500' : 'bg-slate-700 hover:bg-slate-600'"
@@ -235,9 +217,10 @@
<button type="submit" class="rounded-full bg-red-600 px-5 py-3 text-sm font-medium hover:bg-red-500">Leave</button>
</form>
@if ($participant->isHost())
<form method="POST" action="{{ route('meet.room.end', $session) }}" class="inline" onsubmit="return confirm('End meeting for everyone?')">
<button @click="openEndMeetingConfirm()" type="button"
class="rounded-full border border-red-500 px-4 py-3 text-sm text-red-400 hover:bg-red-950">End</button>
<form id="meet-end-form" method="POST" action="{{ route('meet.room.end', $session) }}" class="hidden">
@csrf
<button type="submit" class="rounded-full border border-red-500 px-4 py-3 text-sm text-red-400 hover:bg-red-950">End</button>
</form>
@endif
</div>
@@ -261,27 +244,37 @@
</div>
<div class="space-y-1 p-2">
<x-meet.room.menu-item icon="upload-file" title="Share files" subtitle="Upload a file for everyone in the call"
@click="openUploadPicker()" />
@if ($participant->isHost())
<button @click="createBreakouts(); featuresOpen = false" type="button"
<button @click="toggleRecordingFromMenu()" type="button"
class="flex w-full items-center gap-3 rounded-xl px-3 py-2.5 text-left text-sm text-slate-200 transition-colors hover:bg-slate-800">
<span class="flex h-9 w-9 items-center justify-center rounded-lg bg-indigo-500/15 text-indigo-300">
<svg class="h-5 w-5" fill="none" stroke="currentColor" stroke-width="1.5" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" d="M3.75 6A2.25 2.25 0 0 1 6 3.75h2.25A2.25 2.25 0 0 1 10.5 6v2.25a2.25 2.25 0 0 1-2.25 2.25H6a2.25 2.25 0 0 1-2.25-2.25V6ZM3.75 15.75A2.25 2.25 0 0 1 6 13.5h2.25a2.25 2.25 0 0 1 2.25 2.25V18a2.25 2.25 0 0 1-2.25 2.25H6A2.25 2.25 0 0 1 3.75 18v-2.25ZM13.5 6a2.25 2.25 0 0 1 2.25-2.25H18A2.25 2.25 0 0 1 20.25 6v2.25A2.25 2.25 0 0 1 18 10.5h-2.25a2.25 2.25 0 0 1-2.25-2.25V6ZM13.5 15.75a2.25 2.25 0 0 1 2.25-2.25H18a2.25 2.25 0 0 1 2.25 2.25V18A2.25 2.25 0 0 1 18 20.25h-2.25A2.25 2.25 0 0 1 13.5 18v-2.25Z"/></svg>
<span class="flex h-9 w-9 shrink-0 items-center justify-center rounded-lg bg-red-500/15">
<span class="h-3 w-3 rounded-full" :class="isRecording ? 'bg-red-500 animate-pulse' : 'bg-red-400'"></span>
</span>
<span>
<span class="block font-medium">Start breakouts</span>
<span class="block text-xs text-slate-400">Split participants into smaller rooms</span>
<span class="min-w-0">
<span class="block font-medium" x-text="isRecording ? 'Stop recording' : 'Start recording'"></span>
<span class="block text-xs text-slate-400">Save this meeting to the cloud</span>
</span>
</button>
<button @click="closeBreakouts(); featuresOpen = false" type="button"
<button @click="toggleLockFromMenu()" type="button"
class="flex w-full items-center gap-3 rounded-xl px-3 py-2.5 text-left text-sm text-slate-200 transition-colors hover:bg-slate-800">
<span class="flex h-9 w-9 items-center justify-center rounded-lg bg-slate-700 text-slate-300">
<svg class="h-5 w-5" fill="none" stroke="currentColor" stroke-width="1.5" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" d="M9 9V4.5M9 9H4.5M9 9 3.75M9 15v4.5M9 15H4.5M9 15l-5.25 5.25M15 9h4.5M15 9V4.5M15 9l5.25-5.25M15 15h4.5M15 15v4.5m0-4.5 5.25 5.25"/></svg>
<span class="flex h-9 w-9 shrink-0 items-center justify-center rounded-lg bg-slate-800/80">
@include('meet.room.partials.meet-icon', ['icon' => 'lock', 'class' => 'h-5 w-5'])
</span>
<span>
<span class="block font-medium">Close breakouts</span>
<span class="block text-xs text-slate-400">Bring everyone back to the main room</span>
<span class="min-w-0">
<span class="block font-medium" x-text="isLocked ? 'Unlock meeting' : 'Lock meeting'"></span>
<span class="block text-xs text-slate-400">Control who can join</span>
</span>
</button>
<x-meet.room.menu-item icon="start-breakout" title="Start breakouts" subtitle="Split participants into smaller rooms"
@click="createBreakouts()" />
<x-meet.room.menu-item icon="close-breakout" title="Close breakouts" subtitle="Bring everyone back to the main room"
@click="closeBreakouts()" />
@endif
</div>
@@ -308,6 +301,43 @@
<p x-show="breakoutBroadcast" class="border-t border-slate-800 bg-amber-950/40 px-4 py-3 text-sm text-amber-100" x-text="breakoutBroadcast"></p>
</div>
</footer>
<div x-show="breakoutModalOpen" x-cloak
class="fixed inset-0 z-50 flex items-center justify-center bg-black/60 p-4 backdrop-blur-sm"
@keydown.escape.window="breakoutModalOpen = false">
<div class="w-full max-w-sm rounded-2xl border border-slate-700 bg-slate-900 p-5 shadow-2xl" @click.outside="breakoutModalOpen = false">
<h3 class="text-base font-semibold text-white">Start breakout rooms</h3>
<p class="mt-1 text-sm text-slate-400">How many breakout rooms should be created?</p>
<form @submit.prevent="submitBreakouts()" class="mt-4 space-y-4">
<div>
<label for="breakout-count" class="sr-only">Number of rooms</label>
<input id="breakout-count" type="number" min="1" max="20" x-model="breakoutCount"
class="w-full rounded-xl border-slate-700 bg-slate-950 px-3 py-2.5 text-sm text-white focus:border-indigo-500 focus:ring-indigo-500">
</div>
<div class="flex justify-end gap-2">
<button type="button" @click="breakoutModalOpen = false"
class="rounded-xl px-4 py-2 text-sm font-medium text-slate-300 hover:bg-slate-800">Cancel</button>
<button type="submit"
class="rounded-xl bg-indigo-600 px-4 py-2 text-sm font-medium text-white hover:bg-indigo-500">Create rooms</button>
</div>
</form>
</div>
</div>
<div x-show="endMeetingConfirmOpen" x-cloak
class="fixed inset-0 z-50 flex items-center justify-center bg-black/60 p-4 backdrop-blur-sm"
@keydown.escape.window="endMeetingConfirmOpen = false">
<div class="w-full max-w-sm rounded-2xl border border-slate-700 bg-slate-900 p-5 shadow-2xl" @click.outside="endMeetingConfirmOpen = false">
<h3 class="text-base font-semibold text-white">End meeting for everyone?</h3>
<p class="mt-2 text-sm text-slate-400">All participants will be disconnected and the session will close.</p>
<div class="mt-5 flex justify-end gap-2">
<button type="button" @click="endMeetingConfirmOpen = false"
class="rounded-xl px-4 py-2 text-sm font-medium text-slate-300 hover:bg-slate-800">Cancel</button>
<button type="button" @click="submitEndMeeting()"
class="rounded-xl bg-red-600 px-4 py-2 text-sm font-medium text-white hover:bg-red-500">End meeting</button>
</div>
</div>
</div>
</div>
</body>
</html>