Debug overlay (#97)

* add connection stats

* add permissions

* styling

* css module

* styles

* scrolling

* fix height

* account for body height
This commit is contained in:
lukasIO 2023-08-04 12:09:39 +02:00 committed by GitHub
parent 2c984306cb
commit 2c202c25a2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 218 additions and 5 deletions

View file

@ -1,10 +1,13 @@
import * as React from 'react';
import { useRoomContext } from '@livekit/components-react';
import { setLogLevel, LogLevel } from 'livekit-client';
import { setLogLevel, LogLevel, RemoteTrackPublication } from 'livekit-client';
import { tinykeys } from 'tinykeys';
import styles from '../styles/Debug.module.css';
export const useDebugMode = ({ logLevel }: { logLevel?: LogLevel }) => {
setLogLevel(logLevel ?? 'debug');
const room = useRoomContext();
React.useEffect(() => {
// @ts-expect-error
window.__lk_room = room;
@ -13,10 +16,198 @@ export const useDebugMode = ({ logLevel }: { logLevel?: LogLevel }) => {
// @ts-expect-error
window.__lk_room = undefined;
};
});
}, []);
};
export const DebugMode = ({ logLevel }: { logLevel?: LogLevel }) => {
const room = useRoomContext();
const [isOpen, setIsOpen] = React.useState(false);
const [, setRender] = React.useState({});
useDebugMode({ logLevel });
return <></>;
React.useEffect(() => {
if (window) {
const unsubscribe = tinykeys(window, {
'Shift+D': () => {
console.log('setting open');
setIsOpen((open) => !open);
},
});
// timer to re-render
const interval = setInterval(() => {
setRender({});
}, 1000);
return () => {
unsubscribe();
clearInterval(interval);
};
}
}, [isOpen]);
if (typeof window === 'undefined' || !isOpen) {
return null;
}
const handleSimulate = (event: React.ChangeEvent<HTMLSelectElement>) => {
const { value } = event.target;
if (value == '') {
return;
}
event.target.value = '';
let isReconnect = false;
switch (value) {
case 'signal-reconnect':
isReconnect = true;
// fall through
default:
// @ts-expect-error
room.simulateScenario(value);
}
};
const lp = room.localParticipant;
if (!isOpen) {
return <></>;
} else {
return (
<div className={styles.overlay}>
<section id="room-info">
<h3>
Room Info {room.name}: {room.sid}
</h3>
</section>
<details open>
<summary>
<b>Local Participant: {lp.identity}</b>
</summary>
<details open className={styles.detailsSection}>
<summary>
<b>Published tracks</b>
</summary>
<div>
{Array.from(lp.tracks.values()).map((t) => (
<>
<div>
<i>
{t.source.toString()}
&nbsp;<span>{t.trackSid}</span>
</i>
</div>
<table>
<tbody>
<tr>
<td>Kind</td>
<td>
{t.kind}&nbsp;
{t.kind === 'video' && (
<span>
{t.track?.dimensions?.width}x{t.track?.dimensions?.height}
</span>
)}
</td>
</tr>
<tr>
<td>Bitrate</td>
<td>{Math.ceil(t.track!.currentBitrate / 1000)} kbps</td>
</tr>
</tbody>
</table>
</>
))}
</div>
</details>
<details open className={styles.detailsSection}>
<summary>
<b>Permissions</b>
</summary>
<div>
<table>
<tbody>
{lp.permissions &&
Object.entries(lp.permissions).map(([key, val]) => (
<>
<tr>
<td>{key}</td>
{key !== 'canPublishSources' ? (
<td>{val.toString()}</td>
) : (
<td> {val.join(', ')} </td>
)}
</tr>
</>
))}
</tbody>
</table>
</div>
</details>
</details>
<details>
<summary>
<b>Remote Participants</b>
</summary>
{Array.from(room.participants.values()).map((p) => (
<details key={p.sid} className={styles.detailsSection}>
<summary>
<b>
{p.identity}
<span></span>
</b>
</summary>
<div>
{Array.from(p.tracks.values()).map((t) => (
<>
<div>
<i>
{t.source.toString()}
&nbsp;<span>{t.trackSid}</span>
</i>
</div>
<table>
<tbody>
<tr>
<td>Kind</td>
<td>
{t.kind}&nbsp;
{t.kind === 'video' && (
<span>
{t.dimensions?.width}x{t.dimensions?.height}
</span>
)}
</td>
</tr>
<tr>
<td>Status</td>
<td>{trackStatus(t)}</td>
</tr>
{t.track && (
<tr>
<td>Bitrate</td>
<td>{Math.ceil(t.track.currentBitrate / 1000)} kbps</td>
</tr>
)}
</tbody>
</table>
</>
))}
</div>
</details>
))}
</details>
</div>
);
}
};
function trackStatus(t: RemoteTrackPublication): string {
if (t.isSubscribed) {
return t.isEnabled ? 'enabled' : 'disabled';
} else {
return 'unsubscribed';
}
}

View file

@ -16,7 +16,8 @@
"next": "12.3.4",
"next-seo": "^6.0.0",
"react": "18.2.0",
"react-dom": "18.2.0"
"react-dom": "18.2.0",
"tinykeys": "^2.1.0"
},
"devDependencies": {
"@types/node": "18.17.1",
@ -27,4 +28,4 @@
"source-map-loader": "^4.0.1",
"typescript": "5.1.6"
}
}
}

16
styles/Debug.module.css Normal file
View file

@ -0,0 +1,16 @@
.overlay {
position: absolute;
top: 0;
background: rgba(0, 0, 0, 0.6);
padding: 1rem;
max-height: min(100%, 100vh);
overflow-y: auto;
}
.detailsSection {
padding-left: 1rem;
}
.detailsSection > div {
padding-left: 1rem;
}

View file

@ -2249,6 +2249,11 @@ text-table@^0.2.0:
resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4"
integrity sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==
tinykeys@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/tinykeys/-/tinykeys-2.1.0.tgz#1341563e92a7fac9ca90053fddaf2b7553500298"
integrity sha512-/MESnqBD1xItZJn5oGQ4OsNORQgJfPP96XSGoyu4eLpwpL0ifO0SYR5OD76u0YMhMXsqkb0UqvI9+yXTh4xv8Q==
to-regex-range@^5.0.1:
version "5.0.1"
resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4"