@arbidocs/blocks 0.4.109 → 0.4.111
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/{NativeDocxEditor-WODZAURG.cjs → NativeDocxEditor-AQKRZBIU.cjs} +7 -7
- package/dist/{NativeDocxEditor-WODZAURG.cjs.map → NativeDocxEditor-AQKRZBIU.cjs.map} +1 -1
- package/dist/{NativeDocxEditor-4PY4YZYC.js → NativeDocxEditor-IN2HEO5I.js} +3 -3
- package/dist/{NativeDocxEditor-4PY4YZYC.js.map → NativeDocxEditor-IN2HEO5I.js.map} +1 -1
- package/dist/agents/index.cjs +286 -286
- package/dist/agents/index.js +10 -10
- package/dist/agents/tasks/ConversationListView.d.ts +5 -1
- package/dist/agents/tasks/TaskActivityTimeline.d.ts +5 -1
- package/dist/calendar/EventDetails.d.ts +1 -1
- package/dist/calendar/EventEditor.d.ts +1 -1
- package/dist/calendar/draft.d.ts +7 -1
- package/dist/calendar/index.cjs +71 -10
- package/dist/calendar/index.cjs.map +1 -1
- package/dist/calendar/index.d.ts +1 -1
- package/dist/calendar/index.js +69 -11
- package/dist/calendar/index.js.map +1 -1
- package/dist/calendar/types.d.ts +23 -0
- package/dist/calls/CallSidePanel.d.ts +32 -0
- package/dist/calls/CallStage.d.ts +57 -0
- package/dist/calls/ControlBar.d.ts +113 -0
- package/dist/calls/ControlButton.d.ts +44 -0
- package/dist/calls/PreJoin.d.ts +35 -0
- package/dist/calls/cards.d.ts +58 -0
- package/dist/calls/hooks.d.ts +20 -0
- package/dist/calls/index.cjs +2884 -0
- package/dist/calls/index.cjs.map +1 -0
- package/dist/calls/index.d.ts +21 -0
- package/dist/calls/index.js +2753 -0
- package/dist/calls/index.js.map +1 -0
- package/dist/calls/join-choices.d.ts +11 -0
- package/dist/calls/layout.d.ts +106 -0
- package/dist/calls/panels.d.ts +66 -0
- package/dist/calls/status.d.ts +69 -0
- package/dist/calls/tiles.d.ts +59 -0
- package/dist/calls/webinar/ChatPermissionSetting.d.ts +18 -0
- package/dist/calls/webinar/OnStageCheck.d.ts +23 -0
- package/dist/calls/webinar/QAPanel.d.ts +56 -0
- package/dist/calls/webinar/RaiseHandButton.d.ts +14 -0
- package/dist/calls/webinar/WebinarAudiencePanel.d.ts +23 -0
- package/dist/calls/webinar/WebinarViewer.d.ts +39 -0
- package/dist/calls/webinar/WebinarWaiting.d.ts +19 -0
- package/dist/calls/webinar/hooks.d.ts +13 -0
- package/dist/calls/webinar/index.d.ts +14 -0
- package/dist/calls/webinar/live.d.ts +43 -0
- package/dist/calls/webinar/types.d.ts +49 -0
- package/dist/chat/Message.d.ts +17 -2
- package/dist/chat/TranscriptLine.d.ts +50 -0
- package/dist/chat/index.cjs +147 -126
- package/dist/chat/index.d.ts +3 -1
- package/dist/chat/index.js +5 -4
- package/dist/chat/transcript-runs.d.ts +6 -0
- package/dist/{chunk-GKDWQLVU.js → chunk-2C7TCVQZ.js} +172 -121
- package/dist/chunk-2C7TCVQZ.js.map +1 -0
- package/dist/{chunk-QKWQMOHN.cjs → chunk-2UAJVK3C.cjs} +4 -4
- package/dist/{chunk-QKWQMOHN.cjs.map → chunk-2UAJVK3C.cjs.map} +1 -1
- package/dist/{chunk-54WBIVHV.cjs → chunk-2WO5L7YT.cjs} +5 -5
- package/dist/{chunk-54WBIVHV.cjs.map → chunk-2WO5L7YT.cjs.map} +1 -1
- package/dist/{chunk-7C35WXYB.js → chunk-5HFPRI3W.js} +5 -5
- package/dist/chunk-5HFPRI3W.js.map +1 -0
- package/dist/{chunk-ZSV2NQQP.js → chunk-6RHHMBZ5.js} +158 -5
- package/dist/chunk-6RHHMBZ5.js.map +1 -0
- package/dist/{chunk-FQX6N5G6.js → chunk-6Z7DLJU2.js} +35 -18
- package/dist/chunk-6Z7DLJU2.js.map +1 -0
- package/dist/{chunk-PGDL26TQ.js → chunk-AY32GGKU.js} +10 -4
- package/dist/{chunk-PGDL26TQ.js.map → chunk-AY32GGKU.js.map} +1 -1
- package/dist/{chunk-S55VDTX3.js → chunk-CECWAIHL.js} +3 -3
- package/dist/{chunk-S55VDTX3.js.map → chunk-CECWAIHL.js.map} +1 -1
- package/dist/{chunk-UABPHZWZ.js → chunk-CXZ7FPTQ.js} +3 -3
- package/dist/{chunk-UABPHZWZ.js.map → chunk-CXZ7FPTQ.js.map} +1 -1
- package/dist/{chunk-C2HRZSAT.cjs → chunk-DJM7DLCT.cjs} +5 -5
- package/dist/chunk-DJM7DLCT.cjs.map +1 -0
- package/dist/{chunk-GBYY3PIC.cjs → chunk-FXUQZFAN.cjs} +79 -62
- package/dist/chunk-FXUQZFAN.cjs.map +1 -0
- package/dist/{chunk-W7NZRZ54.js → chunk-GFYGYME3.js} +3 -3
- package/dist/{chunk-W7NZRZ54.js.map → chunk-GFYGYME3.js.map} +1 -1
- package/dist/chunk-GICUGQNN.cjs +76 -0
- package/dist/chunk-GICUGQNN.cjs.map +1 -0
- package/dist/{chunk-I4XQCE6U.cjs → chunk-HBBJYRGI.cjs} +162 -5
- package/dist/{chunk-ZSV2NQQP.js.map → chunk-HBBJYRGI.cjs.map} +1 -1
- package/dist/chunk-JJLMCV3T.js +73 -0
- package/dist/chunk-JJLMCV3T.js.map +1 -0
- package/dist/chunk-MWDW5UBW.cjs +199 -0
- package/dist/chunk-MWDW5UBW.cjs.map +1 -0
- package/dist/{chunk-NUTLXVPX.js → chunk-NRUPUDWB.js} +16 -7
- package/dist/chunk-NRUPUDWB.js.map +1 -0
- package/dist/{chunk-5YXG342J.cjs → chunk-OR4CO743.cjs} +3 -3
- package/dist/{chunk-5YXG342J.cjs.map → chunk-OR4CO743.cjs.map} +1 -1
- package/dist/{chunk-DDF5GPIJ.js → chunk-P5ST47VY.js} +3 -3
- package/dist/{chunk-DDF5GPIJ.js.map → chunk-P5ST47VY.js.map} +1 -1
- package/dist/{chunk-PEHLUB52.js → chunk-Q6B77ZWZ.js} +3 -3
- package/dist/{chunk-PEHLUB52.js.map → chunk-Q6B77ZWZ.js.map} +1 -1
- package/dist/{chunk-ELDUWTRR.cjs → chunk-TF47ATYS.cjs} +4 -4
- package/dist/{chunk-ELDUWTRR.cjs.map → chunk-TF47ATYS.cjs.map} +1 -1
- package/dist/{chunk-PDRYGPZB.cjs → chunk-VP4KJ4LK.cjs} +16 -6
- package/dist/chunk-VP4KJ4LK.cjs.map +1 -0
- package/dist/{chunk-SKSE4OT6.cjs → chunk-WPVIOGDG.cjs} +5 -5
- package/dist/{chunk-SKSE4OT6.cjs.map → chunk-WPVIOGDG.cjs.map} +1 -1
- package/dist/{chunk-JB32KYAH.js → chunk-WTM65ZNG.js} +3 -3
- package/dist/{chunk-JB32KYAH.js.map → chunk-WTM65ZNG.js.map} +1 -1
- package/dist/{chunk-2NIL2ASF.cjs → chunk-XIGWUDOD.cjs} +170 -119
- package/dist/chunk-XIGWUDOD.cjs.map +1 -0
- package/dist/{chunk-3US5OIDQ.cjs → chunk-XSUOEJB3.cjs} +9 -3
- package/dist/chunk-XSUOEJB3.cjs.map +1 -0
- package/dist/chunk-Z3AZ7Q5X.js +177 -0
- package/dist/chunk-Z3AZ7Q5X.js.map +1 -0
- package/dist/{chunk-Z5VUZ6YD.cjs → chunk-ZV3BKLFY.cjs} +3 -3
- package/dist/{chunk-Z5VUZ6YD.cjs.map → chunk-ZV3BKLFY.cjs.map} +1 -1
- package/dist/collaboration/index.cjs +42 -42
- package/dist/collaboration/index.js +2 -2
- package/dist/connect/docx.cjs +6 -6
- package/dist/connect/docx.js +4 -4
- package/dist/connect/index.cjs +72 -72
- package/dist/connect/index.js +18 -18
- package/dist/docx/AgentUndo.d.ts +4 -0
- package/dist/docx/index.cjs +46 -46
- package/dist/docx/index.js +3 -3
- package/dist/facts/index.cjs +4 -4
- package/dist/facts/index.js +4 -4
- package/dist/files/index.cjs +6 -6
- package/dist/files/index.js +6 -6
- package/dist/home/index.cjs +17 -17
- package/dist/home/index.js +3 -3
- package/dist/index.cjs +958 -937
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +24 -23
- package/dist/index.js.map +1 -1
- package/dist/retrieval/index.cjs +1 -1
- package/dist/retrieval/index.js +1 -1
- package/dist/settings/index.cjs +50 -104
- package/dist/settings/index.cjs.map +1 -1
- package/dist/settings/index.js +12 -72
- package/dist/settings/index.js.map +1 -1
- package/dist/shell/index.cjs +12 -12
- package/dist/shell/index.js +4 -4
- package/dist/site/index.cjs +5 -5
- package/dist/site/index.js +1 -1
- package/dist/studio/index.cjs +5 -5
- package/dist/studio/index.js +4 -4
- package/dist/workflow-canvas/index.cjs +53 -53
- package/dist/workflow-canvas/index.js +10 -10
- package/dist/workspaces/index.cjs +2 -2
- package/dist/workspaces/index.js +2 -2
- package/node_modules/@arbidocs/coedit/dist/commands.cjs +179 -9
- package/node_modules/@arbidocs/coedit/dist/commands.cjs.map +1 -1
- package/node_modules/@arbidocs/coedit/dist/commands.d.cts +81 -1
- package/node_modules/@arbidocs/coedit/dist/commands.d.ts +81 -1
- package/node_modules/@arbidocs/coedit/dist/commands.js +173 -11
- package/node_modules/@arbidocs/coedit/dist/commands.js.map +1 -1
- package/node_modules/@arbidocs/coedit/dist/server.cjs +58 -2
- package/node_modules/@arbidocs/coedit/dist/server.cjs.map +1 -1
- package/node_modules/@arbidocs/coedit/dist/server.d.cts +29 -1
- package/node_modules/@arbidocs/coedit/dist/server.d.ts +29 -1
- package/node_modules/@arbidocs/coedit/dist/server.js +58 -3
- package/node_modules/@arbidocs/coedit/dist/server.js.map +1 -1
- package/node_modules/@arbidocs/coedit/package.json +1 -1
- package/package.json +15 -5
- package/src/calls/calls.css +157 -0
- package/dist/chunk-2NIL2ASF.cjs.map +0 -1
- package/dist/chunk-3US5OIDQ.cjs.map +0 -1
- package/dist/chunk-7C35WXYB.js.map +0 -1
- package/dist/chunk-C2HRZSAT.cjs.map +0 -1
- package/dist/chunk-FQX6N5G6.js.map +0 -1
- package/dist/chunk-GBYY3PIC.cjs.map +0 -1
- package/dist/chunk-GKDWQLVU.js.map +0 -1
- package/dist/chunk-I4XQCE6U.cjs.map +0 -1
- package/dist/chunk-NUTLXVPX.js.map +0 -1
- package/dist/chunk-PDRYGPZB.cjs.map +0 -1
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure layout helpers for the call stage: the adaptive tile grid, the per-speaker caption accent,
|
|
3
|
+
* the picture-in-picture corner and the elapsed-time label. No React, no LiveKit: unit-tested on
|
|
4
|
+
* their own.
|
|
5
|
+
*/
|
|
6
|
+
/** Tiles are 16:9, like the cameras they show. */
|
|
7
|
+
export declare const TILE_ASPECT: number;
|
|
8
|
+
/** How long a status notice ("Recording — started by X") stays spelled out before it folds. */
|
|
9
|
+
export declare const NOTICE_MS = 6000;
|
|
10
|
+
/** How long a caption stays on screen. */
|
|
11
|
+
export declare const CAPTION_TTL_MS = 8000;
|
|
12
|
+
/** Caption lines shown at once (two lines, the newest at the bottom). */
|
|
13
|
+
export declare const CAPTION_LINES = 2;
|
|
14
|
+
/** Tiles shown in the grid before the rest fold into a "+N" tile. */
|
|
15
|
+
export declare const MAX_GRID_TILES = 9;
|
|
16
|
+
export interface GridLayout {
|
|
17
|
+
cols: number;
|
|
18
|
+
rows: number;
|
|
19
|
+
/** Tiles per row, top to bottom (a short last row is centred: 3 → [2, 1]). */
|
|
20
|
+
rowCounts: number[];
|
|
21
|
+
tileWidth: number;
|
|
22
|
+
tileHeight: number;
|
|
23
|
+
/** The cell a tile fills when the grid fills the whole stage (the layout's rows × columns). */
|
|
24
|
+
cellWidth: number;
|
|
25
|
+
cellHeight: number;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* The grid that gives `count` tiles of a fixed aspect the largest size inside `width × height`
|
|
29
|
+
* with `gap` between them, preferring a row-first layout when it is nearly as large.
|
|
30
|
+
*/
|
|
31
|
+
export declare function gridLayout(count: number, width: number, height: number, gap?: number, aspect?: number): GridLayout;
|
|
32
|
+
/** Spread `count` tiles over rows of at most `cols`, the fuller rows first: 5 in 3 → [3, 2]. */
|
|
33
|
+
export declare function rowCounts(count: number, cols: number): number[];
|
|
34
|
+
/** What the grid shows: up to {@link MAX_GRID_TILES}; past that, one slot becomes "+N more". */
|
|
35
|
+
export declare function gridSlots<T>(items: T[], max?: number): {
|
|
36
|
+
shown: T[];
|
|
37
|
+
hidden: number;
|
|
38
|
+
};
|
|
39
|
+
/**
|
|
40
|
+
* Speaker accents for captions: semantic text tokens that meet AA contrast on the muted surface
|
|
41
|
+
* in both themes. The same identity always gets the same colour.
|
|
42
|
+
*/
|
|
43
|
+
export declare const SPEAKER_ACCENTS: readonly ["text-blue-text-strong", "text-purple-text-strong", "text-green-text-strong", "text-yellow-text-strong", "text-red-text-strong"];
|
|
44
|
+
export declare function speakerAccent(identity: string): (typeof SPEAKER_ACCENTS)[number];
|
|
45
|
+
/** The same speaker's colour as a border (the live transcript line). */
|
|
46
|
+
export declare const SPEAKER_BORDERS: readonly ["border-blue-text-strong", "border-purple-text-strong", "border-green-text-strong", "border-yellow-text-strong", "border-red-text-strong"];
|
|
47
|
+
export declare function speakerBorder(identity: string): (typeof SPEAKER_BORDERS)[number];
|
|
48
|
+
/** The speaker's colour as an inset ring (the line being spoken). */
|
|
49
|
+
export declare const SPEAKER_RINGS: readonly ["ring-blue-text-strong/40", "ring-purple-text-strong/40", "ring-green-text-strong/40", "ring-yellow-text-strong/40", "ring-red-text-strong/40"];
|
|
50
|
+
export declare function speakerRing(identity: string): (typeof SPEAKER_RINGS)[number];
|
|
51
|
+
/** The speaker's colour as a fill (the "is speaking" dots). */
|
|
52
|
+
export declare const SPEAKER_DOTS: readonly ["bg-blue-text-strong", "bg-purple-text-strong", "bg-green-text-strong", "bg-yellow-text-strong", "bg-red-text-strong"];
|
|
53
|
+
export declare function speakerDot(identity: string): (typeof SPEAKER_DOTS)[number];
|
|
54
|
+
export type PipCorner = 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right';
|
|
55
|
+
/** The viewport corner nearest to a point (where a dragged picture-in-picture card settles). */
|
|
56
|
+
export declare function nearestCorner(x: number, y: number, width: number, height: number): PipCorner;
|
|
57
|
+
/** `m:ss`, or `h:mm:ss` past an hour. */
|
|
58
|
+
export declare function formatElapsed(ms: number): string;
|
|
59
|
+
/** Up to two initials for a camera-off tile: "Alice Morgan" → "AM", "usr-1a2b" → "U". */
|
|
60
|
+
export declare function initialsOf(name: string): string;
|
|
61
|
+
/** Who has the spotlight (speaker view), and who is waiting to take it. */
|
|
62
|
+
export interface SpotlightState {
|
|
63
|
+
id: string | null;
|
|
64
|
+
/** A louder voice that must stay dominant for `holdMs` before it takes over. */
|
|
65
|
+
candidate: string | null;
|
|
66
|
+
since: number;
|
|
67
|
+
}
|
|
68
|
+
export declare const INITIAL_SPOTLIGHT: SpotlightState;
|
|
69
|
+
/** A voice at or above this level (0..1) counts as speaking. */
|
|
70
|
+
export declare const SPEAKING_LEVEL = 0.08;
|
|
71
|
+
/**
|
|
72
|
+
* The speaker view's spotlight, with hysteresis: a new voice takes the spotlight only after it has
|
|
73
|
+
* dominated (`margin` × louder than the current one, above `threshold`) for `holdMs`, so a cough
|
|
74
|
+
* does not flick the view; while several talk at once and nobody dominates, it stays put. With
|
|
75
|
+
* nobody in the spotlight yet, the first voice takes it at once. `exclude` (my own identity) never
|
|
76
|
+
* takes the spotlight: nobody needs to watch themselves large (Meet / Zoom behaviour).
|
|
77
|
+
*/
|
|
78
|
+
export declare function nextSpotlight(state: SpotlightState, levels: Record<string, number>, now: number, { threshold, holdMs, margin, exclude }?: {
|
|
79
|
+
threshold?: number | undefined;
|
|
80
|
+
holdMs?: number | undefined;
|
|
81
|
+
margin?: number | undefined;
|
|
82
|
+
exclude?: readonly string[] | undefined;
|
|
83
|
+
}): SpotlightState;
|
|
84
|
+
/**
|
|
85
|
+
* Who fills the speaker view's large slot: the spotlight if it is someone else in the call, else
|
|
86
|
+
* the first other person, else me (alone in the call). Never me while anyone else is there.
|
|
87
|
+
*/
|
|
88
|
+
export declare function pickSpotlight(people: readonly {
|
|
89
|
+
id: string;
|
|
90
|
+
local: boolean;
|
|
91
|
+
}[], spotlightId: string | null): string | undefined;
|
|
92
|
+
/**
|
|
93
|
+
* The speaker view's large tile: the biggest 16:9 box that fits, leaving room for the filmstrip
|
|
94
|
+
* (below it on a narrow stage — a row of three 16:9 thumbnails per line — or a 15rem column beside
|
|
95
|
+
* it). It never takes a portrait shape: spare height stays blank under the filmstrip.
|
|
96
|
+
*/
|
|
97
|
+
export declare function spotlightBox(width: number, height: number, others: number, narrow: boolean, gap?: number, aspect?: number): {
|
|
98
|
+
width: number;
|
|
99
|
+
height: number;
|
|
100
|
+
};
|
|
101
|
+
/**
|
|
102
|
+
* How tall the call stage's tiles are, for a stage `width` wide and at most `maxHeight` tall:
|
|
103
|
+
* speaker view (`focus`: the large 16:9 tile, plus the filmstrip under it on a narrow stage) or
|
|
104
|
+
* gallery (`grid`). The panel gives the rest of its height to the participant list.
|
|
105
|
+
*/
|
|
106
|
+
export declare function stageContentHeight(view: 'focus' | 'grid', width: number, maxHeight: number, count: number, gap?: number, aspect?: number): number;
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The call panel's participant list and the lobby requests. Presentational: the app supplies the people
|
|
3
|
+
* and the actions. (Chat happens in the call's conversation, beside the panel.)
|
|
4
|
+
*/
|
|
5
|
+
import { type ReactElement, type ReactNode } from 'react';
|
|
6
|
+
export interface CallPerson {
|
|
7
|
+
id: string;
|
|
8
|
+
name: string;
|
|
9
|
+
isLocal: boolean;
|
|
10
|
+
micMuted?: boolean;
|
|
11
|
+
/** Publishing an unmuted camera (the host may turn it off). */
|
|
12
|
+
cameraOn?: boolean;
|
|
13
|
+
/** Talking now, and how loud (0..1): the live speaker list. */
|
|
14
|
+
speaking?: boolean;
|
|
15
|
+
level?: number;
|
|
16
|
+
/** Extra state after the name (e.g. "Presenting"). */
|
|
17
|
+
note?: ReactNode;
|
|
18
|
+
/** The person as the app shows people (a pill); else initials and `name`. */
|
|
19
|
+
entity?: ReactNode;
|
|
20
|
+
}
|
|
21
|
+
/** A small live voice meter: five bars lit by level; "muted" when the mic is off. */
|
|
22
|
+
export declare function VoiceMeter({ level, muted, className, }: {
|
|
23
|
+
level: number;
|
|
24
|
+
muted?: boolean;
|
|
25
|
+
className?: string;
|
|
26
|
+
}): ReactElement;
|
|
27
|
+
/**
|
|
28
|
+
* A person's name as the call shows people: the app's pill (`entity`, which says "(me)" itself),
|
|
29
|
+
* else initials and the name, with `youLabel` after my own. Every list of people in the call
|
|
30
|
+
* (participants, lobby, webinar audience, Q&A askers) names them through this one piece.
|
|
31
|
+
*/
|
|
32
|
+
export declare function CallPersonLabel({ name, entity, isLocal, youLabel, }: {
|
|
33
|
+
name: string;
|
|
34
|
+
entity?: ReactNode;
|
|
35
|
+
isLocal?: boolean;
|
|
36
|
+
youLabel?: string;
|
|
37
|
+
}): ReactElement;
|
|
38
|
+
/**
|
|
39
|
+
* Who is in the call, always on show under the video in the call panel: a live speaker list (the
|
|
40
|
+
* app orders it, talkers first), each row with the person's pill, voice meter, mic and camera
|
|
41
|
+
* state, and — for the host — mute / ask to mute / remove in its "…". Scrolls when long. In a
|
|
42
|
+
* short panel (`compact`) it folds to one strip — who is talking, and "Show all (N)".
|
|
43
|
+
*/
|
|
44
|
+
export declare function CallParticipants({ people, canRemove, removingId, onRemove, onAskToMute, onMute, compact, className, }: {
|
|
45
|
+
people: CallPerson[];
|
|
46
|
+
canRemove: boolean;
|
|
47
|
+
removingId?: string | null;
|
|
48
|
+
onRemove(person: CallPerson): void;
|
|
49
|
+
/** Shown for others whose mic is on (the host). */
|
|
50
|
+
onAskToMute?(person: CallPerson): void;
|
|
51
|
+
/** The host's force-mute of a mic (`audio`, shown while it is on) or a camera (`video`, while on). */
|
|
52
|
+
onMute?(person: CallPerson, track: 'audio' | 'video'): void;
|
|
53
|
+
compact?: boolean;
|
|
54
|
+
className?: string;
|
|
55
|
+
}): ReactElement;
|
|
56
|
+
export interface LobbyRequest {
|
|
57
|
+
id: string;
|
|
58
|
+
name: string;
|
|
59
|
+
/** The person as the app shows people (a pill); else `name`. */
|
|
60
|
+
entity?: ReactNode;
|
|
61
|
+
}
|
|
62
|
+
/** People asking to join, as cards over the stage's top-left corner. */
|
|
63
|
+
export declare function LobbyRequests({ requests, onDecide, }: {
|
|
64
|
+
requests: LobbyRequest[];
|
|
65
|
+
onDecide(id: string, admit: boolean): void;
|
|
66
|
+
}): ReactElement | null;
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The call's status, each shown once in the header (or on the minimised card): recording,
|
|
3
|
+
* transcription and encryption chips, and the live captions strip below the tiles.
|
|
4
|
+
*
|
|
5
|
+
* Recording and transcription chips first spell out who started them ("Recording (Audio) —
|
|
6
|
+
* started by Ada"), then fold to a short label after {@link NOTICE_MS}; the full sentence stays
|
|
7
|
+
* available to screen readers and as the tooltip.
|
|
8
|
+
*/
|
|
9
|
+
import { type ReactElement, type ReactNode } from 'react';
|
|
10
|
+
/** "Recording" with a pulsing dot. */
|
|
11
|
+
export declare function RecordingStatusChip({ sentence, short, compact, announce, onAnnounced, className, ...rest }: {
|
|
12
|
+
/** Spell the sentence out first (false once it has been, e.g. after a re-mount). */
|
|
13
|
+
announce?: boolean;
|
|
14
|
+
onAnnounced?(): void;
|
|
15
|
+
/** e.g. "Recording (Audio + video) — started by you". */
|
|
16
|
+
sentence: string;
|
|
17
|
+
short?: string;
|
|
18
|
+
/** Dot only once folded (a narrow header, the minimised card). */
|
|
19
|
+
compact?: boolean;
|
|
20
|
+
className?: string;
|
|
21
|
+
} & Omit<React.HTMLAttributes<HTMLSpanElement>, 'title'>): ReactElement;
|
|
22
|
+
/** "Transcribing", with my own "stop sending my voice" switch in it. */
|
|
23
|
+
export declare function TranscriptionStatusChip({ sentence, announce, onAnnounced, short, detail, muted, onMutedChange, muteLabel, unmuteLabel, compact, className, ...rest }: {
|
|
24
|
+
/** e.g. "Transcription on — started by Ada". */
|
|
25
|
+
sentence: string;
|
|
26
|
+
announce?: boolean;
|
|
27
|
+
onAnnounced?(): void;
|
|
28
|
+
short?: string;
|
|
29
|
+
/** Tooltip after the sentence (what transcription does). */
|
|
30
|
+
detail?: string;
|
|
31
|
+
muted: boolean;
|
|
32
|
+
onMutedChange(muted: boolean): void;
|
|
33
|
+
muteLabel: string;
|
|
34
|
+
unmuteLabel: string;
|
|
35
|
+
compact?: boolean;
|
|
36
|
+
className?: string;
|
|
37
|
+
} & Omit<React.HTMLAttributes<HTMLSpanElement>, 'title'>): ReactElement;
|
|
38
|
+
/** Whether the media is end-to-end encrypted. */
|
|
39
|
+
export declare function EncryptionStatusChip({ state, compact, labels, }: {
|
|
40
|
+
state: 'on' | 'pending' | 'off';
|
|
41
|
+
compact?: boolean;
|
|
42
|
+
labels?: {
|
|
43
|
+
on: string;
|
|
44
|
+
pending: string;
|
|
45
|
+
off: string;
|
|
46
|
+
onTitle: string;
|
|
47
|
+
offTitle: string;
|
|
48
|
+
};
|
|
49
|
+
}): ReactElement;
|
|
50
|
+
/** One caption: a speaker's latest segment. */
|
|
51
|
+
export interface CaptionEntry {
|
|
52
|
+
id: string;
|
|
53
|
+
speakerId: string;
|
|
54
|
+
name: string;
|
|
55
|
+
text: string;
|
|
56
|
+
/** Local receipt time, ms (drives the fade). */
|
|
57
|
+
receivedAt: number;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Live captions in a strip below the tiles (never over a face): at most two lines, the newest at
|
|
61
|
+
* the bottom, each speaker's name in their own accent. With `reserve` the strip keeps its height
|
|
62
|
+
* while empty, so the tiles do not jump each time someone starts or stops talking.
|
|
63
|
+
*/
|
|
64
|
+
export declare function CaptionsStrip({ captions, reserve, waitingText, className, }: {
|
|
65
|
+
captions: CaptionEntry[];
|
|
66
|
+
reserve?: boolean;
|
|
67
|
+
waitingText?: ReactNode;
|
|
68
|
+
className?: string;
|
|
69
|
+
}): ReactElement | null;
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The call stage's tiles: one participant tile (with a voice-level ring, so it is plain who is
|
|
3
|
+
* talking), the gallery grid (fills the stage: 1, 2 side by side, 2 + 1, 2 × 2, 3 + 2, …), and the
|
|
4
|
+
* spotlight layout — the speaker (or a shared screen) large, everyone else in a filmstrip.
|
|
5
|
+
*
|
|
6
|
+
* Media-library free: the app hands each tile its `<video>` (or nothing, for initials) and its
|
|
7
|
+
* state. Extra props (data attributes the app's recorder reads, say) land on the tile's root.
|
|
8
|
+
* Styles: `@arbidocs/blocks/calls.css`.
|
|
9
|
+
*/
|
|
10
|
+
import { type HTMLAttributes, type ReactElement, type ReactNode } from 'react';
|
|
11
|
+
export interface CallTileProps extends Omit<HTMLAttributes<HTMLDivElement>, 'children'> {
|
|
12
|
+
/**
|
|
13
|
+
* The name, once, at the bottom left: a node (the app's person pill, say), or a string shown in
|
|
14
|
+
* a small dark chip.
|
|
15
|
+
*/
|
|
16
|
+
name: ReactNode;
|
|
17
|
+
/** Shown large while there is no video (camera off). */
|
|
18
|
+
initials: string;
|
|
19
|
+
/** The `<video>` element; omit while the camera is off. */
|
|
20
|
+
video?: ReactNode;
|
|
21
|
+
source?: 'camera' | 'screen';
|
|
22
|
+
/** My own tile: the name says "(me)". */
|
|
23
|
+
local?: boolean;
|
|
24
|
+
/** Mirror the video (my own camera, as a selfie). Only the <video> flips; labels never do. */
|
|
25
|
+
mirror?: boolean;
|
|
26
|
+
/** Talking now: a ring (thicker with `level`) and a sound icon by the name — never colour alone. */
|
|
27
|
+
speaking?: boolean;
|
|
28
|
+
/** Voice level 0..1, updated fast (~60 ms): drives the ring's size. */
|
|
29
|
+
level?: number;
|
|
30
|
+
micMuted?: boolean;
|
|
31
|
+
poorConnection?: boolean;
|
|
32
|
+
/** Small tiles (filmstrip, picture-in-picture) use a smaller avatar. */
|
|
33
|
+
size?: 'normal' | 'small';
|
|
34
|
+
/** Text after my own name when `name` is a string (a pill says "(me)" itself). */
|
|
35
|
+
youLabel?: string;
|
|
36
|
+
}
|
|
37
|
+
/** One participant (or screen-share) tile. */
|
|
38
|
+
export declare const CallTile: import("react").ForwardRefExoticComponent<CallTileProps & import("react").RefAttributes<HTMLDivElement>>;
|
|
39
|
+
/** The adaptive grid. Tiles keep 16:9 and the largest size the stage allows. */
|
|
40
|
+
export declare function CallGrid<T>({ items, getKey, renderTile, moreLabel, className, }: {
|
|
41
|
+
items: T[];
|
|
42
|
+
getKey(item: T): string;
|
|
43
|
+
/** The tile for one item; it fills a box the grid sizes. */
|
|
44
|
+
renderTile(item: T): ReactElement;
|
|
45
|
+
moreLabel?(hidden: number): string;
|
|
46
|
+
className?: string;
|
|
47
|
+
}): ReactElement;
|
|
48
|
+
/**
|
|
49
|
+
* Spotlight: one tile large (the speaker, or a shared screen), everyone else in a filmstrip
|
|
50
|
+
* beside it — below it on a narrow stage such as the docked call panel.
|
|
51
|
+
*/
|
|
52
|
+
export declare function CallFocus<T>({ focus, others, getKey, renderTile, className, testId, }: {
|
|
53
|
+
focus: T;
|
|
54
|
+
others: T[];
|
|
55
|
+
getKey(item: T): string;
|
|
56
|
+
renderTile(item: T, size: 'normal' | 'small'): ReactElement;
|
|
57
|
+
className?: string;
|
|
58
|
+
testId?: string;
|
|
59
|
+
}): ReactElement;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* "Who can chat: Panelists only | Everyone" — the moderators' setting, as a group of the call's
|
|
3
|
+
* More menu (`chatPermissionSection` for `MoreMenu`'s `groups`), so it lives with the other call
|
|
4
|
+
* settings rather than as a control of its own.
|
|
5
|
+
*/
|
|
6
|
+
import { type ReactElement } from 'react';
|
|
7
|
+
import type { MoreMenuSection } from '../ControlBar';
|
|
8
|
+
import type { MaybePromise, WebinarChatPermission } from './types';
|
|
9
|
+
export interface ChatPermissionSettingProps {
|
|
10
|
+
value: WebinarChatPermission;
|
|
11
|
+
onChange(value: WebinarChatPermission): MaybePromise;
|
|
12
|
+
/** The app's change in flight (the menu may have closed and reopened since). */
|
|
13
|
+
pending?: boolean;
|
|
14
|
+
}
|
|
15
|
+
/** Menu content: a label and two radio items. Render it inside a `DropdownMenuContent`. */
|
|
16
|
+
export declare function ChatPermissionSetting({ value, onChange, pending, }: ChatPermissionSettingProps): ReactElement;
|
|
17
|
+
/** The setting as a `MoreMenu` group: `groups={[…, chatPermissionSection(props)]}`. */
|
|
18
|
+
export declare function chatPermissionSection(props: ChatPermissionSettingProps): MoreMenuSection;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* "You're on stage": a moderator promoted me, so before my microphone and camera go out I check
|
|
3
|
+
* them — the call's own `PreJoin` (preview, level meter, device pickers) in a dialog (a bottom
|
|
4
|
+
* sheet on a phone). Only the camera video is mirrored in the self-view; no text ever is.
|
|
5
|
+
*/
|
|
6
|
+
import type { ReactElement } from 'react';
|
|
7
|
+
import { type JoinChoices, type MediaKind } from '../join-choices';
|
|
8
|
+
import type { MaybePromise } from './types';
|
|
9
|
+
export interface OnStageCheckProps {
|
|
10
|
+
open: boolean;
|
|
11
|
+
/** Go on stage with these devices. */
|
|
12
|
+
onJoinStage(choices: JoinChoices): MaybePromise;
|
|
13
|
+
/** Decline: step back down (also on Escape / close). */
|
|
14
|
+
onStayInAudience(): MaybePromise;
|
|
15
|
+
/** Busy from outside (the app's request in flight). */
|
|
16
|
+
busy?: boolean;
|
|
17
|
+
/** A failure to show above the buttons. */
|
|
18
|
+
error?: string | null;
|
|
19
|
+
/** Mirror my camera preview (default true; only the <video> flips). */
|
|
20
|
+
mirror?: boolean;
|
|
21
|
+
describeError?(err: unknown, kind: MediaKind): string;
|
|
22
|
+
}
|
|
23
|
+
export declare function OnStageCheck({ open, onJoinStage, onStayInAudience, busy, error, mirror, describeError, }: OnStageCheckProps): ReactElement;
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The webinar's Q&A, the body of a `CallSidePanel` (the attendee viewer's side, the team's Q&A tab):
|
|
3
|
+
* Open / Answered (and, for the team, Dismissed) questions, most upvoted first; an upvote toggle on
|
|
4
|
+
* each (never on my own); status chips ("Answering live", "Answered") and a text answer under the
|
|
5
|
+
* question. The team answers live, answers in text (an inline composer), marks answered,
|
|
6
|
+
* dismisses or reopens. The ask box sits at the bottom, like a chat composer.
|
|
7
|
+
*
|
|
8
|
+
* Askers are named through the app's pill (`asker`); another attendee's question is "Attendee"
|
|
9
|
+
* (the server hides who asked it), so attendees never learn who else attends.
|
|
10
|
+
*/
|
|
11
|
+
import { type ReactElement } from 'react';
|
|
12
|
+
import type { MaybePromise, QuestionStatus, WebinarQuestion } from './types';
|
|
13
|
+
export type QAFilter = 'open' | 'answered' | 'dismissed';
|
|
14
|
+
export interface QAPanelProps {
|
|
15
|
+
questions: WebinarQuestion[];
|
|
16
|
+
/** A team member (host, co-host, panelist): moderates, and sees dismissed questions. */
|
|
17
|
+
team?: boolean;
|
|
18
|
+
/** The ask box's limit (default 500 characters). */
|
|
19
|
+
maxLength?: number;
|
|
20
|
+
/** Hide the ask box (e.g. before the webinar is live). */
|
|
21
|
+
canAsk?: boolean;
|
|
22
|
+
/** Resolve to clear the box; reject to keep the text (the app reports the error). */
|
|
23
|
+
onAsk(body: string): MaybePromise;
|
|
24
|
+
/** `up` = add my vote, else take it back. */
|
|
25
|
+
onVote(question: WebinarQuestion, up: boolean): MaybePromise;
|
|
26
|
+
/** Team: Answer live (`answering`), Mark answered, Dismiss, Reopen (`open`). */
|
|
27
|
+
onStatusChange?(question: WebinarQuestion, status: QuestionStatus): MaybePromise;
|
|
28
|
+
/** Team: a text answer (the server marks the question answered). */
|
|
29
|
+
onAnswer?(question: WebinarQuestion, answer: string): MaybePromise;
|
|
30
|
+
/** Delete one of my own questions while it is still open. */
|
|
31
|
+
onDelete?(question: WebinarQuestion): MaybePromise;
|
|
32
|
+
/** The filter to start on (default Open). */
|
|
33
|
+
defaultFilter?: QAFilter;
|
|
34
|
+
/** "Now" for the asked times (tests); live by default. */
|
|
35
|
+
now?: Date;
|
|
36
|
+
className?: string;
|
|
37
|
+
}
|
|
38
|
+
/** Questions of a filter, in reading order: live first, then most upvoted, then oldest. */
|
|
39
|
+
export declare function sortQuestions(questions: WebinarQuestion[], filter: QAFilter): WebinarQuestion[];
|
|
40
|
+
/**
|
|
41
|
+
* A text box with a counter and a send button; Enter sends, Shift+Enter breaks the line. The ask
|
|
42
|
+
* box and the text answer here, and the webinar's live chat in the same side panel.
|
|
43
|
+
*/
|
|
44
|
+
export declare function PanelComposer({ placeholder, ariaLabel, sendLabel, maxLength, busy, onSend, onCancel, autoFocus, testId, }: {
|
|
45
|
+
placeholder: string;
|
|
46
|
+
ariaLabel: string;
|
|
47
|
+
sendLabel: string;
|
|
48
|
+
maxLength: number;
|
|
49
|
+
busy: boolean;
|
|
50
|
+
/** Resolves to whether it went (then the box clears). */
|
|
51
|
+
onSend(text: string): Promise<boolean>;
|
|
52
|
+
onCancel?(): void;
|
|
53
|
+
autoFocus?: boolean;
|
|
54
|
+
testId: string;
|
|
55
|
+
}): ReactElement;
|
|
56
|
+
export declare function QAPanel({ questions, team, maxLength, canAsk, onAsk, onVote, onStatusChange, onAnswer, onDelete, defaultFilter, now: fixedNow, className, }: QAPanelProps): ReactElement;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/** Raise / lower my hand: one control-bar button, on (solid) while the hand is up. */
|
|
2
|
+
import type { ReactElement } from 'react';
|
|
3
|
+
import type { MaybePromise } from './types';
|
|
4
|
+
export interface RaiseHandButtonProps {
|
|
5
|
+
raised: boolean;
|
|
6
|
+
/** Asked with the new state (`true` = raise). */
|
|
7
|
+
onToggle(raised: boolean): MaybePromise;
|
|
8
|
+
/** Busy from outside (the app's own request in flight). */
|
|
9
|
+
pending?: boolean;
|
|
10
|
+
/** Press-shortcut inside a `CallControlBar` (default H). */
|
|
11
|
+
shortcut?: string;
|
|
12
|
+
className?: string;
|
|
13
|
+
}
|
|
14
|
+
export declare function RaiseHandButton({ raised, onToggle, pending, shortcut, className, }: RaiseHandButtonProps): ReactElement;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The moderators' audience view, the body of a `CallSidePanel` (its Audience tab): the raised
|
|
3
|
+
* hands, oldest first, each with Promote; then everyone in the room with their stage state, Promote
|
|
4
|
+
* or Move to audience. Only moderators get this; an attendee's UI never lists attendees.
|
|
5
|
+
*/
|
|
6
|
+
import { type ReactElement } from 'react';
|
|
7
|
+
import type { MaybePromise, WebinarAttendee, WebinarHand } from './types';
|
|
8
|
+
export interface WebinarAudiencePanelProps {
|
|
9
|
+
/** Raised hands (any order: shown oldest first). */
|
|
10
|
+
hands: WebinarHand[];
|
|
11
|
+
/** Everyone in the room, team included (the app's order). */
|
|
12
|
+
attendees: WebinarAttendee[];
|
|
13
|
+
/** Bring someone on stage. */
|
|
14
|
+
onPromote(id: string): MaybePromise;
|
|
15
|
+
/** Send someone on stage back to the audience (never offered for the host). */
|
|
16
|
+
onDemote(id: string): MaybePromise;
|
|
17
|
+
/** A search field appears above the attendees when there are more than this many (default 8). */
|
|
18
|
+
searchThreshold?: number;
|
|
19
|
+
/** "Now" for the raised times (tests); live by default. */
|
|
20
|
+
now?: Date;
|
|
21
|
+
className?: string;
|
|
22
|
+
}
|
|
23
|
+
export declare function WebinarAudiencePanel({ hands, attendees, onPromote, onDemote, searchThreshold, now: fixedNow, className, }: WebinarAudiencePanelProps): ReactElement;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The attendee's clean viewer: the stage (the app's `CallGrid` / `CallFocus` of the panelists),
|
|
3
|
+
* the live captions under it, a side slot (Q&A in a `CallSidePanel`) docked on a wide screen and
|
|
4
|
+
* over the stage on a narrow one, and a bottom bar with Raise hand, the side toggle and Leave.
|
|
5
|
+
* Audio-first, and no list of other attendees anywhere. (The team keeps the normal call UI.)
|
|
6
|
+
*/
|
|
7
|
+
import type { ReactElement, ReactNode, Ref } from 'react';
|
|
8
|
+
import { type RaiseHandButtonProps } from './RaiseHandButton';
|
|
9
|
+
export interface WebinarViewerProps {
|
|
10
|
+
/** The header (a `CallHeader` with the title and the live chip). */
|
|
11
|
+
header?: ReactNode;
|
|
12
|
+
/** The stage: the visible panelists' tiles. */
|
|
13
|
+
stage: ReactNode;
|
|
14
|
+
stageRef?: Ref<HTMLDivElement>;
|
|
15
|
+
/** Floating over the stage (connection state). */
|
|
16
|
+
stageOverlay?: ReactNode;
|
|
17
|
+
/** Under the stage: a `CaptionsStrip` (or the live transcript). */
|
|
18
|
+
captions?: ReactNode;
|
|
19
|
+
/** The side (a `CallSidePanel` with the `QAPanel`); omit while closed. */
|
|
20
|
+
side?: ReactNode;
|
|
21
|
+
/** The side's toggle in the bar (Q&A); omit for none. */
|
|
22
|
+
sideToggle?: {
|
|
23
|
+
open: boolean;
|
|
24
|
+
onToggle(): void;
|
|
25
|
+
label: string;
|
|
26
|
+
count?: number;
|
|
27
|
+
};
|
|
28
|
+
/** My raised hand. */
|
|
29
|
+
hand: RaiseHandButtonProps;
|
|
30
|
+
onLeave(): void;
|
|
31
|
+
/** The bar's left end (e.g. "Enable audio"). */
|
|
32
|
+
left?: ReactNode;
|
|
33
|
+
/** More centre controls after Raise hand (e.g. a captions switch). */
|
|
34
|
+
controls?: ReactNode;
|
|
35
|
+
/** Single-key shortcuts on (the viewer is on screen). */
|
|
36
|
+
shortcuts?: boolean;
|
|
37
|
+
className?: string;
|
|
38
|
+
}
|
|
39
|
+
export declare function WebinarViewer({ header, stage, stageRef, stageOverlay, captions, side, sideToggle, hand, onLeave, left, controls, shortcuts, className, }: WebinarViewerProps): ReactElement;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The attendee's holding screen: "Starting soon" before the host goes live (the app keeps polling
|
|
3
|
+
* and joins on its own), and "This webinar has ended" after. A `CallStateMessage`, so it reads like
|
|
4
|
+
* every other state of the call stage.
|
|
5
|
+
*/
|
|
6
|
+
import type { ReactElement, ReactNode } from 'react';
|
|
7
|
+
export interface WebinarWaitingProps {
|
|
8
|
+
variant: 'waiting' | 'ended';
|
|
9
|
+
/** The webinar's name. */
|
|
10
|
+
title?: string | null;
|
|
11
|
+
/** When it is scheduled, already formatted by the app ("Today, 15:00"). */
|
|
12
|
+
when?: string | null;
|
|
13
|
+
/** The host as the app shows people (a pill): "Hosted by …". */
|
|
14
|
+
host?: ReactNode;
|
|
15
|
+
/** A way out (back to the app); omitted, no button. */
|
|
16
|
+
onLeave?(): void;
|
|
17
|
+
leaveLabel?: string;
|
|
18
|
+
}
|
|
19
|
+
export declare function WebinarWaiting({ variant, title, when, host, onLeave, leaveLabel, }: WebinarWaitingProps): ReactElement;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/** Small hooks of the webinar blocks. */
|
|
2
|
+
import type { MaybePromise } from './types';
|
|
3
|
+
/**
|
|
4
|
+
* Pending state for handlers that may return a promise: `run(key, fn)` marks `key` busy until the
|
|
5
|
+
* promise settles and resolves to whether it succeeded (a rejection is the app's to report; the
|
|
6
|
+
* block only stops spinning).
|
|
7
|
+
*/
|
|
8
|
+
export declare function usePending(): {
|
|
9
|
+
isPending(key: string): boolean;
|
|
10
|
+
run(key: string, fn: () => MaybePromise): Promise<boolean>;
|
|
11
|
+
};
|
|
12
|
+
/** The current time, refreshed every `ms` (relative times like "2 minutes ago"). */
|
|
13
|
+
export declare function useNow(ms?: number, fixed?: Date): Date;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Webinar blocks: a webinar is an ordinary call with roles, a stage of panelists, backstage → live,
|
|
3
|
+
* raised hands and Q&A. Presentational like the rest of `@arbidocs/blocks/calls`; names are the
|
|
4
|
+
* app's person pills (`entity` / `asker` / `host`).
|
|
5
|
+
*/
|
|
6
|
+
export type { MaybePromise, WebinarRole, WebinarChatPermission, QuestionStatus, WebinarHand, WebinarAttendee, WebinarQuestion, } from './types';
|
|
7
|
+
export { WebinarWaiting, type WebinarWaitingProps } from './WebinarWaiting';
|
|
8
|
+
export { BackstageBanner, WebinarLiveChip, WebinarLiveControl, WebinarEndConfirm, type WebinarLiveControlProps, } from './live';
|
|
9
|
+
export { RaiseHandButton, type RaiseHandButtonProps } from './RaiseHandButton';
|
|
10
|
+
export { WebinarAudiencePanel, type WebinarAudiencePanelProps } from './WebinarAudiencePanel';
|
|
11
|
+
export { QAPanel, PanelComposer, sortQuestions, type QAPanelProps, type QAFilter } from './QAPanel';
|
|
12
|
+
export { ChatPermissionSetting, chatPermissionSection, type ChatPermissionSettingProps, } from './ChatPermissionSetting';
|
|
13
|
+
export { OnStageCheck, type OnStageCheckProps } from './OnStageCheck';
|
|
14
|
+
export { WebinarViewer, type WebinarViewerProps } from './WebinarViewer';
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Backstage → live → ended, for the team. While backstage, `BackstageBanner` (under the header)
|
|
3
|
+
* says so and carries Go live for the moderators; once live, the header shows `WebinarLiveChip` and
|
|
4
|
+
* the control bar `WebinarLiveControl` (End webinar, confirmed). Each action appears once: Go live
|
|
5
|
+
* only in the banner, End only in the bar.
|
|
6
|
+
*/
|
|
7
|
+
import { type ReactElement } from 'react';
|
|
8
|
+
import type { MaybePromise } from './types';
|
|
9
|
+
/** "Live" in the header while the stage is open to the audience. */
|
|
10
|
+
export declare function WebinarLiveChip({ compact, className, }: {
|
|
11
|
+
compact?: boolean;
|
|
12
|
+
className?: string;
|
|
13
|
+
}): ReactElement;
|
|
14
|
+
export interface WebinarLiveControlProps {
|
|
15
|
+
/** The stage is open to the audience. */
|
|
16
|
+
live: boolean;
|
|
17
|
+
/** Moderators only: opens the stage (shown while backstage). */
|
|
18
|
+
onGoLive?(): MaybePromise;
|
|
19
|
+
/** Moderators only: ends the webinar for everyone, after a confirmation (shown while live). */
|
|
20
|
+
onEnd?(): MaybePromise;
|
|
21
|
+
/** Icon only (a narrow bar). */
|
|
22
|
+
compact?: boolean;
|
|
23
|
+
className?: string;
|
|
24
|
+
}
|
|
25
|
+
/** Go live (backstage) or End webinar (live); nothing for someone who may do neither. */
|
|
26
|
+
export declare function WebinarLiveControl({ live, onGoLive, onEnd, compact, className, }: WebinarLiveControlProps): ReactElement | null;
|
|
27
|
+
/**
|
|
28
|
+
* "End the webinar?" — the one confirmation before ending it for everyone, wherever End is offered
|
|
29
|
+
* (`WebinarLiveControl`, or the Leave button's menu in the app's call bar).
|
|
30
|
+
*/
|
|
31
|
+
export declare function WebinarEndConfirm({ open, onOpenChange, onEnd, }: {
|
|
32
|
+
open: boolean;
|
|
33
|
+
onOpenChange(open: boolean): void;
|
|
34
|
+
onEnd(): MaybePromise;
|
|
35
|
+
}): ReactElement;
|
|
36
|
+
/**
|
|
37
|
+
* Under the header while backstage: the team knows the audience cannot see or hear them yet.
|
|
38
|
+
* Moderators get Go live here (`onGoLive`); other panelists read that the host will start it.
|
|
39
|
+
*/
|
|
40
|
+
export declare function BackstageBanner({ onGoLive, className, }: {
|
|
41
|
+
onGoLive?(): MaybePromise;
|
|
42
|
+
className?: string;
|
|
43
|
+
}): ReactElement;
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/** The shapes the webinar blocks render. The app maps its API (`/v1/calls/{call}/webinar…`) onto them. */
|
|
2
|
+
import type { ReactNode } from 'react';
|
|
3
|
+
/** A handler the block may await: while its promise is pending the control shows it is busy. */
|
|
4
|
+
export type MaybePromise = void | Promise<unknown>;
|
|
5
|
+
export type WebinarRole = 'host' | 'cohost' | 'panelist' | 'attendee';
|
|
6
|
+
/** Who may send chat messages: the team only, or the audience too. */
|
|
7
|
+
export type WebinarChatPermission = 'panelists' | 'everyone';
|
|
8
|
+
export type QuestionStatus = 'open' | 'answering' | 'answered' | 'dismissed';
|
|
9
|
+
/** A raised hand in the moderators' queue. */
|
|
10
|
+
export interface WebinarHand {
|
|
11
|
+
id: string;
|
|
12
|
+
name: string;
|
|
13
|
+
/** ISO time the hand went up. */
|
|
14
|
+
raisedAt: string;
|
|
15
|
+
/** The person as the app shows people (a pill); else initials and `name`. */
|
|
16
|
+
entity?: ReactNode;
|
|
17
|
+
}
|
|
18
|
+
/** Someone in the room, as the moderators see the audience. */
|
|
19
|
+
export interface WebinarAttendee {
|
|
20
|
+
id: string;
|
|
21
|
+
name: string;
|
|
22
|
+
role: WebinarRole;
|
|
23
|
+
/** On stage (visible, may publish). */
|
|
24
|
+
onStage: boolean;
|
|
25
|
+
handRaised: boolean;
|
|
26
|
+
/** The person as the app shows people (a pill); else initials and `name`. */
|
|
27
|
+
entity?: ReactNode;
|
|
28
|
+
}
|
|
29
|
+
/** A question of the webinar's Q&A (body and answer already decrypted by the app). */
|
|
30
|
+
export interface WebinarQuestion {
|
|
31
|
+
id: string;
|
|
32
|
+
body: string;
|
|
33
|
+
status: QuestionStatus;
|
|
34
|
+
/** A text answer, shown under the question. */
|
|
35
|
+
answer?: string | null;
|
|
36
|
+
/** Who wrote the text answer (the app's pill). */
|
|
37
|
+
answeredBy?: ReactNode;
|
|
38
|
+
voteCount: number;
|
|
39
|
+
/** I have upvoted it. */
|
|
40
|
+
voted: boolean;
|
|
41
|
+
/** I asked it (I cannot upvote it; I may delete it while it is open). */
|
|
42
|
+
mine: boolean;
|
|
43
|
+
/** ISO time it was asked. */
|
|
44
|
+
createdAt: string;
|
|
45
|
+
/** The asker's name; `null` when hidden from me (another attendee's question): "Attendee". */
|
|
46
|
+
askerName: string | null;
|
|
47
|
+
/** The asker as the app shows people (a pill; it says "(me)" on mine). */
|
|
48
|
+
asker?: ReactNode;
|
|
49
|
+
}
|