Fixed settings and icons

This commit is contained in:
SujithThirumalaisamy 2025-03-18 20:32:21 +05:30
parent e8c7ff5453
commit 3995ab0486
14 changed files with 244 additions and 131 deletions

View File

@ -1 +0,0 @@
{}

View File

@ -1,11 +1,14 @@
'use client';
import React, { useState, useEffect } from 'react';
import { DisconnectButton, useRoomContext } from '@livekit/components-react';
import { DisconnectButton, useLayoutContext, useRoomContext } from '@livekit/components-react';
import { Room, RoomEvent, Track } from 'livekit-client';
import { mergeClasses } from '@/lib/client-utils';
import { ToggleSource } from '@livekit/components-core';
import '../../styles/CustomControlBar.css';
import { CameraOffSVG, CameraOnSVG } from '../svg/camera';
import { MicOffSVG, MicOnSVG } from '../svg/mic';
import { ScreenShareOnSVG } from '../svg/screen-share';
interface CustomControlBarProps {
room: Room;
@ -15,6 +18,7 @@ interface CustomControlBarProps {
export function CustomControlBar({ room, roomName }: CustomControlBarProps) {
const [recording, setRecording] = useState(false);
const [participantCount, setParticipantCount] = useState(1);
const { dispatch } = useLayoutContext().widget;
useEffect(() => {
if (room) {
@ -23,10 +27,6 @@ export function CustomControlBar({ room, roomName }: CustomControlBarProps) {
setParticipantCount(room.numParticipants);
};
if (room.state === 'connected') {
updateParticipantCount();
}
room.on(RoomEvent.Connected, updateParticipantCount);
room.on(RoomEvent.ParticipantConnected, updateParticipantCount);
room.on(RoomEvent.ParticipantDisconnected, updateParticipantCount);
@ -62,12 +62,12 @@ export function CustomControlBar({ room, roomName }: CustomControlBarProps) {
<TrackToggle source={Track.Source.Microphone} />
<TrackToggle source={Track.Source.Camera} />
<div className={`control-button record-sign ${recording ? '' : 'disabled'}`}>
<div className={`control-btn ${recording ? '' : 'disabled'}`}>
<span className="material-symbols-outlined">radio_button_checked</span>
</div>
<TrackToggle source={Track.Source.ScreenShare} />
<DisconnectButton className="control-button end-call-button">
<DisconnectButton className="end-call-button">
<span className="material-symbols-outlined">call_end</span>
</DisconnectButton>
</div>
@ -79,7 +79,12 @@ export function CustomControlBar({ room, roomName }: CustomControlBarProps) {
<span className="participant-count">{participantCount}</span>
</div>
<div className="settings-box">
<div
className="settings-box"
onClick={() => {
if (dispatch) dispatch({ msg: 'toggle_settings' });
}}
>
<span className="material-symbols-outlined">settings</span>
</div>
</div>
@ -127,32 +132,12 @@ interface TrackIconProps {
function TrackIcon({ trackSource, enabled }: TrackIconProps) {
switch (trackSource) {
case Track.Source.Camera:
return enabled ? (
<span className="material-symbols-outlined">videocam</span>
) : (
<span
button-state="inactive"
data-lk-video-enabled="false"
className="material-symbols-outlined"
>
videocam_off
</span>
);
return enabled ? <CameraOnSVG /> : <CameraOffSVG />;
case Track.Source.Microphone:
return enabled ? (
<span className="material-symbols-outlined">mic</span>
) : (
<span
button-state="inactive"
data-lk-audio-enabled="false"
className="material-symbols-outlined"
>
mic_off
</span>
);
return enabled ? <MicOnSVG /> : <MicOffSVG />;
case Track.Source.ScreenShare:
return enabled ? (
<span className="material-symbols-outlined">screen_share</span>
<ScreenShareOnSVG />
) : (
<span
button-state="inactive"

View File

@ -15,7 +15,6 @@ import {
import { DebugMode } from '@/lib/Debug';
import { useMemo, useEffect, useState } from 'react';
import { decodePassphrase } from '@/lib/client-utils';
import { SettingsMenu } from '@/lib/SettingsMenu';
export function VideoConferenceClientImpl(props: {
liveKitUrl: string;
@ -65,11 +64,7 @@ export function VideoConferenceClientImpl(props: {
return;
}
console.log('ROOM!!!');
const updateTranscriptions = (
segments: TranscriptionSegment[],
participant: any,
publication: any,
) => {
const updateTranscriptions = (segments: TranscriptionSegment[]) => {
console.log('received transcriptions', segments);
setTranscriptions((prev) => {
const newTranscriptions = { ...prev };
@ -95,12 +90,7 @@ export function VideoConferenceClientImpl(props: {
audio={true}
video={true}
>
<VideoConference
chatMessageFormatter={formatChatMessageLinks}
SettingsComponent={
process.env.NEXT_PUBLIC_SHOW_SETTINGS_MENU === 'true' ? SettingsMenu : undefined
}
/>
<VideoConference chatMessageFormatter={formatChatMessageLinks} />
<DebugMode logLevel={LogLevel.debug} />
</LiveKitRoom>
);

View File

@ -8,6 +8,7 @@ import {
PreJoin,
LiveKitRoom,
RoomAudioRenderer,
VideoConference,
} from '@livekit/components-react';
import {
ExternalE2EEKeyProvider,
@ -17,13 +18,12 @@ import {
Room,
DeviceUnsupportedError,
RoomConnectOptions,
E2EEOptions,
} from 'livekit-client';
import { useRouter } from 'next/navigation';
import '../../../styles/PageClientImpl.css';
import { CustomVideoLayout } from '@/lib/CustomVideoLayout';
import { RecordingIndicator } from '@/lib/RecordingIndicator';
const CONN_DETAILS_ENDPOINT =
process.env.NEXT_PUBLIC_CONN_DETAILS_ENDPOINT ?? '/api/connection-details';

37
app/svg/camera.tsx Normal file
View File

@ -0,0 +1,37 @@
const CameraOnSVG = () => {
return (
<svg width="18" height="12" viewBox="0 0 18 12" fill="none" xmlns="http://www.w3.org/2000/svg">
<path
d="M2.35107 11.8945C0.878906 11.8945 0 11.0449 0 9.57275V2.31445C0 0.849609 0.944824 0 2.35107 0H10.2905C11.7627 0 12.5684 0.849609 12.5684 2.31445V9.57275C12.5684 11.0449 11.6895 11.8945 10.2173 11.8945H2.35107ZM13.5645 8.10791V3.7793L16.2305 1.47949C16.4795 1.25977 16.7725 1.12061 17.0361 1.12061C17.6074 1.12061 17.981 1.53809 17.981 2.13867V9.75586C17.981 10.3564 17.6074 10.7739 17.0361 10.7739C16.7725 10.7739 16.4795 10.6348 16.2305 10.415L13.5645 8.10791Z"
fill="#ED7474"
/>
<path
d="M2.35107 11.8945C0.878906 11.8945 0 11.0449 0 9.57275V2.31445C0 0.849609 0.944824 0 2.35107 0H10.2905C11.7627 0 12.5684 0.849609 12.5684 2.31445V9.57275C12.5684 11.0449 11.6895 11.8945 10.2173 11.8945H2.35107ZM13.5645 8.10791V3.7793L16.2305 1.47949C16.4795 1.25977 16.7725 1.12061 17.0361 1.12061C17.6074 1.12061 17.981 1.53809 17.981 2.13867V9.75586C17.981 10.3564 17.6074 10.7739 17.0361 10.7739C16.7725 10.7739 16.4795 10.6348 16.2305 10.415L13.5645 8.10791Z"
fill="white"
/>
</svg>
);
};
const CameraOffSVG = () => {
return (
<svg width="22" height="19" viewBox="0 0 22 19" fill="none" xmlns="http://www.w3.org/2000/svg">
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M4.10204 2.98412C3.66551 3.38015 3.41406 3.9721 3.41406 4.72778V11.9861C3.41406 13.4583 4.29297 14.3079 5.76514 14.3079H13.6313C14.201 14.3079 14.6818 14.1806 15.0573 13.9394L4.10204 2.98412ZM15.9717 12.2615C15.9788 12.1723 15.9824 12.0805 15.9824 11.9861V4.72778C15.9824 3.26294 15.1768 2.41333 13.7046 2.41333H6.1235L15.9717 12.2615ZM16.9785 6.19263V10.5212L19.6445 12.8284C19.8936 13.0481 20.1865 13.1873 20.4502 13.1873C21.0215 13.1873 21.395 12.7698 21.395 12.1692V4.552C21.395 3.95142 21.0215 3.53394 20.4502 3.53394C20.1865 3.53394 19.8936 3.6731 19.6445 3.89282L16.9785 6.19263Z"
fill="#ED7473"
/>
<rect
y="1.29611"
width="1.833"
height="23.7102"
rx="0.916499"
transform="rotate(-45 0 1.29611)"
fill="#ED7473"
/>
</svg>
);
};
export { CameraOnSVG, CameraOffSVG };

36
app/svg/mic.tsx Normal file
View File

@ -0,0 +1,36 @@
const MicOnSVG = () => {
return (
<svg width="14" height="19" viewBox="0 0 14 19" fill="none" xmlns="http://www.w3.org/2000/svg">
<path
d="M7.00032 12.0852C6.17792 12.0852 5.47926 11.7977 4.90436 11.2228C4.32946 10.6479 4.04201 9.94925 4.04201 9.12684V3.66534C4.04201 2.84294 4.32946 2.14429 4.90436 1.56939C5.47926 0.994483 6.17792 0.707031 7.00032 0.707031C7.82272 0.707031 8.52137 0.994483 9.09628 1.56939C9.67118 2.14429 9.95863 2.84294 9.95863 3.66534V9.12684C9.95863 9.94925 9.67118 10.6479 9.09628 11.2228C8.52137 11.7977 7.82272 12.0852 7.00032 12.0852ZM6.00138 17.5852V15.7433C4.5448 15.5411 3.29874 14.9251 2.26321 13.8956C1.22784 12.866 0.597707 11.617 0.372818 10.1487C0.335082 9.86713 0.409332 9.62643 0.595568 9.42659C0.781957 9.22676 1.01594 9.12684 1.29751 9.12684C1.57923 9.12684 1.81794 9.22386 2.01365 9.41788C2.20951 9.61176 2.34167 9.84956 2.41011 10.1313C2.64218 11.2154 3.19042 12.1021 4.05484 12.7914C4.9191 13.4806 5.90093 13.8252 7.00032 13.8252C8.11498 13.8252 9.10063 13.4768 9.95726 12.78C10.814 12.083 11.3585 11.2001 11.5905 10.1313C11.659 9.84956 11.7911 9.61176 11.987 9.41788C12.1827 9.22386 12.4214 9.12684 12.7031 9.12684C12.9847 9.12684 13.2178 9.22768 13.4023 9.42934C13.5869 9.63086 13.6602 9.87248 13.6223 10.1542C13.3974 11.592 12.772 12.8324 11.7461 13.8754C10.7201 14.9184 9.47112 15.5411 7.99926 15.7433V17.5852C7.99926 17.8669 7.90316 18.1038 7.71096 18.2958C7.51877 18.488 7.28189 18.5841 7.00032 18.5841C6.71875 18.5841 6.48187 18.488 6.28967 18.2958C6.09748 18.1038 6.00138 17.8669 6.00138 17.5852Z"
fill="white"
/>
</svg>
);
};
const MicOffSVG = () => {
return (
<svg width="45" height="45" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
<mask
id="mask0_5659_3520"
style={{ maskType: 'alpha' }}
maskUnits="userSpaceOnUse"
x="0"
y="0"
width="30"
height="30"
>
<rect width="30" height="30" fill="#D9D9D9" />
</mask>
<g mask="url(#mask0_5659_3520)">
<path
d="M19.8 17.575C19.5667 17.4417 19.4167 17.2375 19.35 16.9625C19.2833 16.6875 19.3167 16.4333 19.45 16.2C19.5667 16.0167 19.6625 15.8208 19.7375 15.6125C19.8125 15.4042 19.8667 15.1917 19.9 14.975C19.9667 14.6917 20.0958 14.4583 20.2875 14.275C20.4792 14.0917 20.7167 14 21 14C21.2833 14 21.5167 14.1 21.7 14.3C21.8833 14.5 21.9583 14.7417 21.925 15.025C21.875 15.4083 21.7875 15.7833 21.6625 16.15C21.5375 16.5167 21.375 16.8667 21.175 17.2C21.0417 17.4333 20.8375 17.5875 20.5625 17.6625C20.2875 17.7375 20.0333 17.7083 19.8 17.575ZM16.3 13.45L12.575 9.725C12.3917 9.54167 12.25 9.32917 12.15 9.0875C12.05 8.84583 12 8.59167 12 8.325V8C12 7.16667 12.2917 6.45833 12.875 5.875C13.4583 5.29167 14.1667 5 15 5C15.8333 5 16.5417 5.29167 17.125 5.875C17.7083 6.45833 18 7.16667 18 8V12.725C18 13.175 17.7958 13.4875 17.3875 13.6625C16.9792 13.8375 16.6167 13.7667 16.3 13.45ZM14 23V20.9C12.4667 20.7 11.1542 20.0583 10.0625 18.975C8.97083 17.8917 8.30833 16.575 8.075 15.025C8.04166 14.7417 8.11666 14.5 8.3 14.3C8.48333 14.1 8.71666 14 9 14C9.28333 14 9.52083 14.0958 9.7125 14.2875C9.90416 14.4792 10.0333 14.7167 10.1 15C10.3333 16.1667 10.9125 17.125 11.8375 17.875C12.7625 18.625 13.8167 19 15 19C15.5667 19 16.1042 18.9125 16.6125 18.7375C17.1208 18.5625 17.5833 18.3167 18 18L19.425 19.425C18.9417 19.8083 18.4125 20.1292 17.8375 20.3875C17.2625 20.6458 16.65 20.8167 16 20.9V23C16 23.2833 15.9042 23.5208 15.7125 23.7125C15.5208 23.9042 15.2833 24 15 24C14.7167 24 14.4792 23.9042 14.2875 23.7125C14.0958 23.5208 14 23.2833 14 23ZM22.1 24.9L5.1 7.9C4.91666 7.71667 4.825 7.48333 4.825 7.2C4.825 6.91667 4.91666 6.68333 5.1 6.5C5.28333 6.31667 5.51666 6.225 5.8 6.225C6.08333 6.225 6.31666 6.31667 6.5 6.5L23.5 23.5C23.6833 23.6833 23.775 23.9167 23.775 24.2C23.775 24.4833 23.6833 24.7167 23.5 24.9C23.3167 25.0833 23.0833 25.175 22.8 25.175C22.5167 25.175 22.2833 25.0833 22.1 24.9Z"
fill="#ED7474"
/>
</g>
</svg>
);
};
export { MicOnSVG, MicOffSVG };

32
app/svg/screen-share.tsx Normal file
View File

@ -0,0 +1,32 @@
const ScreenShareOnSVG = () => {
return (
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<mask
id="mask0_5675_17827"
style={{ maskType: 'alpha' }}
maskUnits="userSpaceOnUse"
x="0"
y="0"
width="24"
height="24"
>
<rect width="24" height="24" fill="#D9D9D9" />
</mask>
<g mask="url(#mask0_5675_17827)">
<path
d="M11.25 11.2192V14.9038C11.25 15.1166 11.3218 15.2948 11.4655 15.4385C11.609 15.582 11.7872 15.6538 12 15.6538C12.2128 15.6538 12.391 15.582 12.5345 15.4385C12.6782 15.2948 12.75 15.1166 12.75 14.9038V11.2345L14.073 12.5423C14.2218 12.6808 14.3959 12.7542 14.5953 12.7625C14.7946 12.7708 14.9718 12.6974 15.127 12.5423C15.282 12.3871 15.3595 12.2089 15.3595 12.0078C15.3595 11.8064 15.282 11.6282 15.127 11.473L12.6328 8.97875C12.4519 8.79808 12.241 8.70775 12 8.70775C11.759 8.70775 11.5481 8.79808 11.3673 8.97875L8.873 11.473C8.72433 11.6218 8.651 11.7959 8.653 11.9953C8.65483 12.1946 8.73333 12.3718 8.8885 12.527C9.04367 12.6718 9.21933 12.7468 9.4155 12.752C9.6115 12.757 9.78708 12.682 9.94225 12.527L11.25 11.2192ZM4.30775 18.5C3.80258 18.5 3.375 18.325 3.025 17.975C2.675 17.625 2.5 17.1974 2.5 16.6923V6.30775C2.5 5.80258 2.675 5.375 3.025 5.025C3.375 4.675 3.80258 4.5 4.30775 4.5H19.6923C20.1974 4.5 20.625 4.675 20.975 5.025C21.325 5.375 21.5 5.80258 21.5 6.30775V16.6923C21.5 17.1974 21.325 17.625 20.975 17.975C20.625 18.325 20.1974 18.5 19.6923 18.5H4.30775Z"
fill="white"
/>
<path
d="M7 21H17"
stroke="white"
stroke-width="1.5"
stroke-linecap="round"
stroke-linejoin="round"
/>
</g>
</svg>
);
};
export { ScreenShareOnSVG };

View File

@ -1,14 +1,9 @@
import React from 'react';
import {
GridLayout,
useTracks,
RoomAudioRenderer,
LayoutContextProvider,
Chat,
} from '@livekit/components-react';
import { GridLayout, useTracks, LayoutContextProvider, Chat } from '@livekit/components-react';
import { Track, Room } from 'livekit-client';
import { ParticipantTile } from './ParticipantTile';
import { CustomControlBar } from '@/app/custom/CustomControlBar';
import { SettingsMenu } from './SettingsMenu';
interface CustomVideoLayoutProps {
room: Room;
@ -17,6 +12,7 @@ interface CustomVideoLayoutProps {
export const CustomVideoLayout: React.FC<CustomVideoLayoutProps> = ({ room, roomName }) => {
const [showChat, setShowChat] = React.useState(false);
const [showSettings, setShowSettings] = React.useState(false);
const tracks = useTracks(
[
@ -36,12 +32,16 @@ export const CustomVideoLayout: React.FC<CustomVideoLayoutProps> = ({ room, room
widget: {
state: {
showChat,
showSettings,
unreadMessages: 0,
},
dispatch: (action: any) => {
if ('msg' in action && action.msg === 'toggle_chat') {
setShowChat((prev) => !prev);
}
if ('msg' in action && action.msg === 'toggle_settings') {
setShowSettings((prev) => !prev);
}
},
},
}}
@ -51,7 +51,7 @@ export const CustomVideoLayout: React.FC<CustomVideoLayoutProps> = ({ room, room
display: 'flex',
flexDirection: 'row',
height: '100vh',
width: '100%',
width: '100vw',
position: 'relative',
backgroundColor: '#070707',
}}
@ -69,6 +69,7 @@ export const CustomVideoLayout: React.FC<CustomVideoLayoutProps> = ({ room, room
tracks={tracks}
style={{
width: '100%',
padding: '1rem 1rem 0.5rem 1rem',
}}
>
<ParticipantTile />
@ -92,7 +93,7 @@ export const CustomVideoLayout: React.FC<CustomVideoLayoutProps> = ({ room, room
</div>
)}
<CustomControlBar room={room} roomName={roomName} />
<RoomAudioRenderer />
<SettingsMenu showSettings={showSettings} />
</div>
</LayoutContextProvider>
);

View File

@ -1,6 +1,14 @@
import React, { useEffect, useState } from 'react';
import { AudioTrack, useTracks, VideoTrack, useTrackRefContext } from '@livekit/components-react';
import {
AudioTrack,
useTracks,
VideoTrack,
useTrackRefContext,
useEnsureTrackRef,
TrackRefContextIfNeeded,
} from '@livekit/components-react';
import { Track, Participant } from 'livekit-client';
import { isTrackReference } from '@livekit/components-core';
function getAvatarColor(identity: string): string {
const colors = [
@ -49,22 +57,13 @@ export const ParticipantTile: React.FC<ParticipantTileProps> = ({
participant: propParticipant,
}) => {
const trackRef = useTrackRefContext();
const trackReference = useEnsureTrackRef(trackRef);
const participant = propParticipant || trackRef?.participant;
if (!participant) return null;
const [profilePictureUrl, setProfilePictureUrl] = useState<string | null>(null);
const isValidTrackRef =
trackRef && 'publication' in trackRef && trackRef.publication !== undefined;
const cameraTrack =
isValidTrackRef && trackRef.source === Track.Source.Camera
? trackRef
: useTracks([Track.Source.Camera], { onlySubscribed: false }).filter(
(track) => track.participant.identity === participant.identity,
)[0];
const microphoneTrack = useTracks([Track.Source.Microphone], { onlySubscribed: false }).filter(
(track) => track.participant.identity === participant.identity,
)[0];
@ -84,8 +83,9 @@ export const ParticipantTile: React.FC<ParticipantTileProps> = ({
}
}, [participant.metadata]);
const hasCamera = !!cameraTrack;
const isCameraEnabled = hasCamera && !cameraTrack.publication?.isMuted;
const isCameraEnabled =
(trackReference.source === Track.Source.Camera && !trackReference.publication?.isMuted) ||
trackReference.source === Track.Source.ScreenShare;
const hasMicrophone = !!microphoneTrack;
const isMicrophoneEnabled = hasMicrophone && !microphoneTrack.publication?.isMuted;
@ -95,9 +95,9 @@ export const ParticipantTile: React.FC<ParticipantTileProps> = ({
return (
<div className={`participant-tile ${isSpeaking ? 'speaking' : ''}`}>
{isCameraEnabled ? (
{isTrackReference(trackReference) && isCameraEnabled ? (
<div className="video-container">
<VideoTrack trackRef={cameraTrack} />
<VideoTrack trackRef={trackReference} />
</div>
) : (
<div className="avatar-container" style={{ backgroundColor: avatarColor }}>

View File

@ -9,13 +9,14 @@ import {
useRoomContext,
useIsRecording,
} from '@livekit/components-react';
import styles from '../styles/SettingsMenu.module.css';
import type { KrispNoiseFilterProcessor } from '@livekit/krisp-noise-filter';
/**
* @alpha
*/
export interface SettingsMenuProps extends React.HTMLAttributes<HTMLDivElement> {}
export interface SettingsMenuProps extends React.HTMLAttributes<HTMLDivElement> {
showSettings: boolean;
}
/**
* @alpha
@ -111,14 +112,16 @@ export function SettingsMenu(props: SettingsMenuProps) {
}
};
if (!props.showSettings) return null;
return (
<div className="settings-menu" style={{ width: '100%' }} {...props}>
<div className={styles.tabs}>
<div className="settings-menu" {...props}>
<div className="tabs">
{tabs.map(
(tab) =>
settings[tab] && (
<button
className={`${styles.tab} lk-button`}
className={`tab lk-button`}
key={tab}
onClick={() => setActiveTab(tab)}
aria-pressed={tab === activeTab}
@ -131,11 +134,11 @@ export function SettingsMenu(props: SettingsMenuProps) {
),
)}
</div>
<div className="tab-content">
<div className="tab-content" style={{ padding: '1rem' }}>
{activeTab === 'media' && (
<>
{settings.media && settings.media.camera && (
<>
<div>
<h3>Camera</h3>
<section className="lk-button-group">
<TrackToggle source={Track.Source.Camera}>Camera</TrackToggle>
@ -143,10 +146,10 @@ export function SettingsMenu(props: SettingsMenuProps) {
<MediaDeviceMenu kind="videoinput" />
</div>
</section>
</>
</div>
)}
{settings.media && settings.media.microphone && (
<>
<div>
<h3>Microphone</h3>
<section className="lk-button-group">
<TrackToggle source={Track.Source.Microphone}>Microphone</TrackToggle>
@ -154,10 +157,10 @@ export function SettingsMenu(props: SettingsMenuProps) {
<MediaDeviceMenu kind="audioinput" />
</div>
</section>
</>
</div>
)}
{settings.media && settings.media.speaker && (
<>
<div>
<h3>Speaker & Headphones</h3>
<section className="lk-button-group">
<span className="lk-button">Audio Output</span>
@ -165,7 +168,7 @@ export function SettingsMenu(props: SettingsMenuProps) {
<MediaDeviceMenu kind="audiooutput"></MediaDeviceMenu>
</div>
</section>
</>
</div>
)}
</>
)}
@ -201,7 +204,7 @@ export function SettingsMenu(props: SettingsMenuProps) {
)}
</div>
<button
className={`lk-button ${styles.settingsCloseButton}`}
className={`lk-button settingsCloseButton`}
onClick={() => layoutContext?.widget.dispatch?.({ msg: 'toggle_settings' })}
>
Close

View File

@ -5,7 +5,7 @@
display: flex;
justify-content: space-between;
align-items: center;
padding: 10px;
padding: 0.8rem 1rem;
position: fixed;
bottom: 0;
left: 0;
@ -18,21 +18,29 @@
}
.room-name-box {
background: rgba(144, 155, 170, 0.1);
background-color: #181d23;
border-radius: 8px;
padding: 5px 10px;
padding: 0.4rem 1rem;
display: flex;
align-items: center;
justify-content: center;
gap: 5px;
}
.room-name-box:hover {
background-color: #1f242b;
}
.room-name {
font-family: 'Roboto', sans-serif;
font-size: 14px;
font-size: 1rem;
color: #909baa;
}
.copy-link-button {
display: flex;
align-items: center;
justify-content: center;
background: none;
border: none;
cursor: pointer;
@ -52,18 +60,6 @@
flex-grow: 1;
}
.control-button {
width: 40px;
height: 40px;
background: rgba(144, 155, 170, 0.1);
border-radius: 8px;
border: none;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
}
/* Status Disabled */
.material-symbols-outlined[button-state='inactive'] {
color: #ed7473;
@ -104,6 +100,10 @@
color: #ffffff;
}
.screen-share-button[data-lk-screen-share-enabled='true']:hover {
background-color: #3d1d20;
}
/* Record */
.record-sign .material-symbols-outlined {
color: #ff6f6f;
@ -151,17 +151,22 @@
}
.participant-box {
background-color: rgba(144, 155, 170, 0.1);
background-color: #181d23;
border-radius: 8px;
display: flex;
align-items: center;
padding: 5px 10px;
gap: 8px;
gap: 4px;
}
.participant-box:hover {
background-color: #1f242b;
}
.participant-box .material-symbols-outlined {
font-size: 20px;
color: white;
padding: 0.3rem;
}
.participant-count {
@ -172,16 +177,19 @@
}
.settings-box {
background-color: rgba(144, 155, 170, 0.1);
background-color: #181d23;
border-radius: 8px;
width: 40px;
height: 40px;
padding: 0.6rem;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
}
.settings-box:hover {
background-color: #1f242b;
}
.settings-box .material-symbols-outlined {
font-size: 20px;
color: white;

View File

@ -23,3 +23,44 @@
display: grid;
place-items: center;
}
.settings-menu {
display: flex;
width: 45vw;
height: 50vh;
flex-direction: column;
align-items: left;
position: absolute;
top: 50%;
left: 50%;
translate: -50% -50%;
background: var(--background-primary);
border-radius: 0.4rem;
padding: 1rem;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.2);
z-index: 100;
}
.settingsCloseButton {
position: absolute;
right: var(--lk-grid-gap);
bottom: var(--lk-grid-gap);
}
.tabs {
position: relative;
display: flex;
align-content: space-between;
}
.tabs > .tab {
padding: 0.5rem;
border-radius: 0;
padding-bottom: 0.5rem;
border-bottom: 3px solid;
border-color: var(--bg5);
}
.tabs > .tab[aria-pressed='true'] {
border-color: var(--lk-accent-bg);
}

View File

@ -1,23 +0,0 @@
.settingsCloseButton {
position: absolute;
right: var(--lk-grid-gap);
bottom: var(--lk-grid-gap);
}
.tabs {
position: relative;
display: flex;
align-content: space-between;
}
.tabs > .tab {
padding: 0.5rem;
border-radius: 0;
padding-bottom: 0.5rem;
border-bottom: 3px solid;
border-color: var(--bg5);
}
.tabs > .tab[aria-pressed='true'] {
border-color: var(--lk-accent-bg);
}

View File

@ -120,6 +120,10 @@ h2 a {
height: 2.4rem;
}
.custom-control-bar .control-btn:hover {
background-color: #1f242b;
}
.custom-control-bar .room-leave-btn {
background-color: var(--danger-dark);
color: var(--danger-light);
@ -130,15 +134,15 @@ h2 a {
background-color: var(--danger-dark);
}
.custom-control-bar [data-lk-active='true']:hover {
background-color: #2d1e22;
}
.control-link {
display: flex;
color: #909baa;
}
.custom-control-bar span {
font-size: 1.3rem;
}
.left,
.center,
.right {