@joewinke/jatui 0.2.19 → 0.2.21

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,56 @@
1
+ /**
2
+ * livekit-client optional-peer loader.
3
+ *
4
+ * `livekit-client` is an OPTIONAL peer dependency (peerDependenciesMeta), so a
5
+ * consumer that never touches `@joewinke/jatui/meet` need not install it. To
6
+ * keep that promise we NEVER import runtime values from the package at module
7
+ * scope — a static `import { Room } from 'livekit-client'` would make the whole
8
+ * subpath fail to resolve at import time in a consumer without the peer (the
9
+ * d3 lesson: the missing module resolves to `undefined` / a bundler stub and
10
+ * `Room` is `(void 0)` → "is not a constructor"). Instead every runtime value
11
+ * comes through `loadLiveKit()`, a cached lazy `import()` that throws a clear,
12
+ * actionable error when the peer is absent.
13
+ *
14
+ * Types are safe to import statically with `import type` — they are fully
15
+ * erased at compile time and never emit a runtime `require`/`import`.
16
+ */
17
+ /** Thrown when a `jatui/meet` component needs `livekit-client` but it isn't installed. */
18
+ export class LiveKitNotInstalledError extends Error {
19
+ constructor(cause) {
20
+ super("livekit-client is not installed. `@joewinke/jatui/meet` needs it as a peer " +
21
+ 'dependency — run `npm install livekit-client` in the consuming app. ' +
22
+ '(It is an OPTIONAL peer so apps that never render meeting UI can skip it.)');
23
+ this.name = 'LiveKitNotInstalledError';
24
+ if (cause !== undefined)
25
+ this.cause = cause;
26
+ }
27
+ }
28
+ let cached = null;
29
+ /**
30
+ * Lazily load `livekit-client`. Cached after the first call so repeated mounts
31
+ * share one module instance. Rejects with {@link LiveKitNotInstalledError} when
32
+ * the peer is missing, so callers can render a graceful fallback instead of a
33
+ * hard page crash.
34
+ */
35
+ export function loadLiveKit() {
36
+ if (cached)
37
+ return cached;
38
+ cached = import('livekit-client')
39
+ .then((mod) => mod)
40
+ .catch((err) => {
41
+ // Reset so a later call (e.g. after the dep is added in dev) can retry.
42
+ cached = null;
43
+ throw new LiveKitNotInstalledError(err);
44
+ });
45
+ return cached;
46
+ }
47
+ /** True if `livekit-client` can be loaded in this environment. Never throws. */
48
+ export async function isLiveKitAvailable() {
49
+ try {
50
+ await loadLiveKit();
51
+ return true;
52
+ }
53
+ catch {
54
+ return false;
55
+ }
56
+ }
@@ -0,0 +1,103 @@
1
+ /**
2
+ * View-model types for the `jatui/meet` component suite.
3
+ *
4
+ * These are deliberately independent of `livekit-client`'s own types so that
5
+ * the presentational components (VideoTile, ControlBar, ParticipantList,
6
+ * RecordingBadge) can be driven by plain objects — and therefore rendered in
7
+ * docs demos with a mocked participant, no LiveKit server required. The
8
+ * container components (MeetingRoom, PreJoinLobby) adapt real LiveKit
9
+ * `Participant`/`Track` objects into these shapes.
10
+ */
11
+ /** A participant's role in the room — governs which host controls appear. */
12
+ export type MeetRole = 'host' | 'participant' | 'observer';
13
+ /** Connection quality bucket, mirrors livekit-client's ConnectionQuality. */
14
+ export type MeetConnectionQuality = 'excellent' | 'good' | 'poor' | 'unknown';
15
+ /**
16
+ * A participant as the UI sees it. `videoTrack`/`audioTrack` are opaque Media
17
+ * stream sources: either a real `MediaStreamTrack` (from LiveKit) or, in demos,
18
+ * a track obtained from `getUserMedia`/`null`. VideoTile only needs to attach
19
+ * the track to a `<video>` element, so it accepts `MediaStreamTrack | null`.
20
+ */
21
+ export interface MeetParticipant {
22
+ /** Stable identity — LiveKit `Participant.identity`. */
23
+ id: string;
24
+ /** Display name shown on the tile and in the roster. */
25
+ name: string;
26
+ /** Role — drives host-control affordances. */
27
+ role: MeetRole;
28
+ /** Whether the mic is currently muted. */
29
+ micMuted: boolean;
30
+ /** Whether the camera is off (no video track publishing). */
31
+ cameraOff: boolean;
32
+ /** Whether this participant is the local user. */
33
+ isLocal?: boolean;
34
+ /** Whether this participant is currently sharing their screen. */
35
+ isScreenSharing?: boolean;
36
+ /** Whether audio activity was detected recently (drives the speaking ring). */
37
+ isSpeaking?: boolean;
38
+ /** Connection quality indicator. */
39
+ connectionQuality?: MeetConnectionQuality;
40
+ /** Avatar image URL for the camera-off placeholder. */
41
+ avatarUrl?: string;
42
+ /**
43
+ * The video MediaStreamTrack to render, or null when the camera is off /
44
+ * not yet resolved. VideoTile builds a MediaStream from it and attaches it
45
+ * to a muted, autoplaying <video>.
46
+ */
47
+ videoTrack?: MediaStreamTrack | null;
48
+ /**
49
+ * The screen-share MediaStreamTrack, rendered instead of the camera track
50
+ * when present and `isScreenSharing` is true.
51
+ */
52
+ screenTrack?: MediaStreamTrack | null;
53
+ }
54
+ /** The set of host actions ControlBar can surface against a participant. */
55
+ export interface MeetHostActions {
56
+ /** Mute a remote participant's mic (host only). */
57
+ onMuteParticipant?: (participantId: string) => void;
58
+ /** Remove a participant from the room (host only). */
59
+ onRemoveParticipant?: (participantId: string) => void;
60
+ /** Toggle whether new participants can join (room lock). */
61
+ onToggleLock?: (locked: boolean) => void;
62
+ /** Start or stop server-side recording. */
63
+ onToggleRecording?: (recording: boolean) => void;
64
+ /** End the meeting for everyone. */
65
+ onEndForAll?: () => void;
66
+ }
67
+ /** Local-control callbacks fired by ControlBar for the local participant. */
68
+ export interface MeetLocalControls {
69
+ /** Toggle the local mic. Receives the desired muted state. */
70
+ onToggleMic?: (muted: boolean) => void;
71
+ /** Toggle the local camera. Receives the desired off state. */
72
+ onToggleCamera?: (off: boolean) => void;
73
+ /** Toggle screen share. Receives the desired sharing state. */
74
+ onToggleScreenShare?: (sharing: boolean) => void;
75
+ /** Leave the meeting (local participant only). */
76
+ onLeave?: () => void;
77
+ }
78
+ /** Result of the pre-join device check, handed to the join handler. */
79
+ export interface PreJoinResult {
80
+ /** Confirmed display name. */
81
+ displayName: string;
82
+ /** Whether the user wants to join with the camera enabled. */
83
+ cameraEnabled: boolean;
84
+ /** Whether the user wants to join with the mic enabled. */
85
+ micEnabled: boolean;
86
+ /** Selected camera deviceId, if the user picked one. */
87
+ videoDeviceId?: string;
88
+ /** Selected microphone deviceId, if the user picked one. */
89
+ audioDeviceId?: string;
90
+ /** Whether the consent checkbox was ticked (only relevant when required). */
91
+ consentGiven: boolean;
92
+ }
93
+ /** Room-level status the UI reflects (badge, lock icon, etc.). */
94
+ export interface MeetRoomState {
95
+ /** Whether the room is currently being recorded. */
96
+ recording?: boolean;
97
+ /** Whether the room is locked to new joiners. */
98
+ locked?: boolean;
99
+ /** Connection lifecycle state for the local participant. */
100
+ connectionState?: 'connecting' | 'connected' | 'reconnecting' | 'disconnected';
101
+ }
102
+ /** Layout mode for the meeting grid. */
103
+ export type MeetLayout = 'grid' | 'focus';
@@ -0,0 +1,11 @@
1
+ /**
2
+ * View-model types for the `jatui/meet` component suite.
3
+ *
4
+ * These are deliberately independent of `livekit-client`'s own types so that
5
+ * the presentational components (VideoTile, ControlBar, ParticipantList,
6
+ * RecordingBadge) can be driven by plain objects — and therefore rendered in
7
+ * docs demos with a mocked participant, no LiveKit server required. The
8
+ * container components (MeetingRoom, PreJoinLobby) adapt real LiveKit
9
+ * `Participant`/`Track` objects into these shapes.
10
+ */
11
+ export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@joewinke/jatui",
3
- "version": "0.2.19",
3
+ "version": "0.2.21",
4
4
  "private": false,
