Compare commits

..

2 Commits

Author SHA1 Message Date
Mathew Kamkar
2c4a1278cf support emoji reactions 2025-07-30 14:30:04 +01:00
Mathew Kamkar
1b4f0261d9 css to support separate control bar from chat and video 2025-07-29 18:49:18 +01:00
9 changed files with 721 additions and 681 deletions

View File

@ -1,16 +1,33 @@
# .github/workflows/sync-to-production.yaml
name: Sync main to sandbox-production name: Sync main to sandbox-production
on: on:
workflow_dispatch: push:
branches:
- main
permissions:
contents: write
pull-requests: write
jobs: jobs:
sync: sync:
runs-on: ubuntu-latest runs-on: ubuntu-latest
permissions:
contents: write
steps: steps:
- uses: livekit-examples/sandbox-deploy-action@v1 - name: Checkout code
uses: actions/checkout@v4
with: with:
production_branch: 'sandbox-production' fetch-depth: 0 # Fetch all history so we can force push
token: ${{ secrets.GITHUB_TOKEN }}
- name: Set up Git
run: |
git config --global user.name 'github-actions[bot]'
git config --global user.email 'github-actions[bot]@livekit.io'
- name: Sync to sandbox-production
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
git checkout sandbox-production || git checkout -b sandbox-production
git merge --strategy-option theirs main
git push origin sandbox-production

View File

@ -11,12 +11,12 @@ jobs:
test: test:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v6 - uses: actions/checkout@v4
- uses: pnpm/action-setup@v4 - uses: pnpm/action-setup@v4
- name: Use Node.js 22 - name: Use Node.js 20
uses: actions/setup-node@v4 uses: actions/setup-node@v4
with: with:
node-version: 24 node-version: 20
cache: 'pnpm' cache: 'pnpm'
- name: Install dependencies - name: Install dependencies
@ -29,4 +29,4 @@ jobs:
run: pnpm format:check run: pnpm format:check
- name: Run Tests - name: Run Tests
run: pnpm test run: pnpm test

View File

@ -21,7 +21,6 @@ export function VideoConferenceClientImpl(props: {
liveKitUrl: string; liveKitUrl: string;
token: string; token: string;
codec: VideoCodec | undefined; codec: VideoCodec | undefined;
singlePeerConnection: boolean | undefined;
}) { }) {
const keyProvider = new ExternalE2EEKeyProvider(); const keyProvider = new ExternalE2EEKeyProvider();
const { worker, e2eePassphrase } = useSetupE2EE(); const { worker, e2eePassphrase } = useSetupE2EE();
@ -40,11 +39,10 @@ export function VideoConferenceClientImpl(props: {
dynacast: true, dynacast: true,
e2ee: e2eeEnabled e2ee: e2eeEnabled
? { ? {
keyProvider, keyProvider,
worker, worker,
} }
: undefined, : undefined,
singlePeerConnection: props.singlePeerConnection,
}; };
}, [e2eeEnabled, props.codec, keyProvider, worker]); }, [e2eeEnabled, props.codec, keyProvider, worker]);

View File

@ -7,10 +7,9 @@ export default async function CustomRoomConnection(props: {
liveKitUrl?: string; liveKitUrl?: string;
token?: string; token?: string;
codec?: string; codec?: string;
singlePC?: string;
}>; }>;
}) { }) {
const { liveKitUrl, token, codec, singlePC } = await props.searchParams; const { liveKitUrl, token, codec } = await props.searchParams;
if (typeof liveKitUrl !== 'string') { if (typeof liveKitUrl !== 'string') {
return <h2>Missing LiveKit URL</h2>; return <h2>Missing LiveKit URL</h2>;
} }
@ -23,12 +22,7 @@ export default async function CustomRoomConnection(props: {
return ( return (
<main data-lk-theme="default" style={{ height: '100%' }}> <main data-lk-theme="default" style={{ height: '100%' }}>
<VideoConferenceClientImpl <VideoConferenceClientImpl liveKitUrl={liveKitUrl} token={token} codec={codec} />
liveKitUrl={liveKitUrl}
token={token}
codec={codec}
singlePeerConnection={singlePC === 'true'}
/>
</main> </main>
); );
} }

View File

@ -129,7 +129,6 @@ function VideoConferenceComponent(props: {
adaptiveStream: true, adaptiveStream: true,
dynacast: true, dynacast: true,
e2ee: keyProvider && worker && e2eeEnabled ? { keyProvider, worker } : undefined, e2ee: keyProvider && worker && e2eeEnabled ? { keyProvider, worker } : undefined,
singlePeerConnection: true,
}; };
}, [props.userChoices, props.options.hq, props.options.codec]); }, [props.userChoices, props.options.hq, props.options.codec]);

