@joewinke/jatui 0.2.26 → 0.2.28
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.
- package/dist/meet/ControlBar.svelte +125 -2
- package/dist/meet/ControlBar.svelte.d.ts +15 -1
- package/dist/meet/index.d.ts +1 -1
- package/dist/meet/types.d.ts +25 -0
- package/package.json +1 -1
|
@@ -11,7 +11,13 @@
|
|
|
11
11
|
* red "Leave" that stands apart. Presentational: it reflects the local
|
|
12
12
|
* participant + room state you pass and calls back on intent.
|
|
13
13
|
*/
|
|
14
|
-
import type {
|
|
14
|
+
import type {
|
|
15
|
+
MeetParticipant,
|
|
16
|
+
MeetRoomState,
|
|
17
|
+
MeetHostActions,
|
|
18
|
+
MeetLocalControls,
|
|
19
|
+
MeetDevicePickers
|
|
20
|
+
} from './types.js';
|
|
15
21
|
|
|
16
22
|
interface Props {
|
|
17
23
|
/** The local participant (drives mic/cam/share pressed state). */
|
|
@@ -26,6 +32,20 @@
|
|
|
26
32
|
controls?: MeetLocalControls;
|
|
27
33
|
/** Host-action callbacks. */
|
|
28
34
|
hostActions?: MeetHostActions;
|
|
35
|
+
/**
|
|
36
|
+
* Live input level of the local microphone, 0–1. When provided (and the
|
|
37
|
+
* mic is unmuted) the mic button renders a small 3-bar meter under the
|
|
38
|
+
* icon so the user can SEE that their voice is being picked up — the
|
|
39
|
+
* camera gives that feedback for free, the mic never did. Omit to hide.
|
|
40
|
+
*/
|
|
41
|
+
micLevel?: number;
|
|
42
|
+
/**
|
|
43
|
+
* In-meeting device switching. When provided, a settings gear renders a
|
|
44
|
+
* dropdown listing microphones and cameras with the active one checked —
|
|
45
|
+
* pick to switch live (mid-meeting mic/cam tweaking). Available to every
|
|
46
|
+
* participant, unlike the host menu.
|
|
47
|
+
*/
|
|
48
|
+
devices?: MeetDevicePickers;
|
|
29
49
|
class?: string;
|
|
30
50
|
}
|
|
31
51
|
|
|
@@ -36,9 +56,20 @@
|
|
|
36
56
|
allowScreenShare = true,
|
|
37
57
|
controls = {},
|
|
38
58
|
hostActions = {},
|
|
59
|
+
micLevel,
|
|
60
|
+
devices,
|
|
39
61
|
class: className = ''
|
|
40
62
|
}: Props = $props();
|
|
41
63
|
|
|
64
|
+
// Perceptual boost: raw RMS of speech sits low (~0.05–0.3); sqrt spreads
|
|
65
|
+
// the useful range across the meter. Each bar lights past its threshold.
|
|
66
|
+
const meterLevel = $derived(Math.sqrt(Math.min(1, Math.max(0, micLevel ?? 0))));
|
|
67
|
+
const barActive = $derived([
|
|
68
|
+
meterLevel > 0.15,
|
|
69
|
+
meterLevel > 0.4,
|
|
70
|
+
meterLevel > 0.65
|
|
71
|
+
]);
|
|
72
|
+
|
|
42
73
|
const recording = $derived(room.recording ?? false);
|
|
43
74
|
const locked = $derived(room.locked ?? false);
|
|
44
75
|
</script>
|
|
@@ -52,7 +83,7 @@
|
|
|
52
83
|
<!-- Mic -->
|
|
53
84
|
<button
|
|
54
85
|
type="button"
|
|
55
|
-
class="btn btn-circle"
|
|
86
|
+
class="btn btn-circle relative"
|
|
56
87
|
class:btn-error={local.micMuted}
|
|
57
88
|
class:btn-ghost={!local.micMuted}
|
|
58
89
|
aria-pressed={local.micMuted}
|
|
@@ -67,6 +98,17 @@
|
|
|
67
98
|
<svg class="h-5 w-5" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true">
|
|
68
99
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 11a7 7 0 01-14 0m7 7v3m0-3a4 4 0 004-4V5a4 4 0 00-8 0v6a4 4 0 004 4z" />
|
|
69
100
|
</svg>
|
|
101
|
+
{#if micLevel !== undefined}
|
|
102
|
+
<span class="jatui-mic-meter" aria-hidden="true">
|
|
103
|
+
{#each barActive as active, i (i)}
|
|
104
|
+
<span
|
|
105
|
+
class="jatui-mic-meter-bar"
|
|
106
|
+
class:is-active={active}
|
|
107
|
+
style="height: {0.2 + i * 0.125}rem"
|
|
108
|
+
></span>
|
|
109
|
+
{/each}
|
|
110
|
+
</span>
|
|
111
|
+
{/if}
|
|
70
112
|
{/if}
|
|
71
113
|
</button>
|
|
72
114
|
|
|
@@ -108,6 +150,63 @@
|
|
|
108
150
|
</button>
|
|
109
151
|
{/if}
|
|
110
152
|
|
|
153
|
+
{#if devices}
|
|
154
|
+
<!-- Device settings: mic/camera source pickers, live-switchable -->
|
|
155
|
+
<div class="dropdown dropdown-top dropdown-end">
|
|
156
|
+
<div tabindex="0" role="button" class="btn btn-circle btn-ghost" aria-label="Device settings">
|
|
157
|
+
<svg class="h-5 w-5" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true">
|
|
158
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 001.065 2.572c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 00-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 00-2.572 1.065c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 00-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 00-1.065-2.572c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 001.066-2.573c-.94-1.543.826-3.31 2.37-2.37.996.608 2.296.07 2.572-1.065z" />
|
|
159
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" />
|
|
160
|
+
</svg>
|
|
161
|
+
</div>
|
|
162
|
+
<ul
|
|
163
|
+
class="dropdown-content menu z-[1] mb-2 max-h-80 w-72 flex-nowrap gap-0.5 overflow-y-auto bg-base-100 p-2 shadow-lg"
|
|
164
|
+
style="border-radius: var(--jatui-radius-box);"
|
|
165
|
+
>
|
|
166
|
+
<li class="menu-title text-xs">Microphone</li>
|
|
167
|
+
{#each devices.audioInputs as d (d.deviceId)}
|
|
168
|
+
<li>
|
|
169
|
+
<button
|
|
170
|
+
type="button"
|
|
171
|
+
class="justify-between gap-2"
|
|
172
|
+
aria-pressed={d.deviceId === devices.activeAudioId}
|
|
173
|
+
onclick={() => devices?.onSelectAudio?.(d.deviceId)}
|
|
174
|
+
>
|
|
175
|
+
<span class="truncate">{d.label}</span>
|
|
176
|
+
{#if d.deviceId === devices.activeAudioId}
|
|
177
|
+
<svg class="h-4 w-4 shrink-0 text-success" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true">
|
|
178
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2.5" d="M5 13l4 4L19 7" />
|
|
179
|
+
</svg>
|
|
180
|
+
{/if}
|
|
181
|
+
</button>
|
|
182
|
+
</li>
|
|
183
|
+
{:else}
|
|
184
|
+
<li class="px-3 py-1 text-xs opacity-60">No microphones found</li>
|
|
185
|
+
{/each}
|
|
186
|
+
<li class="menu-title mt-1 text-xs">Camera</li>
|
|
187
|
+
{#each devices.videoInputs as d (d.deviceId)}
|
|
188
|
+
<li>
|
|
189
|
+
<button
|
|
190
|
+
type="button"
|
|
191
|
+
class="justify-between gap-2"
|
|
192
|
+
aria-pressed={d.deviceId === devices.activeVideoId}
|
|
193
|
+
onclick={() => devices?.onSelectVideo?.(d.deviceId)}
|
|
194
|
+
>
|
|
195
|
+
<span class="truncate">{d.label}</span>
|
|
196
|
+
{#if d.deviceId === devices.activeVideoId}
|
|
197
|
+
<svg class="h-4 w-4 shrink-0 text-success" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true">
|
|
198
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2.5" d="M5 13l4 4L19 7" />
|
|
199
|
+
</svg>
|
|
200
|
+
{/if}
|
|
201
|
+
</button>
|
|
202
|
+
</li>
|
|
203
|
+
{:else}
|
|
204
|
+
<li class="px-3 py-1 text-xs opacity-60">No cameras found</li>
|
|
205
|
+
{/each}
|
|
206
|
+
</ul>
|
|
207
|
+
</div>
|
|
208
|
+
{/if}
|
|
209
|
+
|
|
111
210
|
{#if isHost}
|
|
112
211
|
<div class="mx-1 h-8 w-px bg-base-content/15"></div>
|
|
113
212
|
|
|
@@ -174,3 +273,27 @@
|
|
|
174
273
|
<span class="hidden sm:inline">Leave</span>
|
|
175
274
|
</button>
|
|
176
275
|
</div>
|
|
276
|
+
|
|
277
|
+
<style>
|
|
278
|
+
/* Mic level meter — 3 tiny bars docked bottom-center of the mic button.
|
|
279
|
+
Color/motion stay subtle: base-content at rest, success when lit. */
|
|
280
|
+
.jatui-mic-meter {
|
|
281
|
+
position: absolute;
|
|
282
|
+
bottom: 0.3rem;
|
|
283
|
+
left: 50%;
|
|
284
|
+
transform: translateX(-50%);
|
|
285
|
+
display: inline-flex;
|
|
286
|
+
align-items: flex-end;
|
|
287
|
+
gap: 2px;
|
|
288
|
+
pointer-events: none;
|
|
289
|
+
}
|
|
290
|
+
.jatui-mic-meter-bar {
|
|
291
|
+
width: 3px;
|
|
292
|
+
border-radius: 1px;
|
|
293
|
+
background: color-mix(in oklch, var(--color-base-content) 25%, transparent);
|
|
294
|
+
transition: background var(--motion-fast, 150ms) ease;
|
|
295
|
+
}
|
|
296
|
+
.jatui-mic-meter-bar.is-active {
|
|
297
|
+
background: var(--color-success);
|
|
298
|
+
}
|
|
299
|
+
</style>
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
* red "Leave" that stands apart. Presentational: it reflects the local
|
|
11
11
|
* participant + room state you pass and calls back on intent.
|
|
12
12
|
*/
|
|
13
|
-
import type { MeetParticipant, MeetRoomState, MeetHostActions, MeetLocalControls } from './types.js';
|
|
13
|
+
import type { MeetParticipant, MeetRoomState, MeetHostActions, MeetLocalControls, MeetDevicePickers } from './types.js';
|
|
14
14
|
interface Props {
|
|
15
15
|
/** The local participant (drives mic/cam/share pressed state). */
|
|
16
16
|
local: MeetParticipant;
|
|
@@ -24,6 +24,20 @@ interface Props {
|
|
|
24
24
|
controls?: MeetLocalControls;
|
|
25
25
|
/** Host-action callbacks. */
|
|
26
26
|
hostActions?: MeetHostActions;
|
|
27
|
+
/**
|
|
28
|
+
* Live input level of the local microphone, 0–1. When provided (and the
|
|
29
|
+
* mic is unmuted) the mic button renders a small 3-bar meter under the
|
|
30
|
+
* icon so the user can SEE that their voice is being picked up — the
|
|
31
|
+
* camera gives that feedback for free, the mic never did. Omit to hide.
|
|
32
|
+
*/
|
|
33
|
+
micLevel?: number;
|
|
34
|
+
/**
|
|
35
|
+
* In-meeting device switching. When provided, a settings gear renders a
|
|
36
|
+
* dropdown listing microphones and cameras with the active one checked —
|
|
37
|
+
* pick to switch live (mid-meeting mic/cam tweaking). Available to every
|
|
38
|
+
* participant, unlike the host menu.
|
|
39
|
+
*/
|
|
40
|
+
devices?: MeetDevicePickers;
|
|
27
41
|
class?: string;
|
|
28
42
|
}
|
|
29
43
|
declare const ControlBar: import("svelte").Component<Props, {}, "">;
|
package/dist/meet/index.d.ts
CHANGED
|
@@ -6,4 +6,4 @@ export { default as ParticipantList } from './ParticipantList.svelte';
|
|
|
6
6
|
export { default as RecordingBadge } from './RecordingBadge.svelte';
|
|
7
7
|
export { loadLiveKit, isLiveKitAvailable, LiveKitNotInstalledError } from './livekit.js';
|
|
8
8
|
export type { LiveKitModule } from './livekit.js';
|
|
9
|
-
export type { MeetRole, MeetConnectionQuality, MeetParticipant, MeetHostActions, MeetLocalControls, PreJoinResult, MeetRoomState, MeetLayout } from './types.js';
|
|
9
|
+
export type { MeetRole, MeetConnectionQuality, MeetParticipant, MeetHostActions, MeetLocalControls, MeetDeviceOption, MeetDevicePickers, PreJoinResult, MeetRoomState, MeetLayout } from './types.js';
|
package/dist/meet/types.d.ts
CHANGED
|
@@ -75,6 +75,31 @@ export interface MeetLocalControls {
|
|
|
75
75
|
/** Leave the meeting (local participant only). */
|
|
76
76
|
onLeave?: () => void;
|
|
77
77
|
}
|
|
78
|
+
/** One selectable input device (a distilled MediaDeviceInfo). */
|
|
79
|
+
export interface MeetDeviceOption {
|
|
80
|
+
deviceId: string;
|
|
81
|
+
label: string;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* In-meeting device pickers for ControlBar's settings dropdown. Presentational
|
|
85
|
+
* contract: the host app enumerates devices and performs the actual switch
|
|
86
|
+
* (e.g. livekit-client `room.switchActiveDevice`); ControlBar only renders the
|
|
87
|
+
* menu and reports intent.
|
|
88
|
+
*/
|
|
89
|
+
export interface MeetDevicePickers {
|
|
90
|
+
/** Available microphones. */
|
|
91
|
+
audioInputs: MeetDeviceOption[];
|
|
92
|
+
/** Available cameras. */
|
|
93
|
+
videoInputs: MeetDeviceOption[];
|
|
94
|
+
/** deviceId of the microphone currently in use. */
|
|
95
|
+
activeAudioId?: string;
|
|
96
|
+
/** deviceId of the camera currently in use. */
|
|
97
|
+
activeVideoId?: string;
|
|
98
|
+
/** User picked a microphone. */
|
|
99
|
+
onSelectAudio?: (deviceId: string) => void;
|
|
100
|
+
/** User picked a camera. */
|
|
101
|
+
onSelectVideo?: (deviceId: string) => void;
|
|
102
|
+
}
|
|
78
103
|
/** Result of the pre-join device check, handed to the join handler. */
|
|
79
104
|
export interface PreJoinResult {
|
|
80
105
|
/** Confirmed display name. */
|