5
5
  "description": "Shared Svelte 5 component library for JAT projects",
6
6
  "type": "module",
@@ -54,6 +54,11 @@
54
54
  "types": "./dist/showcase/scroll/index.d.ts",
55
55
  "svelte": "./dist/showcase/scroll/index.js",
56
56
  "default": "./dist/showcase/scroll/index.js"
57
+ },
58
+ "./meet": {
59
+ "types": "./dist/meet/index.d.ts",
60
+ "svelte": "./dist/meet/index.js",
61
+ "default": "./dist/meet/index.js"
57
62
  }
58
63
  },
59
64
  "files": [
@@ -78,17 +83,18 @@
78
83
  "@atlaskit/pragmatic-drag-and-drop-auto-scroll": "~3.0.0",
79
84
  "@atlaskit/pragmatic-drag-and-drop-hitbox": "~2.0.0",
80
85
  "@atlaskit/pragmatic-drag-and-drop-live-region": "~2.0.0",
81
- "d3": "^7.0.0",
82
- "daisyui": "^5.0.0",
83
- "svelte": "^5.0.0",
84
- "tailwindcss": "^4.0.0",
86
+ "@paper-design/shaders": ">=0.0.50",
85
87
  "@tanstack/table-core": "^8.0.0",
86
88
  "@xyflow/svelte": "^1.0.0",
87
89
  "cobe": "^2.0.0",
88
- "@paper-design/shaders": ">=0.0.50",
89
- "motion": "^12.0.0",
90
+ "d3": "^7.0.0",
91
+ "daisyui": "^5.0.0",
92
+ "embla-carousel-svelte": "^8.0.0",
90
93
  "lenis": "^1.0.0",
91
- "embla-carousel-svelte": "^8.0.0"
94
+ "livekit-client": "^2.0.0",
95
+ "motion": "^12.0.0",
96
+ "svelte": "^5.0.0",
97
+ "tailwindcss": "^4.0.0"
92
98
  },