View File

@ -23,7 +23,3 @@ export function randomString(length: number): string {
export function isLowPowerDevice() { export function isLowPowerDevice() {
return navigator.hardwareConcurrency < 6; return navigator.hardwareConcurrency < 6;
} }
export function isMeetStaging() {
return new URL(location.origin).host === 'meet.staging.livekit.io';
}

View File

@ -14,31 +14,31 @@
}, },
"dependencies": { "dependencies": {
"@datadog/browser-logs": "^5.23.3", "@datadog/browser-logs": "^5.23.3",
"@livekit/components-react": "2.9.19", "@livekit/components-react": "2.9.13",
"@livekit/components-styles": "1.2.0", "@livekit/components-styles": "1.1.6",
"@livekit/krisp-noise-filter": "0.4.1", "@livekit/krisp-noise-filter": "0.3.4",
"@livekit/track-processors": "^0.7.0", "@livekit/track-processors": "^0.5.4",
"livekit-client": "2.17.2", "livekit-client": "2.15.2",
"livekit-server-sdk": "2.15.0", "livekit-server-sdk": "2.13.1",
"next": "15.2.8", "next": "15.2.4",
"react": "18.3.1", "react": "18.3.1",
"react-dom": "18.3.1", "react-dom": "18.3.1",
"react-hot-toast": "^2.5.2", "react-hot-toast": "^2.5.2",
"tinykeys": "^3.0.0" "tinykeys": "^3.0.0"
}, },
"devDependencies": { "devDependencies": {
"@types/node": "24.10.13", "@types/node": "22.15.31",
"@types/react": "18.3.27", "@types/react": "18.3.23",
"@types/react-dom": "18.3.7", "@types/react-dom": "18.3.7",
"eslint": "9.39.1", "eslint": "9.29.0",
"eslint-config-next": "15.5.6", "eslint-config-next": "15.3.3",
"prettier": "3.7.3", "prettier": "3.5.3",
"source-map-loader": "^5.0.0", "source-map-loader": "^5.0.0",
"typescript": "5.9.3", "typescript": "5.8.3",
"vitest": "^3.2.4" "vitest": "^3.2.4"
}, },
"engines": { "engines": {
"node": ">=18" "node": ">=18"
}, },
"packageManager": "pnpm@10.18.2" "packageManager": "pnpm@10.9.0"
} }

1219
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@ -25,13 +25,13 @@ body {
padding-inline: 2rem; padding-inline: 2rem;
} }
.header > img { .header>img {
display: block; display: block;
margin: auto; margin: auto;
max-width: 100%; max-width: 100%;
} }
.header > h2 { .header>h2 {
font-family: 'TWK Everett', sans-serif; font-family: 'TWK Everett', sans-serif;
font-style: normal; font-style: normal;
font-weight: 400; font-weight: 400;
@ -65,3 +65,92 @@ h2 a {
h2 a { h2 a {
text-decoration: none; text-decoration: none;
} }
/* Override LiveKit video conference layout to fix control bar positioning */
.lk-video-conference {
position: relative;
display: flex !important;
flex-direction: column !important;
align-items: stretch;
height: 100%;
}
.lk-video-conference-content {
display: flex;
flex-direction: row;
align-items: stretch;
width: 100%;
flex: 1;
min-height: 0;
}
.lk-video-conference-inner {
display: flex;
flex-direction: column;
align-items: stretch;
width: 100%;
flex: 1;
min-height: 0;
}
.lk-grid-layout-wrapper,
.lk-focus-layout-wrapper {
position: relative;
display: flex;
justify-content: center;
width: 100%;
height: 100% !important;
flex: 1;
min-height: 0;
}
.lk-grid-layout-wrapper {
flex-direction: column;
align-items: center;
}
.lk-focus-layout-wrapper {
align-items: stretch;
}
/* Emoji Reaction Styles */
.lk-emoji-reactions {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
display: flex;
flex-direction: column;
align-items: center;
gap: 0.5rem;
pointer-events: none;
z-index: 10;
}
.lk-emoji-reaction {
font-size: 3rem;
animation: emojiReaction 3s ease-out forwards;
text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.5);
}
@keyframes emojiReaction {
0% {
opacity: 0;
transform: scale(0.5) translateY(20px);
}
20% {
opacity: 1;
transform: scale(1.2) translateY(0);
}
80% {
opacity: 1;
transform: scale(1) translateY(-10px);
}
100% {
opacity: 0;
transform: scale(0.8) translateY(-20px);
}
}