@joewinke/jatui 0.2.27 → 0.2.29
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/CLAUDE.md +10 -0
- package/dist/components/VideoPlayer.svelte +1592 -0
- package/dist/components/VideoPlayer.svelte.d.ts +72 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +6 -0
- package/dist/meet/ControlBar.svelte +72 -1
- package/dist/meet/ControlBar.svelte.d.ts +8 -1
- package/dist/meet/index.d.ts +1 -1
- package/dist/meet/types.d.ts +25 -0
- package/package.json +1 -1
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/** A model-authored marker: the video's structure. */
|
|
2
|
+
export interface VideoChapter {
|
|
3
|
+
startSec: number;
|
|
4
|
+
title: string;
|
|
5
|
+
/** Optional captured still, shown in the band's hover card. */
|
|
6
|
+
thumbUrl?: string | null;
|
|
7
|
+
}
|
|
8
|
+
/** A diarized turn (or an ad read, `ad: true`). Structurally accepts meeting data. */
|
|
9
|
+
export interface VideoSpeakerTurn {
|
|
10
|
+
startSec: number;
|
|
11
|
+
endSec: number;
|
|
12
|
+
speakerIdentity?: string | null;
|
|
13
|
+
speakerName?: string | null;
|
|
14
|
+
/** Explicit oklch hue (0–360). Omit to take the next palette colour. */
|
|
15
|
+
hue?: number | null;
|
|
16
|
+
/** Ad read — shares the lane but reads as not-a-person. */
|
|
17
|
+
ad?: boolean;
|
|
18
|
+
}
|
|
19
|
+
/** Transcript line. Structurally compatible with TranscriptPlayer's `TranscriptSegment`. */
|
|
20
|
+
export interface VideoTranscriptSegment {
|
|
21
|
+
id: string;
|
|
22
|
+
startSec: number;
|
|
23
|
+
endSec: number;
|
|
24
|
+
speakerIdentity?: string | null;
|
|
25
|
+
speakerName?: string | null;
|
|
26
|
+
text: string;
|
|
27
|
+
}
|
|
28
|
+
/** A user-authored marker. Structurally compatible with `TranscriptBookmark`. */
|
|
29
|
+
export interface VideoBookmark {
|
|
30
|
+
playbackSec: number;
|
|
31
|
+
label?: string | null;
|
|
32
|
+
note?: string | null;
|
|
33
|
+
participantName?: string | null;
|
|
34
|
+
}
|
|
35
|
+
/** Precomputed audio envelope. `peaks` are normalised against their own max. */
|
|
36
|
+
export interface VideoWaveform {
|
|
37
|
+
peaks: number[];
|
|
38
|
+
/** Seconds the peaks span. Defaults to the video's duration. */
|
|
39
|
+
durationSec?: number;
|
|
40
|
+
}
|
|
41
|
+
/** Per-speaker totals. Structurally compatible with `TranscriptSpeakerTalkTime`. */
|
|
42
|
+
export interface VideoSpeakerTalkTime {
|
|
43
|
+
speakerIdentity?: string | null;
|
|
44
|
+
speakerName?: string | null;
|
|
45
|
+
seconds: number;
|
|
46
|
+
}
|
|
47
|
+
type $$ComponentProps = {
|
|
48
|
+
src: string;
|
|
49
|
+
poster?: string | null;
|
|
50
|
+
captionsSrc?: string | null;
|
|
51
|
+
captionsLabel?: string;
|
|
52
|
+
captionsLang?: string;
|
|
53
|
+
chapters?: VideoChapter[];
|
|
54
|
+
speakers?: VideoSpeakerTurn[];
|
|
55
|
+
transcript?: VideoTranscriptSegment[];
|
|
56
|
+
bookmarks?: VideoBookmark[];
|
|
57
|
+
waveform?: VideoWaveform | null;
|
|
58
|
+
talkTime?: VideoSpeakerTalkTime[];
|
|
59
|
+
/** Seek here once metadata lands (a queued seek never fires on a later source). */
|
|
60
|
+
startAt?: number | null;
|
|
61
|
+
transcriptPosition?: 'below' | 'side';
|
|
62
|
+
transcriptMaxHeight?: string;
|
|
63
|
+
onTimeUpdate?: (video: HTMLVideoElement) => void;
|
|
64
|
+
/** Present ⇒ the band grows an "add bookmark at current time" control. */
|
|
65
|
+
onAddBookmark?: (playbackSec: number, label: string) => void;
|
|
66
|
+
/** Right-click on the band. The host owns the panel; this only hit-tests. */
|
|
67
|
+
onBookmarkContext?: (sec: number, existing: VideoBookmark | null, clientX: number, clientY: number) => void;
|
|
68
|
+
class?: string;
|
|
69
|
+
};
|
|
70
|
+
declare const VideoPlayer: import("svelte").Component<$$ComponentProps, {}, "">;
|
|
71
|
+
type VideoPlayer = ReturnType<typeof VideoPlayer>;
|
|
72
|
+
export default VideoPlayer;
|
package/dist/index.d.ts
CHANGED
|
@@ -11,6 +11,8 @@ export { default as ChapterTimeline } from './components/replay/ChapterTimeline.
|
|
|
11
11
|
export type { ChapterBlock } from './components/replay/ChapterTimeline.svelte';
|
|
12
12
|
export { default as TranscriptPlayer } from './components/TranscriptPlayer.svelte';
|
|
13
13
|
export type { TranscriptSegment, TranscriptBookmark, TranscriptChapter, TranscriptSpeakerTalkTime, } from './components/TranscriptPlayer.svelte';
|
|
14
|
+
export { default as VideoPlayer } from './components/VideoPlayer.svelte';
|
|
15
|
+
export type { VideoChapter, VideoSpeakerTurn, VideoTranscriptSegment, VideoBookmark, VideoWaveform, VideoSpeakerTalkTime, } from './components/VideoPlayer.svelte';
|
|
14
16
|
export { buildLinkedSession, buildLinkedSessionFromWork, rawTypeToState, sampleLinkedSession, SAMPLE_TRANSCRIPT_LINES, } from './components/session-nav/transcriptModel';
|
|
15
17
|
export type { ItemKind, TranscriptItem, FileTouchedEntry, SignalNode, LinkedSession, WorkBlockClient, WorkTranscriptItemClient, } from './components/session-nav/transcriptModel';
|
|
16
18
|
export { default as UserAvatar } from './components/UserAvatar.svelte';
|
package/dist/index.js
CHANGED
|
@@ -7,6 +7,12 @@ export { default as Collapse } from './components/Collapse.svelte';
|
|
|
7
7
|
export { default as LinkedColumns } from './components/linked-columns/LinkedColumns.svelte';
|
|
8
8
|
export { default as ChapterTimeline } from './components/replay/ChapterTimeline.svelte';
|
|
9
9
|
export { default as TranscriptPlayer } from './components/TranscriptPlayer.svelte';
|
|
10
|
+
// The generalized player: everything TranscriptPlayer does plus the
|
|
11
|
+
// extension-library timeline band (waveform scrubber, chapters, diarized
|
|
12
|
+
// speaker colouring, bookmarks, scroll-to-zoom), with every intelligence prop
|
|
13
|
+
// optional so a bare mp4 still gets the full playback UX. TranscriptPlayer
|
|
14
|
+
// stays exported unchanged — meetings already depends on it.
|
|
15
|
+
export { default as VideoPlayer } from './components/VideoPlayer.svelte';
|
|
10
16
|
// Data models — Session transcript (from JAT IDE, jat-jj79f.16 G1)
|
|
11
17
|
export { buildLinkedSession, buildLinkedSessionFromWork, rawTypeToState, sampleLinkedSession, SAMPLE_TRANSCRIPT_LINES, } from './components/session-nav/transcriptModel';
|
|
12
18
|
// Components — Universal
|
|
@@ -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). */
|
|
@@ -33,6 +39,13 @@
|
|
|
33
39
|
* camera gives that feedback for free, the mic never did. Omit to hide.
|
|
34
40
|
*/
|
|
35
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;
|
|
36
49
|
class?: string;
|
|
37
50
|
}
|
|
38
51
|
|
|
@@ -44,6 +57,7 @@
|
|
|
44
57
|
controls = {},
|
|
45
58
|
hostActions = {},
|
|
46
59
|
micLevel,
|
|
60
|
+
devices,
|
|
47
61
|
class: className = ''
|
|
48
62
|
}: Props = $props();
|
|
49
63
|
|
|
@@ -136,6 +150,63 @@
|
|
|
136
150
|
</button>
|
|
137
151
|
{/if}
|
|
138
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
|
+
|
|
139
210
|
{#if isHost}
|
|
140
211
|
<div class="mx-1 h-8 w-px bg-base-content/15"></div>
|
|
141
212
|
|
|
@@ -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;
|
|
@@ -31,6 +31,13 @@ interface Props {
|
|
|
31
31
|
* camera gives that feedback for free, the mic never did. Omit to hide.
|
|
32
32
|
*/
|
|
33
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;
|
|
34
41
|
class?: string;
|
|
35
42
|
}
|
|
36
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. */
|