93
99
  "peerDependenciesMeta": {
94
100
  "@atlaskit/pragmatic-drag-and-drop": {
@@ -132,6 +138,9 @@
132
138
  },
133
139
  "embla-carousel-svelte": {
134
140
  "optional": true
141
+ },
142
+ "livekit-client": {
143
+ "optional": true
135
144
  }
136
145
  },
137
146
  "devDependencies": {
@@ -155,6 +164,7 @@
155
164
  "daisyui": "^5.0.0",
156
165
  "embla-carousel-svelte": "^8.6.0",
157
166
  "lenis": "^1.3.25",
167
+ "livekit-client": "^2.20.1",
158
168
  "motion": "^12.42.2",
159
169
  "svelte": "^5.0.0",
160
170
  "tailwindcss": "^4.0.0",
@@ -162,11 +172,11 @@
162
172
  "vite": "^6.0.0"
163
173
  },
164
174
  "dependencies": {
175
+ "@floating-ui/dom": "^1.6.0",
165
176
  "diff": "^9.0.0",
166
177
  "libphonenumber-js": "^1.12.41",
167
178
  "pdfjs-dist": "^5.6.205",
168
- "svelte-dnd-action": "^0.9.69",
169
- "@floating-ui/dom": "^1.6.0"
179
+ "svelte-dnd-action": "^0.9.69"
170
180
  },
171
181
  "license": "MIT"
172
182
  }