Compare commits

...

131 Commits

Author SHA1 Message Date
lukasIO
12cee3ed06
Update livekit dependencies (#512) 2026-02-19 17:07:12 +01:00
renovate[bot]
2220072d47
chore(deps): update dependency node to v24 (#491)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-02-19 16:56:32 +01:00
renovate[bot]
392ca136de
fix(deps): update dependency @livekit/krisp-noise-filter to v0.4.1 (#505)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-02-19 16:54:36 +01:00
renovate[bot]
3a75f3222f
fix(deps): update livekit dependencies (non-major) (#499)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-02-10 16:24:35 +01:00
vercel[bot]
f80673aba8
Fix React Server Components CVE vulnerabilities (#503)
Updated dependencies to fix Next.js and React CVE vulnerabilities.

The fix-react2shell-next tool automatically updated the following packages to their secure versions:
- next
- react-server-dom-webpack
- react-server-dom-parcel  
- react-server-dom-turbopack

All package.json files have been scanned and vulnerable versions have been patched to the correct fixed versions based on the official React advisory.

Co-authored-by: Vercel <vercel[bot]@users.noreply.github.com>
2025-12-26 11:35:52 +01:00
vercel[bot]
690dc1011a
Update Next.js/React Flight RCE vulnerability patches (#501)
## React Flight / Next.js RCE Advisory - Security Update

### Summary
Updated the project to address the React Flight / Next.js RCE advisory (CVE-2024-50383) by upgrading Next.js to the patched version.

### Vulnerability Assessment
 **Project is affected by the advisory:**
- Uses **Next.js 15.2.x** (vulnerable version range)
- Does NOT use React Flight packages (react-server-dom-webpack, react-server-dom-parcel, react-server-dom-turbopack)
- Uses React 18.3.1 (not vulnerable React 19.x versions)

### Changes Made

#### Modified Files:
1. **package.json**
   - Upgraded `next` from `15.2.4` to `15.2.6` (patched version for 15.2.x)
   - No React or React DOM changes required (Next.js manages its own patched React versions)

2. **pnpm-lock.yaml**
   - Updated lockfile to reflect `next@15.2.6` installation
   - All dependencies resolved correctly with patched versions

### Implementation Details
- This project is a Next.js 15 application without React Server Components/Flight
- The RCE vulnerability in Next.js 15.2.x is addressed by upgrading to 15.2.6
- No React Flight packages required updating since they are not used
- React versions (18.3.1) are not affected by this vulnerability

### Build Status
⚠️ **Note on Pre-existing Issue:**
The build fails due to corrupted image files in `public/background-images/` (pre-existing issue):
- `ali-kazal-tbw_KQE3Cbg-unsplash.jpg` (130 bytes - should be larger)
- `samantha-gades-BlIhVfXbi9s-unsplash.jpg` (132 bytes - should be larger)

This image corruption issue exists in the original codebase and is unrelated to the security update. The Next.js upgrade to 15.2.6 itself is successful and the patched version is correctly installed.

### Testing
- Verified dependency installation with `pnpm install`
- Confirmed lockfile contains `next@15.2.6`
- Confirmed no React Flight packages are used
- Pre-existing image corruption prevents full build, but dependency upgrade is verified

### Security Impact
 **Successfully patched against CVE-2024-50383**
- Next.js upgraded to 15.2.6 (patched version for 15.2.x)
- No vulnerable React Flight packages in use
- React versions remain compatible and secure

Co-authored-by: Vercel <vercel[bot]@users.noreply.github.com>
2025-12-08 12:50:11 +01:00
renovate[bot]
6de1bc8cc6
fix(deps): update dependency livekit-server-sdk to v2.14.2 (#495)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-04 09:33:18 +00:00
renovate[bot]
563925f757
chore(deps): update actions/checkout action to v6 (#497)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-03 22:31:46 -08:00
renovate[bot]
0b62ed930e
chore(deps): update devdependencies (non-major) (#480)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-29 20:58:17 -08:00
lukasIO
baa4e787a2
Default to dual peer connection for custom tab (#496) 2025-11-20 15:55:51 +01:00
renovate[bot]
dc82cc23b9
fix(deps): update dependency livekit-client to v2.16.0 (#494)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-19 09:56:42 +01:00
renovate[bot]
e9b037bac1
fix(deps): update livekit dependencies (non-major) (#492)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-15 08:20:55 +01:00
lukasIO
49b83637dc
Enable singlePC mode for meet also on prod (#493)
* Enable singlePC mode for meet also on prod

* fix
2025-11-10 11:04:29 +01:00
renovate[bot]
aa9be8cdc0
fix(deps): update dependency livekit-client to v2.15.13 (#487)
* fix(deps): update dependency livekit-client to v2.15.12

* bump

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: lukasIO <mail@lukasseiler.de>
2025-10-21 19:25:41 +02:00
lukasIO
03aac6591a
Enable single pc connection on staging (#488)
* Enable single pc connection on staging

* fix deps

* 'security'

* vp9

* use util
2025-10-16 10:32:49 +02:00
lukasIO
83424b27d5
Revert "Use single pc (#483)" (#484)
* Revert "Update livekit client and use single pc (#483)"

This reverts commit 55adec00d31c25ef40e10f67ef7dd4880c9e81a6.

* still update livekit client
2025-10-13 17:53:27 +02:00
lukasIO
55adec00d3
Update livekit client and use single pc (#483) 2025-10-13 16:57:59 +02:00
renovate[bot]
5ff6fa32ac
chore(deps): update pnpm to v10.18.2 (#408)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-13 15:38:57 +02:00
renovate[bot]
8e66391a01
fix(deps): update livekit dependencies (non-major) (#481)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-02 17:20:43 +02:00
renovate[bot]
e9dba9861a
fix(deps): update dependency react-hot-toast to v2.6.0 (#473)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-15 13:21:40 +02:00
renovate[bot]
76234cdf93
fix(deps): update livekit dependencies (non-major) (#475)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-11 14:28:39 +02:00
Tobias Fried
0b4af83a3f
chore(ci): tag deployment versions (#478) 2025-09-09 00:48:31 -06:00
renovate[bot]
6fdf7f0b9a
fix(deps): update livekit dependencies (non-major) (#474)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-29 14:04:10 +02:00
renovate[bot]
372cdfe760
chore(deps): update dependency node to v22 (#470)
* chore(deps): update dependency node to v22

* Update test.yaml

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: lukasIO <mail@lukasseiler.de>
2025-08-15 12:37:50 +02:00
renovate[bot]
fcec3a2459
chore(deps): update devdependencies (non-major) (#451)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-15 12:36:16 +02:00
renovate[bot]
7d1d62b6c3
fix(deps): update dependency livekit-client to v2.15.5 (#472)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-15 12:35:54 +02:00
renovate[bot]
aa310ade64
fix(deps): update livekit dependencies (non-major) (#463)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-11 17:11:12 +02:00
ThomFoolery
762f1c4a6e
feat: add template media (#465) 2025-07-21 17:50:03 -04:00
ThomFoolery
6ce2570868
feat: add template media (#464) 2025-07-21 16:53:56 -04:00
renovate[bot]
785359f977
fix(deps): update dependency @livekit/components-react to v2.9.13 (#459)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-04 17:52:35 +02:00
lukasIO
26d90de86c
Don't enable krisp by default on low power devices (#457) 2025-07-04 10:14:27 +02:00
David Zhao
f13f8df08e
Dynamic handling of low-power devices (#450) 2025-07-03 19:03:38 +02:00
renovate[bot]
4dd11f412b
fix(deps): update dependency livekit-client to v2.15.2 (#456)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-03 13:10:01 +02:00
renovate[bot]
a8a48d5a7f
fix(deps): update dependency livekit-client to v2.15.1 (#455)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-03 08:06:49 +02:00
renovate[bot]
68046da53c
fix(deps): update dependency @livekit/krisp-noise-filter to v0.3.4 (#453)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-02 18:41:54 +02:00
lukasIO
8826f588a0
Add cross origin headers for shared memory usage (#454) 2025-07-02 18:37:36 +02:00
lukasIO
3f4b5a14b9
Revert "fix(deps): update dependency @livekit/krisp-noise-filter to v0.3.2 (#446)" (#452)
This reverts commit f6cdb176e9eb5119220e5e092f891871ed96ba28.
2025-07-02 17:34:13 +02:00
renovate[bot]
f6cdb176e9
fix(deps): update dependency @livekit/krisp-noise-filter to v0.3.2 (#446)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-02 16:54:10 +02:00
renovate[bot]
3260877886
fix(deps): update livekit dependencies (non-major) (#448)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-02 16:12:42 +02:00
renovate[bot]
e1954a739d
fix(deps): update livekit dependencies (non-major) (#447)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-24 11:39:17 +02:00
renovate[bot]
03fda24b39
fix(deps): update dependency livekit-client to v2.13.8 (#444)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-23 17:56:54 +02:00
renovate[bot]
2e35ce825e
fix(deps): update livekit dependencies (non-major) (#443)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-19 16:06:06 +02:00
renovate[bot]
62df7245a3
fix(deps): update dependency livekit-client to v2.13.6 (#442)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-19 12:03:42 +02:00
lukasIO
b650fecdd4
Derive region url from project url (#441)
* Derive region url from project url

* add tests

* test workflow

* fix workflow

* ugh

* fix

* fix staging/prod
2025-06-18 17:03:40 +02:00
renovate[bot]
5230af4fb6
chore(deps): update devdependencies (non-major) (#436)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-16 08:20:23 +02:00
renovate[bot]
8736088a7e
fix(deps): update dependency livekit-client to v2.13.5 (#439)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-15 21:36:47 -07:00
renovate[bot]
5619c99aa9
fix(deps): update dependency livekit-client to v2.13.4 (#437)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-14 22:52:07 -07:00
lukasIO
c99a780f58
optimizations for low-powered devices (#438)
* ensure low power devices don't use high quality for krisp

* Update MicrophoneSettings.tsx

* use lower video publish settings

* fix import

* comments

---------

Co-authored-by: David Zhao <dz@livekit.io>
2025-06-14 17:23:36 -07:00
lukasIO
c4ea8a31ec
Unify e2ee setup on demo and custom page (#434)
* Unify e2ee setup on demo and custom page

* fix
2025-05-27 16:40:42 +02:00
lukasIO
bfde08ea91
fix: recreate options when e2ee enabled changes (#432) 2025-05-27 10:08:33 +02:00
renovate[bot]
96b193098d
fix(deps): update livekit dependencies (non-major) (#431)
* fix(deps): update livekit dependencies (non-major)

* update settings

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: lukasIO <mail@lukasseiler.de>
2025-05-23 16:50:31 +02:00
renovate[bot]
b3b8901cf7
fix(deps): update dependency @livekit/components-react to v2.9.8 (#429)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-05-23 09:17:03 +02:00
renovate[bot]
e418eeaac4
fix(deps): update livekit dependencies (non-major) (#428)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-05-22 11:02:47 +02:00
lukasIO
489ee7896b
fix mobile settings view (#427) 2025-05-22 11:02:24 +02:00
Tobias Fried
8a9a5a0aef
feat: add common keyboard shortcuts (#424)
Co-authored-by: lukasIO <mail@lukasseiler.de>
2025-05-21 09:59:03 -06:00
renovate[bot]
71f62858b9
fix(deps): update dependency livekit-client to v2.13.1 (#425) 2025-05-21 08:19:02 +02:00
renovate[bot]
851079eaf0
chore(deps): update devdependencies (non-major) (#417)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-05-19 08:15:35 +02:00
renovate[bot]
2880d3685d
fix(deps): update dependency livekit-client to v2.13.0 (#421)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-05-16 17:27:07 +02:00
renovate[bot]
fcd941d859
fix(deps): update dependency @livekit/krisp-noise-filter to v0.3.0 (#420)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-05-15 14:53:36 +02:00
renovate[bot]
6b6e7c7ee7
fix(deps): update livekit dependencies (non-major) (#419)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-05-14 18:50:41 +02:00
renovate[bot]
edcb266dc4
fix(deps): update livekit dependencies (non-major) (#416) 2025-04-28 14:41:57 +02:00
lukasIO
680633c33c
chore: update @livekit/track-processors (#414) 2025-04-18 10:17:28 +02:00
lukasIO
efac802d7b
Add background filters to settings menu (#413) 2025-04-15 20:26:22 +02:00
lukasIO
8b2ee6c324
Adopt room context provider pattern (#412) 2025-04-15 20:26:07 +02:00
renovate[bot]
7396247483
fix(deps): update dependency livekit-client to v2.11.2 (#411)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-04-11 16:27:28 +02:00
renovate[bot]
ffef3846b8
fix(deps): update livekit dependencies (non-major) (#410)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-04-10 21:29:06 +02:00
lukasIO
52d6f6a416
Use toast instead of window.alert as a recording indicator (#409)
* Use toast instead of window.alert as a recording indicator

* fix color
2025-04-08 08:37:56 +02:00
renovate[bot]
17ff1d6092
fix(deps): update nextjs monorepo to v15 (major) (#406)
* fix(deps): update nextjs monorepo to v15

* fix code for v15 upgrade

* fix param await

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: lukasIO <mail@lukasseiler.de>
2025-04-08 08:09:47 +02:00
renovate[bot]
fca845d3a4
fix(deps): update dependency @datadog/browser-logs to v5.35.1 (#397)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-04-08 07:57:00 +02:00
renovate[bot]
38b3951e7f
fix(deps): update dependency next to v14.2.26 [security] (#407)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-04-08 07:55:46 +02:00
renovate[bot]
edb22f66d1
chore(deps): update devdependencies (non-major) (#400)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-04-08 07:55:21 +02:00
renovate[bot]
4619ccc038
fix(deps): update dependency next to v14.2.25 [security] (#405)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-04-08 07:54:33 +02:00
renovate[bot]
f5355b911f
fix(deps): update livekit dependencies (non-major) (#401)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-04-08 07:52:26 +02:00
renovate[bot]
4a3c54d84a
fix(deps): update livekit dependencies (non-major) (#399)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-28 14:14:38 +00:00
renovate[bot]
6753a96b91
fix(deps): update dependency livekit-client to v2.9.4 (#398) 2025-02-24 21:49:45 +01:00
renovate[bot]
0a7d440a54
fix(deps): update dependency next to v14.2.24 (#395)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-20 00:31:26 -06:00
renovate[bot]
c32ba3ae87
fix(deps): update livekit dependencies (non-major) (#396)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-19 13:58:13 +01:00
renovate[bot]
19115417db
fix(deps): update dependency livekit-client to v2.9.1 (#394) 2025-02-11 14:27:47 +01:00
renovate[bot]
a47447c592
fix(deps): update livekit dependencies (non-major) (#390)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-10 14:22:45 -06:00
Scott Brenner
b97de89158
Correct LiveKit Cloud hyperlink in README.md (#392)
* Correct hyperlink in README.md

* Update README.md
2025-02-03 22:39:53 -08:00
renovate[bot]
2d99e7703e
fix(deps): update dependency @livekit/krisp-noise-filter to v0.2.16 (#389) 2025-01-27 14:18:43 +01:00
renovate[bot]
63747dad36
fix(deps): update livekit dependencies (non-major) (#388)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-20 16:52:52 +01:00
renovate[bot]
210595ad93
chore(deps): update devdependencies (non-major) (#371)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-16 11:10:51 +01:00
Jonas Schell
10a0ac4a35
feat(api): add cookie handling for random participant postfix in connection details (#385) 2025-01-14 14:36:03 +01:00
renovate[bot]
686b749282
fix(deps): update dependency @datadog/browser-logs to v5.35.0 (#384) 2025-01-13 19:23:48 +01:00
renovate[bot]
f4ce8f53c3
fix(deps): update dependency @datadog/browser-logs to v5.34.1 (#376)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-13 18:47:50 +01:00
renovate[bot]
aef4ba5e91
fix(deps): update dependency @livekit/krisp-noise-filter to v0.2.14 (#381)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-13 18:47:12 +01:00
renovate[bot]
96cac3c5ec
fix(deps): update dependency next to v14.2.23 (#383)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-13 18:46:54 +01:00
renovate[bot]
f07bef61cf
fix(deps): update livekit dependencies (non-major) (#382)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-13 17:45:12 +00:00
renovate[bot]
09a45809a9
fix(deps): update dependency next to v14.2.22 (#372)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-06 14:31:56 +01:00
renovate[bot]
84ee6ac6b0
fix(deps): update dependency livekit-server-sdk to v2.9.5 (#380)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-06 13:42:56 +01:00
renovate[bot]
18ef3c70f1
fix(deps): update livekit dependencies (non-major) (#377)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-03 23:53:01 -06:00
renovate[bot]
53d309b74f
fix(deps): update dependency next to v14.2.21 [security] (#379)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-03 23:51:47 -06:00
renovate[bot]
23d8fbf5b0
fix(deps): update dependency livekit-server-sdk to v2.9.3 (#374)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-05 10:25:07 -06:00
renovate[bot]
fb4a8b0e11
fix(deps): update dependency livekit-client to v2.7.3 (#373)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-03 21:52:01 +01:00
renovate[bot]
f981960a08
fix(deps): update livekit dependencies (non-major) (#370)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-03 18:05:58 +01:00
renovate[bot]
7c4597d865
fix(deps): update dependency @datadog/browser-logs to v5.32.0 (#369)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-27 14:29:31 +01:00
renovate[bot]
b3548ad265
fix(deps): update dependency livekit-client to v2.7.0 (#368)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-26 15:58:24 +01:00
renovate[bot]
bb949d2b73
fix(deps): update dependency @datadog/browser-logs to v5.31.1 (#366)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-25 15:00:20 +01:00
renovate[bot]
5bf838a880
fix(deps): update livekit dependencies (non-major) (#367)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-25 15:00:03 +01:00
renovate[bot]
6bea1c4852
fix(deps): update livekit dependencies (non-major) (#365)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-19 11:56:48 +01:00
renovate[bot]
294a478d7a
fix(deps): update dependency @datadog/browser-logs to v5.30.1 (#364)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-18 16:38:05 +01:00
renovate[bot]
436f4ebefc
fix(deps): update dependency @datadog/browser-logs to v5.30.0 (#363)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-15 00:28:21 -07:00
renovate[bot]
1f08a442bb
fix(deps): update dependency @livekit/krisp-noise-filter to v0.2.13 (#362)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-15 00:27:58 -07:00
renovate[bot]
227378884e
fix(deps): update dependency next to v14.2.18 (#350)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-15 00:27:39 -07:00
renovate[bot]
6e8081abba
fix(deps): update dependency @datadog/browser-logs to v5.30.0 (#352)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-15 00:07:21 -07:00
renovate[bot]
0df1f08871
chore(deps): update devdependencies (non-major) (#360)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-15 00:06:44 -07:00
renovate[bot]
ae5352331f
fix(deps): update livekit dependencies (non-major) (#357)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-15 00:03:04 -07:00
renovate[bot]
86038b2e0d
chore(deps): update dependency @types/node to v22 (#359)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-15 00:02:24 -07:00
rektdeckard
4c209e7cfa chore(meta): add template graphic 2024-10-17 20:49:29 -06:00
renovate[bot]
ffbbea062b
fix(deps): update dependency @livekit/components-styles to v1.1.4 (#355)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-10-15 13:55:39 +02:00
renovate[bot]
da88946c11
fix(deps): update livekit dependencies (non-major) (#354)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-10-14 22:30:17 +02:00
renovate[bot]
0bc4c00627
fix(deps): update dependency livekit-client to v2.5.8 (#353)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-10-10 17:06:33 +02:00
renovate[bot]
8ddaec6cec
fix(deps): update dependency livekit-server-sdk to v2.7.1 (#351)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-10-09 15:58:04 +02:00
renovate[bot]
45dfb8e2e5
fix(deps): update livekit dependencies (non-major) (#349)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-10-07 16:20:05 +02:00
renovate[bot]
b0b0a1db32
chore(deps): update devdependencies (non-major) (#347)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-10-02 15:40:14 +02:00
renovate[bot]
c1cbbd943d
fix(deps): update livekit dependencies (non-major) (#344)
* fix(deps): update livekit dependencies (non-major)

* Update import path for krisp

* irngore misonfigured tinykeys package

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: lukasIO <mail@lukasseiler.de>
2024-10-02 15:37:41 +02:00
renovate[bot]
75b144497c
fix(deps): update dependency @datadog/browser-logs to v5.27.0 (#334)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-10-02 15:29:01 +02:00
renovate[bot]
50fe5d6208
fix(deps): update dependency next to v14.2.14 (#337)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-10-02 15:28:44 +02:00
lukasIO
37e190bc99
use krisp noise filter hook (#346) 2024-09-30 20:40:27 +02:00
renovate[bot]
737b40b059
fix(deps): update dependency livekit-client to v2.5.5 (#343)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-09-27 12:34:26 -07:00
lukasIO
7964ba6778
Ensure encryption key has been set before enabling e2ee (#342) 2024-09-27 10:25:25 +02:00
lukasIO
398b172400
Surface errors via alert (#341) 2024-09-26 18:02:29 +02:00
renovate[bot]
dbab065056
fix(deps): update dependency livekit-client to v2.5.4 (#340)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-09-24 14:56:29 +02:00
renovate[bot]
4ff13a70c4
fix(deps): update dependency @livekit/components-react to v2.6.0 (#339)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-09-23 08:05:52 +02:00
lukasIO
bf86c02e59
Revert "feat(app): add connection details headers (#335)" (#338)
This reverts commit 8e7c8adab85ab8f98b76aeed681cd6dbea185091.
2024-09-20 17:16:42 +02:00
lukasIO
c24b42eaad
Fix height of custom page (#336) 2024-09-19 18:06:00 +02:00
Tobias Fried
8e7c8adab8
feat(app): add connection details headers (#335) 2024-09-18 22:01:26 -06:00
renovate[bot]
e9b103be4b
fix(deps): update dependency next to v14.2.12 (#333)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-09-18 10:59:21 +02:00
renovate[bot]
e0001e4100
fix(deps): update dependency livekit-client to v2.5.2 (#332)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-09-17 21:56:53 +00:00
renovate[bot]
2c752affcb
fix(deps): update dependency @livekit/components-react to v2.5.4 (#330)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-09-17 15:58:10 +02:00
32 changed files with 2767 additions and 1000 deletions

1
.gitattributes vendored Normal file
View File

@ -0,0 +1 @@
public/background-images/*.jpg filter=lfs diff=lfs merge=lfs -text

BIN
.github/assets/template-dark.webp vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 758 B

31
.github/assets/template-graphic.svg vendored Normal file
View File

@ -0,0 +1,31 @@
<svg width="270" height="151" viewBox="0 0 270 151" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="270" height="151" fill="#070707"/>
<rect x="8.5" y="8.5" width="192" height="134" rx="1.5" fill="#131313"/>
<rect x="8.5" y="8.5" width="192" height="134" rx="1.5" stroke="#1F1F1F"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M101.167 71.4998C101.167 69.6589 102.66 68.1665 104.501 68.1665C106.342 68.1665 107.834 69.6589 107.834 71.4998C107.834 73.3408 106.342 74.8332 104.501 74.8332C102.66 74.8332 101.167 73.3408 101.167 71.4998Z" fill="#666666"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M97.834 82.1665C97.834 78.4846 100.819 75.4998 104.501 75.4998C108.183 75.4998 111.167 78.4846 111.167 82.1665V82.8332H97.834V82.1665Z" fill="#666666"/>
<rect x="209.5" y="8.5" width="52" height="38.6667" rx="1.5" fill="#131313"/>
<rect x="209.5" y="8.5" width="52" height="38.6667" rx="1.5" stroke="#1F1F1F"/>
<g clip-path="url(#clip0_834_19648)">
<path fill-rule="evenodd" clip-rule="evenodd" d="M232.167 23.8333C232.167 21.9924 233.66 20.5 235.501 20.5C237.342 20.5 238.834 21.9924 238.834 23.8333C238.834 25.6743 237.342 27.1667 235.501 27.1667C233.66 27.1667 232.167 25.6743 232.167 23.8333Z" fill="#666666"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M228.834 34.5C228.834 30.8181 231.819 27.8333 235.501 27.8333C239.183 27.8333 242.167 30.8181 242.167 34.5V35.1667H228.834V34.5Z" fill="#666666"/>
</g>
<rect x="209.5" y="56.1665" width="52" height="38.6667" rx="1.5" fill="#131313"/>
<rect x="209.5" y="56.1665" width="52" height="38.6667" rx="1.5" stroke="#CCCCCC"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M232.167 71.4998C232.167 69.6589 233.66 68.1665 235.501 68.1665C237.342 68.1665 238.834 69.6589 238.834 71.4998C238.834 73.3408 237.342 74.8332 235.501 74.8332C233.66 74.8332 232.167 73.3408 232.167 71.4998Z" fill="#CCCCCC"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M228.834 82.1665C228.834 78.4846 231.819 75.4998 235.501 75.4998C239.183 75.4998 242.167 78.4846 242.167 82.1665V82.8332H228.834V82.1665Z" fill="#CCCCCC"/>
<rect x="209.5" y="103.833" width="52" height="38.6667" rx="1.5" fill="#131313"/>
<rect x="209.5" y="103.833" width="52" height="38.6667" rx="1.5" stroke="#1F1F1F"/>
<g clip-path="url(#clip1_834_19648)">
<path fill-rule="evenodd" clip-rule="evenodd" d="M232.167 119.167C232.167 117.326 233.66 115.833 235.501 115.833C237.342 115.833 238.834 117.326 238.834 119.167C238.834 121.008 237.342 122.5 235.501 122.5C233.66 122.5 232.167 121.008 232.167 119.167Z" fill="#666666"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M228.834 129.833C228.834 126.152 231.819 123.167 235.501 123.167C239.183 123.167 242.167 126.152 242.167 129.833V130.5H228.834V129.833Z" fill="#666666"/>
</g>
<defs>
<clipPath id="clip0_834_19648">
<rect width="16" height="16" fill="white" transform="translate(227.5 19.8335)"/>
</clipPath>
<clipPath id="clip1_834_19648">
<rect width="16" height="16" fill="white" transform="translate(227.5 115.167)"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 3.0 KiB

BIN
.github/assets/template-light.webp vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 794 B

View File

@ -1,33 +1,16 @@
# .github/workflows/sync-to-production.yaml
name: Sync main to sandbox-production
on:
push:
branches:
- main
permissions:
contents: write
pull-requests: write
workflow_dispatch:
jobs:
sync:
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- name: Checkout code
uses: actions/checkout@v4
- uses: livekit-examples/sandbox-deploy-action@v1
with:
fetch-depth: 0 # Fetch all history so we can force push
- 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
production_branch: 'sandbox-production'
token: ${{ secrets.GITHUB_TOKEN }}

32
.github/workflows/test.yaml vendored Normal file
View File

@ -0,0 +1,32 @@
name: Test
on:
push:
branches: [ main ]
pull_request:
permissions:
contents: read
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: pnpm/action-setup@v4
- name: Use Node.js 22
uses: actions/setup-node@v4
with:
node-version: 24
cache: 'pnpm'
- name: Install dependencies
run: pnpm install
- name: ESLint
run: pnpm lint
- name: Prettier
run: pnpm format:check
- name: Run Tests
run: pnpm test

View File

@ -18,7 +18,7 @@
<br>
LiveKit Meet is an open source video conferencing app built on [LiveKit Components](https://github.com/livekit/components-js), [LiveKit Cloud](https://livekit.io/cloud), and Next.js. It's been completely redesigned from the ground up using our new components library.
LiveKit Meet is an open source video conferencing app built on [LiveKit Components](https://github.com/livekit/components-js), [LiveKit Cloud](https://cloud.livekit.io/), and Next.js. It's been completely redesigned from the ground up using our new components library.
![LiveKit Meet screenshot](./.github/assets/livekit-meet.jpg)

View File

@ -1,4 +1,5 @@
import { randomString } from '@/lib/client-utils';
import { getLiveKitURL } from '@/lib/getLiveKitURL';
import { ConnectionDetails } from '@/lib/types';
import { AccessToken, AccessTokenOptions, VideoGrant } from 'livekit-server-sdk';
import { NextRequest, NextResponse } from 'next/server';
@ -7,6 +8,8 @@ const API_KEY = process.env.LIVEKIT_API_KEY;
const API_SECRET = process.env.LIVEKIT_API_SECRET;
const LIVEKIT_URL = process.env.LIVEKIT_URL;
const COOKIE_KEY = 'random-participant-postfix';
export async function GET(request: NextRequest) {
try {
// Parse query parameters
@ -14,7 +17,11 @@ export async function GET(request: NextRequest) {
const participantName = request.nextUrl.searchParams.get('participantName');
const metadata = request.nextUrl.searchParams.get('metadata') ?? '';
const region = request.nextUrl.searchParams.get('region');
const livekitServerUrl = region ? getLiveKitURL(region) : LIVEKIT_URL;
if (!LIVEKIT_URL) {
throw new Error('LIVEKIT_URL is not defined');
}
const livekitServerUrl = region ? getLiveKitURL(LIVEKIT_URL, region) : LIVEKIT_URL;
let randomParticipantPostfix = request.cookies.get(COOKIE_KEY)?.value;
if (livekitServerUrl === undefined) {
throw new Error('Invalid region');
}
@ -27,9 +34,12 @@ export async function GET(request: NextRequest) {
}
// Generate participant token
if (!randomParticipantPostfix) {
randomParticipantPostfix = randomString(4);
}
const participantToken = await createParticipantToken(
{
identity: `${participantName}__${randomString(4)}`,
identity: `${participantName}__${randomParticipantPostfix}`,
name: participantName,
metadata,
},
@ -43,7 +53,12 @@ export async function GET(request: NextRequest) {
participantToken: participantToken,
participantName: participantName,
};
return NextResponse.json(data);
return new NextResponse(JSON.stringify(data), {
headers: {
'Content-Type': 'application/json',
'Set-Cookie': `${COOKIE_KEY}=${randomParticipantPostfix}; Path=/; HttpOnly; SameSite=Strict; Secure; Expires=${getCookieExpirationTime()}`,
},
});
} catch (error) {
if (error instanceof Error) {
return new NextResponse(error.message, { status: 500 });
@ -65,17 +80,10 @@ function createParticipantToken(userInfo: AccessTokenOptions, roomName: string)
return at.toJwt();
}
/**
* Get the LiveKit server URL for the given region.
*/
function getLiveKitURL(region: string | null): string {
let targetKey = 'LIVEKIT_URL';
if (region) {
targetKey = `LIVEKIT_URL_${region}`.toUpperCase();
}
const url = process.env[targetKey];
if (!url) {
throw new Error(`${targetKey} is not defined`);
}
return url;
function getCookieExpirationTime(): string {
var now = new Date();
var time = now.getTime();
var expireTime = time + 60 * 120 * 1000;
now.setTime(expireTime);
return now.toUTCString();
}

View File

@ -1,6 +1,6 @@
'use client';
import { formatChatMessageLinks, LiveKitRoom, VideoConference } from '@livekit/components-react';
import { formatChatMessageLinks, RoomContext, VideoConference } from '@livekit/components-react';
import {
ExternalE2EEKeyProvider,
LogLevel,
@ -11,23 +11,24 @@ import {
type VideoCodec,
} from 'livekit-client';
import { DebugMode } from '@/lib/Debug';
import { useMemo } from 'react';
import { decodePassphrase } from '@/lib/client-utils';
import { useEffect, useMemo, useState } from 'react';
import { KeyboardShortcuts } from '@/lib/KeyboardShortcuts';
import { SettingsMenu } from '@/lib/SettingsMenu';
import { useSetupE2EE } from '@/lib/useSetupE2EE';
import { useLowCPUOptimizer } from '@/lib/usePerfomanceOptimiser';
export function VideoConferenceClientImpl(props: {
liveKitUrl: string;
token: string;
codec: VideoCodec | undefined;
singlePeerConnection: boolean | undefined;
}) {
const worker =
typeof window !== 'undefined' &&
new Worker(new URL('livekit-client/e2ee-worker', import.meta.url));
const keyProvider = new ExternalE2EEKeyProvider();
const e2eePassphrase =
typeof window !== 'undefined' ? decodePassphrase(window.location.hash.substring(1)) : undefined;
const { worker, e2eePassphrase } = useSetupE2EE();
const e2eeEnabled = !!(e2eePassphrase && worker);
const [e2eeSetupComplete, setE2eeSetupComplete] = useState(false);
const roomOptions = useMemo((): RoomOptions => {
return {
publishDefaults: {
@ -43,36 +44,55 @@ export function VideoConferenceClientImpl(props: {
worker,
}
: undefined,
singlePeerConnection: props.singlePeerConnection,
};
}, []);
}, [e2eeEnabled, props.codec, keyProvider, worker]);
const room = useMemo(() => new Room(roomOptions), [roomOptions]);
const room = useMemo(() => new Room(roomOptions), []);
if (e2eeEnabled) {
keyProvider.setKey(e2eePassphrase);
room.setE2EEEnabled(true);
}
const connectOptions = useMemo((): RoomConnectOptions => {
return {
autoSubscribe: true,
};
}, []);
useEffect(() => {
if (e2eeEnabled) {
keyProvider.setKey(e2eePassphrase).then(() => {
room.setE2EEEnabled(true).then(() => {
setE2eeSetupComplete(true);
});
});
} else {
setE2eeSetupComplete(true);
}
}, [e2eeEnabled, e2eePassphrase, keyProvider, room, setE2eeSetupComplete]);
useEffect(() => {
if (e2eeSetupComplete) {
room.connect(props.liveKitUrl, props.token, connectOptions).catch((error) => {
console.error(error);
});
room.localParticipant.enableCameraAndMicrophone().catch((error) => {
console.error(error);
});
}
}, [room, props.liveKitUrl, props.token, connectOptions, e2eeSetupComplete]);
useLowCPUOptimizer(room);
return (
<LiveKitRoom
room={room}
token={props.token}
connectOptions={connectOptions}
serverUrl={props.liveKitUrl}
audio={true}
video={true}
>
<VideoConference
chatMessageFormatter={formatChatMessageLinks}
SettingsComponent={
process.env.NEXT_PUBLIC_SHOW_SETTINGS_MENU === 'true' ? SettingsMenu : undefined
}
/>
<DebugMode logLevel={LogLevel.debug} />
</LiveKitRoom>
<div className="lk-room-container">
<RoomContext.Provider value={room}>
<KeyboardShortcuts />
<VideoConference
chatMessageFormatter={formatChatMessageLinks}
SettingsComponent={
process.env.NEXT_PUBLIC_SHOW_SETTINGS_MENU === 'true' ? SettingsMenu : undefined
}
/>
<DebugMode logLevel={LogLevel.debug} />
</RoomContext.Provider>
</div>
);
}

View File

@ -2,14 +2,15 @@ import { videoCodecs } from 'livekit-client';
import { VideoConferenceClientImpl } from './VideoConferenceClientImpl';
import { isVideoCodec } from '@/lib/types';
export default function CustomRoomConnection(props: {
searchParams: {
export default async function CustomRoomConnection(props: {
searchParams: Promise<{
liveKitUrl?: string;
token?: string;
codec?: string;
};
singlePC?: string;
}>;
}) {
const { liveKitUrl, token, codec } = props.searchParams;
const { liveKitUrl, token, codec, singlePC } = await props.searchParams;
if (typeof liveKitUrl !== 'string') {
return <h2>Missing LiveKit URL</h2>;
}
@ -21,8 +22,13 @@ export default function CustomRoomConnection(props: {
}
return (
<main data-lk-theme="default">
<VideoConferenceClientImpl liveKitUrl={liveKitUrl} token={token} codec={codec} />
<main data-lk-theme="default" style={{ height: '100%' }}>
<VideoConferenceClientImpl
liveKitUrl={liveKitUrl}
token={token}
codec={codec}
singlePeerConnection={singlePC === 'true'}
/>
</main>
);
}

View File

@ -2,6 +2,7 @@ import '../styles/globals.css';
import '@livekit/components-styles';
import '@livekit/components-styles/prefabs';
import type { Metadata, Viewport } from 'next';
import { Toaster } from 'react-hot-toast';
export const metadata: Metadata = {
title: {
@ -50,7 +51,10 @@ export const viewport: Viewport = {
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en">
<body>{children}</body>
<body data-lk-theme="default">
<Toaster />
{children}
</body>
</html>
);
}

View File

@ -1,15 +1,17 @@
'use client';
import React from 'react';
import { decodePassphrase } from '@/lib/client-utils';
import { DebugMode } from '@/lib/Debug';
import { KeyboardShortcuts } from '@/lib/KeyboardShortcuts';
import { RecordingIndicator } from '@/lib/RecordingIndicator';
import { SettingsMenu } from '@/lib/SettingsMenu';
import { ConnectionDetails } from '@/lib/types';
import {
formatChatMessageLinks,
LiveKitRoom,
LocalUserChoices,
PreJoin,
RoomContext,
VideoConference,
} from '@livekit/components-react';
import {
@ -20,9 +22,13 @@ import {
Room,
DeviceUnsupportedError,
RoomConnectOptions,
RoomEvent,
TrackPublishDefaults,
VideoCaptureOptions,
} from 'livekit-client';
import { useRouter } from 'next/navigation';
import React from 'react';
import { useSetupE2EE } from '@/lib/useSetupE2EE';
import { useLowCPUOptimizer } from '@/lib/usePerfomanceOptimiser';
const CONN_DETAILS_ENDPOINT =
process.env.NEXT_PUBLIC_CONN_DETAILS_ENDPOINT ?? '/api/connection-details';
@ -91,90 +97,137 @@ function VideoConferenceComponent(props: {
codec: VideoCodec;
};
}) {
const e2eePassphrase =
typeof window !== 'undefined' && decodePassphrase(location.hash.substring(1));
const worker =
typeof window !== 'undefined' &&
e2eePassphrase &&
new Worker(new URL('livekit-client/e2ee-worker', import.meta.url));
const e2eeEnabled = !!(e2eePassphrase && worker);
const keyProvider = new ExternalE2EEKeyProvider();
const { worker, e2eePassphrase } = useSetupE2EE();
const e2eeEnabled = !!(e2eePassphrase && worker);
const [e2eeSetupComplete, setE2eeSetupComplete] = React.useState(false);
const roomOptions = React.useMemo((): RoomOptions => {
let videoCodec: VideoCodec | undefined = props.options.codec ? props.options.codec : 'vp9';
if (e2eeEnabled && (videoCodec === 'av1' || videoCodec === 'vp9')) {
videoCodec = undefined;
}
const videoCaptureDefaults: VideoCaptureOptions = {
deviceId: props.userChoices.videoDeviceId ?? undefined,
resolution: props.options.hq ? VideoPresets.h2160 : VideoPresets.h720,
};
const publishDefaults: TrackPublishDefaults = {
dtx: false,
videoSimulcastLayers: props.options.hq
? [VideoPresets.h1080, VideoPresets.h720]
: [VideoPresets.h540, VideoPresets.h216],
red: !e2eeEnabled,
videoCodec,
};
return {
videoCaptureDefaults: {
deviceId: props.userChoices.videoDeviceId ?? undefined,
resolution: props.options.hq ? VideoPresets.h2160 : VideoPresets.h720,
},
publishDefaults: {
dtx: false,
videoSimulcastLayers: props.options.hq
? [VideoPresets.h1080, VideoPresets.h720]
: [VideoPresets.h540, VideoPresets.h216],
red: !e2eeEnabled,
videoCodec,
},
videoCaptureDefaults: videoCaptureDefaults,
publishDefaults: publishDefaults,
audioCaptureDefaults: {
deviceId: props.userChoices.audioDeviceId ?? undefined,
},
adaptiveStream: { pixelDensity: 'screen' },
adaptiveStream: true,
dynacast: true,
e2ee: e2eeEnabled
? {
keyProvider,
worker,
}
: undefined,
e2ee: keyProvider && worker && e2eeEnabled ? { keyProvider, worker } : undefined,
singlePeerConnection: true,
};
// @ts-ignore
setLogLevel('debug', 'lk-e2ee');
}, [props.userChoices, props.options.hq, props.options.codec]);
const room = React.useMemo(() => new Room(roomOptions), []);
if (e2eeEnabled) {
keyProvider.setKey(decodePassphrase(e2eePassphrase));
room.setE2EEEnabled(true).catch((e) => {
if (e instanceof DeviceUnsupportedError) {
alert(
`You're trying to join an encrypted meeting, but your browser does not support it. Please update it to the latest version and try again.`,
);
console.error(e);
}
});
}
React.useEffect(() => {
if (e2eeEnabled) {
keyProvider
.setKey(decodePassphrase(e2eePassphrase))
.then(() => {
room.setE2EEEnabled(true).catch((e) => {
if (e instanceof DeviceUnsupportedError) {
alert(
`You're trying to join an encrypted meeting, but your browser does not support it. Please update it to the latest version and try again.`,
);
console.error(e);
} else {
throw e;
}
});
})
.then(() => setE2eeSetupComplete(true));
} else {
setE2eeSetupComplete(true);
}
}, [e2eeEnabled, room, e2eePassphrase]);
const connectOptions = React.useMemo((): RoomConnectOptions => {
return {
autoSubscribe: true,
};
}, []);
React.useEffect(() => {
room.on(RoomEvent.Disconnected, handleOnLeave);
room.on(RoomEvent.EncryptionError, handleEncryptionError);
room.on(RoomEvent.MediaDevicesError, handleError);
if (e2eeSetupComplete) {
room
.connect(
props.connectionDetails.serverUrl,
props.connectionDetails.participantToken,
connectOptions,
)
.catch((error) => {
handleError(error);
});
if (props.userChoices.videoEnabled) {
room.localParticipant.setCameraEnabled(true).catch((error) => {
handleError(error);
});
}
if (props.userChoices.audioEnabled) {
room.localParticipant.setMicrophoneEnabled(true).catch((error) => {
handleError(error);
});
}
}
return () => {
room.off(RoomEvent.Disconnected, handleOnLeave);
room.off(RoomEvent.EncryptionError, handleEncryptionError);
room.off(RoomEvent.MediaDevicesError, handleError);
};
}, [e2eeSetupComplete, room, props.connectionDetails, props.userChoices]);
const lowPowerMode = useLowCPUOptimizer(room);
const router = useRouter();
const handleOnLeave = React.useCallback(() => router.push('/'), [router]);
const handleError = React.useCallback((error: Error) => {
console.error(error);
alert(`Encountered an unexpected error, check the console logs for details: ${error.message}`);
}, []);
const handleEncryptionError = React.useCallback((error: Error) => {
console.error(error);
alert(
`Encountered an unexpected encryption error, check the console logs for details: ${error.message}`,
);
}, []);
React.useEffect(() => {
if (lowPowerMode) {
console.warn('Low power mode enabled');
}
}, [lowPowerMode]);
return (
<>
<LiveKitRoom
room={room}
token={props.connectionDetails.participantToken}
serverUrl={props.connectionDetails.serverUrl}
connectOptions={connectOptions}
video={props.userChoices.videoEnabled}
audio={props.userChoices.audioEnabled}
onDisconnected={handleOnLeave}
>
<div className="lk-room-container">
<RoomContext.Provider value={room}>
<KeyboardShortcuts />
<VideoConference
chatMessageFormatter={formatChatMessageLinks}
SettingsComponent={SHOW_SETTINGS_MENU ? SettingsMenu : undefined}
/>
<DebugMode />
<RecordingIndicator />
</LiveKitRoom>
</>
</RoomContext.Provider>
</div>
);
}

View File

@ -2,25 +2,32 @@ import * as React from 'react';
import { PageClientImpl } from './PageClientImpl';
import { isVideoCodec } from '@/lib/types';
export default function Page({
export default async function Page({
params,
searchParams,
}: {
params: { roomName: string };
searchParams: {
params: Promise<{ roomName: string }>;
searchParams: Promise<{
// FIXME: We should not allow values for regions if in playground mode.
region?: string;
hq?: string;
codec?: string;
};
}>;
}) {
const _params = await params;
const _searchParams = await searchParams;
const codec =
typeof searchParams.codec === 'string' && isVideoCodec(searchParams.codec)
? searchParams.codec
typeof _searchParams.codec === 'string' && isVideoCodec(_searchParams.codec)
? _searchParams.codec
: 'vp9';
const hq = searchParams.hq === 'true' ? true : false;
const hq = _searchParams.hq === 'true' ? true : false;
return (
<PageClientImpl roomName={params.roomName} region={searchParams.region} hq={hq} codec={codec} />
<PageClientImpl
roomName={_params.roomName}
region={_searchParams.region}
hq={hq}
codec={codec}
/>
);
}

175
lib/CameraSettings.tsx Normal file
View File

@ -0,0 +1,175 @@
import React from 'react';
import {
MediaDeviceMenu,
TrackReference,
TrackToggle,
useLocalParticipant,
VideoTrack,
} from '@livekit/components-react';
import { BackgroundBlur, VirtualBackground } from '@livekit/track-processors';
import { isLocalTrack, LocalTrackPublication, Track } from 'livekit-client';
import Desk from '../public/background-images/samantha-gades-BlIhVfXbi9s-unsplash.jpg';
import Nature from '../public/background-images/ali-kazal-tbw_KQE3Cbg-unsplash.jpg';
// Background image paths
const BACKGROUND_IMAGES = [
{ name: 'Desk', path: Desk },
{ name: 'Nature', path: Nature },
];
// Background options
type BackgroundType = 'none' | 'blur' | 'image';
export function CameraSettings() {
const { cameraTrack, localParticipant } = useLocalParticipant();
const [backgroundType, setBackgroundType] = React.useState<BackgroundType>(
(cameraTrack as LocalTrackPublication)?.track?.getProcessor()?.name === 'background-blur'
? 'blur'
: (cameraTrack as LocalTrackPublication)?.track?.getProcessor()?.name === 'virtual-background'
? 'image'
: 'none',
);
const [virtualBackgroundImagePath, setVirtualBackgroundImagePath] = React.useState<string | null>(
null,
);
const camTrackRef: TrackReference | undefined = React.useMemo(() => {
return cameraTrack
? { participant: localParticipant, publication: cameraTrack, source: Track.Source.Camera }
: undefined;
}, [localParticipant, cameraTrack]);
const selectBackground = (type: BackgroundType, imagePath?: string) => {
setBackgroundType(type);
if (type === 'image' && imagePath) {
setVirtualBackgroundImagePath(imagePath);
} else if (type !== 'image') {
setVirtualBackgroundImagePath(null);
}
};
React.useEffect(() => {
if (isLocalTrack(cameraTrack?.track)) {
if (backgroundType === 'blur') {
cameraTrack.track?.setProcessor(BackgroundBlur());
} else if (backgroundType === 'image' && virtualBackgroundImagePath) {
cameraTrack.track?.setProcessor(VirtualBackground(virtualBackgroundImagePath));
} else {
cameraTrack.track?.stopProcessor();
}
}
}, [cameraTrack, backgroundType, virtualBackgroundImagePath]);
return (
<div style={{ display: 'flex', flexDirection: 'column', gap: '10px' }}>
{camTrackRef && (
<VideoTrack
style={{
maxHeight: '280px',
objectFit: 'contain',
objectPosition: 'right',
transform: 'scaleX(-1)',
}}
trackRef={camTrackRef}
/>
)}
<section className="lk-button-group">
<TrackToggle source={Track.Source.Camera}>Camera</TrackToggle>
<div className="lk-button-group-menu">
<MediaDeviceMenu kind="videoinput" />
</div>
</section>
<div style={{ marginTop: '10px' }}>
<div style={{ marginBottom: '8px' }}>Background Effects</div>
<div style={{ display: 'flex', gap: '10px', flexWrap: 'wrap' }}>
<button
onClick={() => selectBackground('none')}
className="lk-button"
aria-pressed={backgroundType === 'none'}
style={{
border: backgroundType === 'none' ? '2px solid #0090ff' : '1px solid #d1d1d1',
minWidth: '80px',
}}
>
None
</button>
<button
onClick={() => selectBackground('blur')}
className="lk-button"
aria-pressed={backgroundType === 'blur'}
style={{
border: backgroundType === 'blur' ? '2px solid #0090ff' : '1px solid #d1d1d1',
minWidth: '80px',
backgroundColor: '#f0f0f0',
position: 'relative',
overflow: 'hidden',
height: '60px',
}}
>
<div
style={{
position: 'absolute',
top: 0,
left: 0,
right: 0,
bottom: 0,
backgroundColor: '#e0e0e0',
filter: 'blur(8px)',
zIndex: 0,
}}
/>
<span
style={{
position: 'relative',
zIndex: 1,
backgroundColor: 'rgba(0,0,0,0.6)',
padding: '2px 5px',
borderRadius: '4px',
fontSize: '12px',
}}
>
Blur
</span>
</button>
{BACKGROUND_IMAGES.map((image) => (
<button
key={image.path.src}
onClick={() => selectBackground('image', image.path.src)}
className="lk-button"
aria-pressed={
backgroundType === 'image' && virtualBackgroundImagePath === image.path.src
}
style={{
backgroundImage: `url(${image.path.src})`,
backgroundSize: 'cover',
backgroundPosition: 'center',
width: '80px',
height: '60px',
border:
backgroundType === 'image' && virtualBackgroundImagePath === image.path.src
? '2px solid #0090ff'
: '1px solid #d1d1d1',
}}
>
<span
style={{
backgroundColor: 'rgba(0,0,0,0.6)',
padding: '2px 5px',
borderRadius: '4px',
fontSize: '12px',
}}
>
{image.name}
</span>
</button>
))}
</div>
</div>
</div>
);
}

View File

@ -1,6 +1,7 @@
import * as React from 'react';
import { useRoomContext } from '@livekit/components-react';
import { setLogLevel, LogLevel, RemoteTrackPublication, setLogExtension } from 'livekit-client';
// @ts-ignore
import { tinykeys } from 'tinykeys';
import { datadogLogs } from '@datadog/browser-logs';

31
lib/KeyboardShortcuts.tsx Normal file
View File

@ -0,0 +1,31 @@
'use client';
import React from 'react';
import { Track } from 'livekit-client';
import { useTrackToggle } from '@livekit/components-react';
export function KeyboardShortcuts() {
const { toggle: toggleMic } = useTrackToggle({ source: Track.Source.Microphone });
const { toggle: toggleCamera } = useTrackToggle({ source: Track.Source.Camera });
React.useEffect(() => {
function handleShortcut(event: KeyboardEvent) {
// Toggle microphone: Cmd/Ctrl-Shift-A
if (toggleMic && event.key === 'A' && (event.ctrlKey || event.metaKey)) {
event.preventDefault();
toggleMic();
}
// Toggle camera: Cmd/Ctrl-Shift-V
if (event.key === 'V' && (event.ctrlKey || event.metaKey)) {
event.preventDefault();
toggleCamera();
}
}
window.addEventListener('keydown', handleShortcut);
return () => window.removeEventListener('keydown', handleShortcut);
}, [toggleMic, toggleCamera]);
return null;
}

View File

@ -0,0 +1,55 @@
import React from 'react';
import { useKrispNoiseFilter } from '@livekit/components-react/krisp';
import { TrackToggle } from '@livekit/components-react';
import { MediaDeviceMenu } from '@livekit/components-react';
import { Track } from 'livekit-client';
import { isLowPowerDevice } from './client-utils';
export function MicrophoneSettings() {
const { isNoiseFilterEnabled, setNoiseFilterEnabled, isNoiseFilterPending } = useKrispNoiseFilter(
{
filterOptions: {
bufferOverflowMs: 100,
bufferDropMs: 200,
quality: isLowPowerDevice() ? 'low' : 'medium',
onBufferDrop: () => {
console.warn(
'krisp buffer dropped, noise filter versions >= 0.3.2 will automatically disable the filter',
);
},
},
},
);
React.useEffect(() => {
// enable Krisp by default on non-low power devices
setNoiseFilterEnabled(!isLowPowerDevice());
}, []);
return (
<div
style={{
display: 'flex',
flexDirection: 'row',
gap: '10px',
alignItems: 'center',
justifyContent: 'space-between',
}}
>
<section className="lk-button-group">
<TrackToggle source={Track.Source.Microphone}>Microphone</TrackToggle>
<div className="lk-button-group-menu">
<MediaDeviceMenu kind="audioinput" />
</div>
</section>
<button
className="lk-button"
onClick={() => setNoiseFilterEnabled(!isNoiseFilterEnabled)}
disabled={isNoiseFilterPending}
aria-pressed={isNoiseFilterEnabled}
>
{isNoiseFilterEnabled ? 'Disable' : 'Enable'} Enhanced Noise Cancellation
</button>
</div>
);
}

View File

@ -1,5 +1,6 @@
import { useIsRecording } from '@livekit/components-react';
import * as React from 'react';
import toast from 'react-hot-toast';
export function RecordingIndicator() {
const isRecording = useIsRecording();
@ -9,7 +10,16 @@ export function RecordingIndicator() {
if (isRecording !== wasRecording) {
setWasRecording(isRecording);
if (isRecording) {
window.alert('This meeting is being recorded');
toast('This meeting is being recorded', {
duration: 3000,
icon: '🎥',
position: 'top-center',
className: 'lk-button',
style: {
backgroundColor: 'var(--lk-danger3)',
color: 'var(--lk-fg)',
},
});
}
}
}, [isRecording]);
@ -22,7 +32,7 @@ export function RecordingIndicator() {
left: '0',
width: '100%',
height: '100%',
boxShadow: isRecording ? 'red 0px 0px 0px 3px inset' : 'none',
boxShadow: isRecording ? 'var(--lk-danger3) 0px 0px 0px 3px inset' : 'none',
pointerEvents: 'none',
}}
></div>

View File

@ -1,17 +1,16 @@
'use client';
import * as React from 'react';
import { LocalAudioTrack, Track } from 'livekit-client';
import { Track } from 'livekit-client';
import {
useMaybeLayoutContext,
useLocalParticipant,
MediaDeviceMenu,
TrackToggle,
useRoomContext,
useIsRecording,
} from '@livekit/components-react';
import styles from '../styles/SettingsMenu.module.css';
import type { KrispNoiseFilterProcessor } from '@livekit/krisp-noise-filter';
import { CameraSettings } from './CameraSettings';
import { MicrophoneSettings } from './MicrophoneSettings';
/**
* @alpha
*/
@ -28,7 +27,6 @@ export function SettingsMenu(props: SettingsMenuProps) {
const settings = React.useMemo(() => {
return {
media: { camera: true, microphone: true, label: 'Media Devices', speaker: true },
effects: { label: 'Effects' },
recording: recordingEndpoint ? { label: 'Recording' } : undefined,
};
}, []);
@ -37,40 +35,7 @@ export function SettingsMenu(props: SettingsMenuProps) {
() => Object.keys(settings).filter((t) => t !== undefined) as Array<keyof typeof settings>,
[settings],
);
const { microphoneTrack } = useLocalParticipant();
const [activeTab, setActiveTab] = React.useState(tabs[0]);
const [isNoiseFilterEnabled, setIsNoiseFilterEnabled] = React.useState(true);
const [isNoiseFilterPending, setIsNoiseFilterPending] = React.useState(false);
React.useEffect(() => {
const micPublication = microphoneTrack;
if (micPublication && micPublication.track instanceof LocalAudioTrack) {
const currentProcessor = micPublication.track.getProcessor();
if (currentProcessor && currentProcessor.name === 'livekit-noise-filter') {
setIsNoiseFilterPending(true);
(currentProcessor as KrispNoiseFilterProcessor)
.setEnabled(isNoiseFilterEnabled)
.finally(() => setIsNoiseFilterPending(false));
} else if (!currentProcessor && isNoiseFilterEnabled) {
setIsNoiseFilterPending(true);
import('@livekit/krisp-noise-filter')
.then(({ KrispNoiseFilter, isKrispNoiseFilterSupported }) => {
if (!isKrispNoiseFilterSupported()) {
console.error('Enhanced noise filter is not supported for this browser');
setIsNoiseFilterEnabled(false);
return;
}
micPublication?.track
// @ts-ignore
?.setProcessor(KrispNoiseFilter())
.then(() => console.log('successfully set noise filter'));
})
.catch((e) => console.error('Failed to load noise filter', e))
.finally(() => setIsNoiseFilterPending(false));
}
}
}, [isNoiseFilterEnabled, microphoneTrack]);
const isRecording = useIsRecording();
const [initialRecStatus, setInitialRecStatus] = React.useState(isRecording);
@ -109,7 +74,7 @@ export function SettingsMenu(props: SettingsMenuProps) {
};
return (
<div className="settings-menu" style={{ width: '100%' }} {...props}>
<div className="settings-menu" style={{ width: '100%', position: 'relative' }} {...props}>
<div className={styles.tabs}>
{tabs.map(
(tab) =>
@ -134,22 +99,16 @@ export function SettingsMenu(props: SettingsMenuProps) {
{settings.media && settings.media.camera && (
<>
<h3>Camera</h3>
<section className="lk-button-group">
<TrackToggle source={Track.Source.Camera}>Camera</TrackToggle>
<div className="lk-button-group-menu">
<MediaDeviceMenu kind="videoinput" />
</div>
<section>
<CameraSettings />
</section>
</>
)}
{settings.media && settings.media.microphone && (
<>
<h3>Microphone</h3>
<section className="lk-button-group">
<TrackToggle source={Track.Source.Microphone}>Microphone</TrackToggle>
<div className="lk-button-group-menu">
<MediaDeviceMenu kind="audioinput" />
</div>
<section>
<MicrophoneSettings />
</section>
</>
)}
@ -166,21 +125,6 @@ export function SettingsMenu(props: SettingsMenuProps) {
)}
</>
)}
{activeTab === 'effects' && (
<>
<h3>Audio</h3>
<section>
<label htmlFor="noise-filter"> Enhanced Noise Cancellation</label>
<input
type="checkbox"
id="noise-filter"
onChange={(ev) => setIsNoiseFilterEnabled(ev.target.checked)}
checked={isNoiseFilterEnabled}
disabled={isNoiseFilterPending}
></input>
</section>
</>
)}
{activeTab === 'recording' && (
<>
<h3>Record Meeting</h3>
@ -197,12 +141,14 @@ export function SettingsMenu(props: SettingsMenuProps) {
</>
)}
</div>
<button
className={`lk-button ${styles.settingsCloseButton}`}
onClick={() => layoutContext?.widget.dispatch?.({ msg: 'toggle_settings' })}
>
Close
</button>
<div style={{ display: 'flex', justifyContent: 'flex-end', width: '100%' }}>
<button
className={`lk-button`}
onClick={() => layoutContext?.widget.dispatch?.({ msg: 'toggle_settings' })}
>
Close
</button>
</div>
</div>
);
}

View File

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

35
lib/getLiveKitURL.test.ts Normal file
View File

@ -0,0 +1,35 @@
import { describe, it, expect } from 'vitest';
import { getLiveKitURL } from './getLiveKitURL';
describe('getLiveKitURL', () => {
it('returns the original URL if no region is provided', () => {
const url = 'https://myproject.livekit.cloud';
expect(getLiveKitURL(url, null)).toBe(url + '/');
});
it('inserts the region into livekit.cloud URLs', () => {
const url = 'https://myproject.livekit.cloud';
const region = 'eu';
expect(getLiveKitURL(url, region)).toBe('https://myproject.eu.production.livekit.cloud/');
});
it('inserts the region into livekit.cloud URLs and preserves the staging environment', () => {
const url = 'https://myproject.staging.livekit.cloud';
const region = 'eu';
expect(getLiveKitURL(url, region)).toBe('https://myproject.eu.staging.livekit.cloud/');
});
it('returns the original URL for non-livekit.cloud hosts, even with region', () => {
const url = 'https://example.com';
const region = 'us';
expect(getLiveKitURL(url, region)).toBe(url + '/');
});
it('handles URLs with paths and query params', () => {
const url = 'https://myproject.livekit.cloud/room?foo=bar';
const region = 'ap';
expect(getLiveKitURL(url, region)).toBe(
'https://myproject.ap.production.livekit.cloud/room?foo=bar',
);
});
});

12
lib/getLiveKitURL.ts Normal file
View File

@ -0,0 +1,12 @@
export function getLiveKitURL(projectUrl: string, region: string | null): string {
const url = new URL(projectUrl);
if (region && url.hostname.includes('livekit.cloud')) {
let [projectId, ...hostParts] = url.hostname.split('.');
if (hostParts[0] !== 'staging') {
hostParts = ['production', ...hostParts];
}
const regionURL = [projectId, region, ...hostParts].join('.');
url.hostname = regionURL;
}
return url.toString();
}

View File

@ -0,0 +1,71 @@
import {
Room,
ParticipantEvent,
RoomEvent,
RemoteTrack,
RemoteTrackPublication,
VideoQuality,
LocalVideoTrack,
isVideoTrack,
} from 'livekit-client';
import * as React from 'react';
export type LowCPUOptimizerOptions = {
reducePublisherVideoQuality: boolean;
reduceSubscriberVideoQuality: boolean;
disableVideoProcessing: boolean;
};
const defaultOptions: LowCPUOptimizerOptions = {
reducePublisherVideoQuality: true,
reduceSubscriberVideoQuality: true,
disableVideoProcessing: false,
} as const;
/**
* This hook ensures that on devices with low CPU, the performance is optimised when needed.
* This is done by primarily reducing the video quality to low when the CPU is constrained.
*/
export function useLowCPUOptimizer(room: Room, options: Partial<LowCPUOptimizerOptions> = {}) {
const [lowPowerMode, setLowPowerMode] = React.useState(false);
const opts = React.useMemo(() => ({ ...defaultOptions, ...options }), [options]);
React.useEffect(() => {
const handleCpuConstrained = async (track: LocalVideoTrack) => {
setLowPowerMode(true);
console.warn('Local track CPU constrained', track);
if (opts.reducePublisherVideoQuality) {
track.prioritizePerformance();
}
if (opts.disableVideoProcessing && isVideoTrack(track)) {
track.stopProcessor();
}
if (opts.reduceSubscriberVideoQuality) {
room.remoteParticipants.forEach((participant) => {
participant.videoTrackPublications.forEach((publication) => {
publication.setVideoQuality(VideoQuality.LOW);
});
});
}
};
room.localParticipant.on(ParticipantEvent.LocalTrackCpuConstrained, handleCpuConstrained);
return () => {
room.localParticipant.off(ParticipantEvent.LocalTrackCpuConstrained, handleCpuConstrained);
};
}, [room, opts.reducePublisherVideoQuality, opts.reduceSubscriberVideoQuality]);
React.useEffect(() => {
const lowerQuality = (_: RemoteTrack, publication: RemoteTrackPublication) => {
publication.setVideoQuality(VideoQuality.LOW);
};
if (lowPowerMode && opts.reduceSubscriberVideoQuality) {
room.on(RoomEvent.TrackSubscribed, lowerQuality);
}
return () => {
room.off(RoomEvent.TrackSubscribed, lowerQuality);
};
}, [lowPowerMode, room, opts.reduceSubscriberVideoQuality]);
return lowPowerMode;
}

15
lib/useSetupE2EE.ts Normal file
View File

@ -0,0 +1,15 @@
import React from 'react';
import { ExternalE2EEKeyProvider } from 'livekit-client';
import { decodePassphrase } from './client-utils';
export function useSetupE2EE() {
const e2eePassphrase =
typeof window !== 'undefined' ? decodePassphrase(location.hash.substring(1)) : undefined;
const worker: Worker | undefined =
typeof window !== 'undefined' && e2eePassphrase
? new Worker(new URL('livekit-client/e2ee-worker', import.meta.url))
: undefined;
return { worker, e2eePassphrase };
}

2
next-env.d.ts vendored
View File

@ -2,4 +2,4 @@
/// <reference types="next/image-types/global" />
// NOTE: This file should not be edited
// see https://nextjs.org/docs/app/building-your-application/configuring/typescript for more information.
// see https://nextjs.org/docs/app/api-reference/config/typescript for more information.

View File

@ -2,6 +2,9 @@
const nextConfig = {
reactStrictMode: false,
productionBrowserSourceMaps: true,
images: {
formats: ['image/webp'],
},
webpack: (config, { buildId, dev, isServer, defaultLoaders, nextRuntime, webpack }) => {
// Important: return the modified config
config.module.rules.push({
@ -9,8 +12,26 @@ const nextConfig = {
enforce: 'pre',
use: ['source-map-loader'],
});
return config;
},
headers: async () => {
return [
{
source: '/(.*)',
headers: [
{
key: 'Cross-Origin-Opener-Policy',
value: 'same-origin',
},
{
key: 'Cross-Origin-Embedder-Policy',
value: 'credentialless',
},
],
},
];
},
};
module.exports = nextConfig;

View File

@ -6,33 +6,39 @@
"dev": "next dev",
"build": "next build",
"start": "next start",
"lint": "next lint"
"lint": "next lint",
"lint:fix": "next lint --fix",
"test": "vitest run",
"format:check": "prettier --check \"**/*.{ts,tsx,md,json}\"",
"format:write": "prettier --write \"**/*.{ts,tsx,md,json}\""
},
"dependencies": {
"@datadog/browser-logs": "^5.23.3",
"@livekit/components-react": "2.5.3",
"@livekit/components-styles": "1.1.2",
"@livekit/krisp-noise-filter": "^0.2.8",
"livekit-client": "2.5.1",
"livekit-server-sdk": "2.6.2",
"next": "14.2.11",
"@livekit/components-react": "2.9.19",
"@livekit/components-styles": "1.2.0",
"@livekit/krisp-noise-filter": "0.4.1",
"@livekit/track-processors": "^0.7.0",
"livekit-client": "2.17.2",
"livekit-server-sdk": "2.15.0",
"next": "15.2.8",
"react": "18.3.1",
"react-dom": "18.3.1",
"tinykeys": "^2.1.0"
"react-hot-toast": "^2.5.2",
"tinykeys": "^3.0.0"
},
"devDependencies": {
"@types/node": "20.16.3",
"@types/react": "18.3.5",
"@types/react-dom": "18.3.0",
"eslint": "9.9.1",
"eslint-config-next": "14.2.7",
"@types/node": "24.10.13",
"@types/react": "18.3.27",
"@types/react-dom": "18.3.7",
"eslint": "9.39.1",
"eslint-config-next": "15.5.6",
"prettier": "3.7.3",
"source-map-loader": "^5.0.0",
"typescript": "5.5.4"
"typescript": "5.9.3",
"vitest": "^3.2.4"
},
"engines": {
"node": ">=18"
},
"pnpm": {
"overrides": {}
}
"packageManager": "pnpm@10.18.2"
}

2758
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:4a3c9eb8da1ef3ddf2439428b49c11abd9a765e056600bd4f1d89a5dfc82778a
size 52339

View File

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:8bc017736e04acb0188f69cec0aafb88bd6891bfc4a6ff1530665e8dc210dbdf
size 1273171

View File

@ -1,9 +1,3 @@
.settingsCloseButton {
position: absolute;
right: var(--lk-grid-gap);
bottom: var(--lk-grid-gap);
}
.tabs {
position: relative;
display: flex;

View File

@ -3,13 +3,13 @@
"target": "ES2020",
"lib": ["dom", "dom.iterable", "ES2020"],
"allowJs": true,
"skipLibCheck": false,
"skipLibCheck": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"noEmit": true,
"esModuleInterop": true,
"module": "ES2020",
"moduleResolution": "node",
"moduleResolution": "Bundler",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "preserve",