@factorialco/f0-react 7.13.0 → 7.15.0

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.
Files changed (35) hide show
  1. package/dist/{ChatPdfThumbnail-CasoTzmS.js → ChatPdfThumbnail-C1UcYl1H.js} +1 -1
  2. package/dist/{ChatPostComposerDialog-CLk4RYiT.js → ChatPostComposerDialog-BaOB3UHy.js} +3 -3
  3. package/dist/{DocumentToolbar-oQ1B9uYt.js → DocumentToolbar-CV9lcCCY.js} +2 -2
  4. package/dist/{DocxViewer-VCoDg5wv.js → DocxViewer-rl6xFuWg.js} +2 -2
  5. package/dist/{F0AiProcessingOverlay-Cv4B3Za1.js → F0AiProcessingOverlay-DJxrxlhM.js} +1385 -1577
  6. package/dist/{F0CanvasPanel-D3xyeygU.js → F0CanvasPanel-Cd27SePP.js} +14097 -13831
  7. package/dist/{F0VideoPlayer-fLkttxV3.js → F0VideoPlayer-sGOAl4BZ.js} +19 -19
  8. package/dist/{MapProvider-CS2D1_u-.js → MapProvider-BJSTrUhF.js} +2 -2
  9. package/dist/{SheetViewer-CPwlJsIf.js → SheetViewer-sSCtCF-k.js} +2 -2
  10. package/dist/{TextViewer-DOCvTpB4.js → TextViewer-J0dGSrMy.js} +2 -2
  11. package/dist/{VolumeMuted-DKLKhxyd.js → VolumeMuted-UfTwt2zn.js} +6 -26
  12. package/dist/ai.d.ts +99 -1
  13. package/dist/ai.js +2 -2
  14. package/dist/component-status.js +2 -2
  15. package/dist/{experimental-BiVfnjJ8.js → experimental-Bp9WMpVV.js} +14390 -11212
  16. package/dist/experimental.d.ts +1303 -7
  17. package/dist/experimental.js +284 -240
  18. package/dist/f0.d.ts +99 -1
  19. package/dist/f0.js +11 -11
  20. package/dist/i18n-provider-defaults.d.ts +85 -0
  21. package/dist/i18n-provider-defaults.js +93 -0
  22. package/dist/{index-ngfMeMZd.js → index--lL16aZU.js} +4 -4
  23. package/dist/index-D37fIjbv.js +4 -0
  24. package/dist/{index-FtDheNrV.js → index-x715W5gJ.js} +2 -2
  25. package/dist/{pdfWorker-lezjTonm.js → pdfWorker-D4wyA0qi.js} +3 -3
  26. package/dist/styles.css +1 -1
  27. package/dist/{tooltip-CMBdJvJA.js → tooltip-BElsa4MF.js} +415 -407
  28. package/icons/app/Floating.d.ts +4 -0
  29. package/icons/app/Floating.d.ts.map +1 -0
  30. package/icons/app/Floating.js +5 -0
  31. package/icons/app/index.d.ts +4 -3
  32. package/icons/app/index.d.ts.map +1 -1
  33. package/icons/app/index.js +4 -3
  34. package/package.json +2 -2
  35. package/dist/index-KQQFyh9M.js +0 -4
@@ -1072,7 +1072,7 @@ declare const ANCHOR_ATTRIBUTE = "data-f0-coachmark";
1072
1072
  */
1073
1073
  export declare const ApplicationFrame: typeof _ApplicationFrame;
1074
1074
 
1075
- declare function _ApplicationFrame({ children, sidebar, banner, ai, aiPromotion, sidePanel, }: ApplicationFrameProps): JSX_2.Element;
1075
+ declare function _ApplicationFrame({ children, sidebar, banner, ai, aiPromotion, sidePanel, meeting, }: ApplicationFrameProps): JSX_2.Element;
1076
1076
 
1077
1077
  export declare interface ApplicationFrameProps {
1078
1078
  ai?: Omit<AiChatProviderProps, "children">;
@@ -1082,6 +1082,16 @@ export declare interface ApplicationFrameProps {
1082
1082
  * behaves exactly as before: present when `ai.enabled`, absent otherwise.
1083
1083
  */
1084
1084
  sidePanel?: ApplicationFrameSidePanelProps;
1085
+ /**
1086
+ * Live meeting. The frame owns the one place a call can render; the host owns
1087
+ * the runtime. `runtime: null` (or omitting the prop) means no meeting.
1088
+ *
1089
+ * In `panel` mode the call IS the side panel's content, like a conversation.
1090
+ * In every other mode its window portals to `document.body`, because a
1091
+ * floating window cannot work under a transformed, overflow-hidden ancestor.
1092
+ * See `F0MeetingSurface`.
1093
+ */
1094
+ meeting?: Omit<F0MeetingProviderProps, "children">;
1085
1095
  banner?: React.ReactNode;
1086
1096
  sidebar: React.ReactNode;
1087
1097
  children: React.ReactNode;
@@ -1160,6 +1170,22 @@ export declare const AUTO_PER_PAGE_MAX = 30;
1160
1170
  */
1161
1171
  export declare const AUTO_PER_PAGE_MIN_RESERVED_ROWS = 10;
1162
1172
 
1173
+ declare type AutoFocusInput = {
1174
+ tiles: F0MeetingTile[];
1175
+ /** What the user asked for, if anything. */
1176
+ intent: F0MeetingFocusIntent;
1177
+ /** Screen shares already auto-focused once, so they are not re-focused. */
1178
+ seenShareKeys: ReadonlySet<string>;
1179
+ };
1180
+
1181
+ declare type AutoFocusResult = {
1182
+ focusKey: string | null;
1183
+ /** Share keys to remember as already auto-focused. */
1184
+ seenShareKeys: ReadonlySet<string>;
1185
+ /** True when the stored intent no longer matches a live tile. */
1186
+ clearIntent: boolean;
1187
+ };
1188
+
1163
1189
  export declare const AutoGrid: ForwardRefExoticComponent<Omit<HTMLAttributes<HTMLDivElement> & VariantProps<(props?: ({
1164
1190
  tileSize?: "lg" | "md" | "sm" | undefined;
1165
1191
  gap?: "0" | "1" | "2" | "3" | "4" | "lg" | "md" | "sm" | "5" | "6" | "7" | "8" | "9" | "10" | "11" | "12" | "14" | "16" | "px" | "xl" | "1.5" | "0.5" | "2.5" | undefined;
@@ -1655,6 +1681,13 @@ declare interface BreakType {
1655
1681
  isPaid: boolean;
1656
1682
  }
1657
1683
 
1684
+ /**
1685
+ * Flattens the roster into tiles, preserving the host's participant order.
1686
+ * Screen shares come first so they are never pushed off the visible page by a
1687
+ * long roster.
1688
+ */
1689
+ export declare const buildTiles: (participants: F0MeetingParticipant[]) => F0MeetingTile[];
1690
+
1658
1691
  /**
1659
1692
  * Represents a bulk action that can be performed on a collection.
1660
1693
  */
@@ -4240,6 +4273,17 @@ declare const counterVariants: (props?: ({
4240
4273
 
4241
4274
  declare type CountryCode = keyof TranslationsType["countries"];
4242
4275
 
4276
+ /**
4277
+ * External store for the volatile per-participant signals. These never go
4278
+ * through React context: with a dozen participants that would re-render the
4279
+ * whole room a hundred times a second. Only the leaf that draws the waveform
4280
+ * subscribes, so a burst of audio events repaints five `<div>`s.
4281
+ *
4282
+ * F0 ships the factory so hosts only have to call the setters from their
4283
+ * transport listeners — nobody should hand-roll subscribe/getSnapshot.
4284
+ */
4285
+ export declare const createMeetingSignalStore: () => MeetingSignalStore;
4286
+
4243
4287
  /**
4244
4288
  * Credits usage data returned by the host app
4245
4289
  */
@@ -5251,8 +5295,10 @@ declare const defaultTranslations: {
5251
5295
  readonly tomorrow: "Tomorrow";
5252
5296
  readonly inProgress: "In progress";
5253
5297
  readonly inProgressTitle: "Call in progress";
5298
+ readonly ringing: "Ringing…";
5254
5299
  readonly summarizing: "Summarizing";
5255
5300
  readonly finished: "Finished";
5301
+ readonly missed: "Missed";
5256
5302
  readonly cancelled: "Cancelled";
5257
5303
  readonly startingNow: "Starting now";
5258
5304
  readonly startsIn: {
@@ -5806,6 +5852,7 @@ declare const defaultTranslations: {
5806
5852
  readonly edit: "Edit";
5807
5853
  readonly editing: "Editing";
5808
5854
  readonly edited: "edited";
5855
+ readonly aiAssisted: "drafted with One";
5809
5856
  readonly cancelEdit: "Cancel edit";
5810
5857
  readonly saveEdit: "Save";
5811
5858
  readonly you: "You";
@@ -5850,6 +5897,12 @@ declare const defaultTranslations: {
5850
5897
  readonly membersWithLast: "{{names}} and {{last}}";
5851
5898
  readonly membersWithMore: "{{names}} and {{count}} more";
5852
5899
  };
5900
+ readonly call: {
5901
+ readonly startedBy: "{{name}} started a huddle";
5902
+ readonly ended: "Huddle ended";
5903
+ readonly missed: "Missed huddle";
5904
+ readonly start: "Start huddle";
5905
+ };
5853
5906
  readonly unreadCount: {
5854
5907
  readonly one: "{{count}} unread";
5855
5908
  readonly other: "{{count}} unread";
@@ -5940,6 +5993,82 @@ declare const defaultTranslations: {
5940
5993
  readonly noData: "No data";
5941
5994
  readonly canceled: "Canceled";
5942
5995
  };
5996
+ readonly meeting: {
5997
+ readonly you: "You";
5998
+ readonly muted: "Muted";
5999
+ readonly waitingToJoin: "Waiting…";
6000
+ readonly sharingScreen: "Sharing screen";
6001
+ readonly chatPanel: "Chat";
6002
+ readonly transcriptPanel: "Transcript";
6003
+ readonly notesPanel: "Notes";
6004
+ readonly openChat: "Open chat";
6005
+ readonly closeChat: "Close chat";
6006
+ readonly closePanel: "Close panel";
6007
+ readonly notesPlaceholder: "Take notes for everyone in the call…";
6008
+ readonly roomChatPlaceholder: "Message this call…";
6009
+ readonly roomChatEmpty: "Messages here are only for this call and disappear when it ends.";
6010
+ readonly sendMessage: "Send";
6011
+ readonly notesReadOnly: "Only the host can edit these notes.";
6012
+ readonly transcriptEmpty: "The transcript will appear here as people speak.";
6013
+ readonly copyLink: "Copy link";
6014
+ readonly linkCopied: "Link copied";
6015
+ readonly handRaised: "Hand raised";
6016
+ readonly morePeople: "{{count}} more";
6017
+ readonly connecting: "Connecting…";
6018
+ readonly reconnecting: "Reconnecting…";
6019
+ readonly connectionLost: "Connection lost";
6020
+ readonly ended: "The meeting has ended";
6021
+ readonly rejoin: "Rejoin";
6022
+ readonly weakConnection: "Weak connection";
6023
+ readonly recording: "Recording";
6024
+ readonly cameraBlocked: "Camera access is blocked. Allow it in your browser settings to turn it on.";
6025
+ readonly microphoneBlocked: "Microphone access is blocked. Allow it in your browser settings to speak.";
6026
+ readonly cameraUnavailable: "No camera found";
6027
+ readonly microphoneUnavailable: "No microphone found";
6028
+ readonly cameraInUse: "Your camera is being used by another app";
6029
+ readonly microphoneInUse: "Your microphone is being used by another app";
6030
+ readonly audioBlocked: "Click to enable sound";
6031
+ readonly turnOnMicrophone: "Turn on microphone";
6032
+ readonly turnOffMicrophone: "Turn off microphone";
6033
+ readonly turnOnCamera: "Turn on camera";
6034
+ readonly turnOffCamera: "Turn off camera";
6035
+ readonly shareScreen: "Share screen";
6036
+ readonly stopSharing: "Stop sharing";
6037
+ readonly raiseHand: "Raise hand";
6038
+ readonly lowerHand: "Lower hand";
6039
+ readonly leave: "Leave";
6040
+ readonly moreActions: "More actions";
6041
+ readonly controls: "Meeting controls";
6042
+ readonly unreadCount: "{{count}} unread";
6043
+ readonly selectMicrophone: "Select microphone";
6044
+ readonly systemDefault: "System default";
6045
+ readonly selectCamera: "Select camera";
6046
+ readonly enterFullscreen: "Expand to fullscreen";
6047
+ readonly exitFullscreen: "Exit fullscreen";
6048
+ readonly meetingWindow: "Meeting window";
6049
+ readonly moveWindow: "Move meeting window";
6050
+ readonly pinParticipant: "Pin";
6051
+ readonly unpinParticipant: "Unpin";
6052
+ readonly modeSwitch: "Meeting position";
6053
+ readonly modeSidePanel: "Show in the side panel";
6054
+ readonly modeFloating: "Show in a floating window";
6055
+ readonly modeFullscreen: "Fill the screen";
6056
+ readonly exitPictureInPicture: "Back to the tab";
6057
+ readonly resizePanel: "Resize the meeting panel";
6058
+ readonly movedToCorner: "Meeting window moved to the {{corner}}";
6059
+ readonly cornerTopLeft: "top left";
6060
+ readonly cornerTopRight: "top right";
6061
+ readonly cornerBottomLeft: "bottom left";
6062
+ readonly cornerBottomRight: "bottom right";
6063
+ readonly participantJoined: "{{name}} joined";
6064
+ readonly participantLeft: "{{name}} left";
6065
+ readonly movedToFullscreen: "Meeting fills the screen";
6066
+ readonly movedToFloating: "Meeting in a floating window";
6067
+ readonly movedToSidePanel: "Meeting in the side panel";
6068
+ readonly movedToMinimized: "Meeting minimized";
6069
+ readonly movedInline: "Meeting on the page";
6070
+ readonly movedToPictureInPicture: "Meeting in a picture-in-picture window";
6071
+ };
5943
6072
  readonly select: {
5944
6073
  readonly noResults: "No results found";
5945
6074
  readonly loadingMore: "Loading...";
@@ -7705,6 +7834,73 @@ export declare type F0ChatAttachment = F0ChatImageAttachment | F0ChatFileAttachm
7705
7834
  * {@link F0ChatRuntime.uploadFiles} cannot tell these apart. */
7706
7835
  export declare type F0ChatAttachSource = "button" | "drop" | "paste";
7707
7836
 
7837
+ declare type F0ChatCall = {
7838
+ id: string;
7839
+ state: F0ChatCallState;
7840
+ /** Who started it. */
7841
+ startedBy: F0ChatUser;
7842
+ /** ISO timestamp the call was started. */
7843
+ startedAt: string;
7844
+ /** ISO timestamp it ended. Drives the duration on `ended`. */
7845
+ endedAt?: string;
7846
+ /**
7847
+ * Who is in the room right now while `ringing` or `live`, and who was in it
7848
+ * once it has `ended`.
7849
+ */
7850
+ participants?: F0ChatUser[];
7851
+ /**
7852
+ * Present only while the call can be joined. Its absence is what removes the
7853
+ * button — the same convention as the rest of the contract: no callback, no
7854
+ * action. A host that omits it on an `ended` call needs no other flag.
7855
+ *
7856
+ * Return the promise and the button spins until it settles. Joining is never
7857
+ * instant — a room has to be asked for first — so a host that drops the
7858
+ * promise leaves the card looking as if the press did nothing.
7859
+ */
7860
+ join?: () => void | Promise<unknown>;
7861
+ /**
7862
+ * What the call was about, shown once it has `ended`. Plain text: the card
7863
+ * renders it whole, and a transcript-sized block does not belong in a row of
7864
+ * a conversation.
7865
+ */
7866
+ summary?: string;
7867
+ /**
7868
+ * Footer buttons on the ended card. The transcript is the reason this exists
7869
+ * — it is long, and where it opens (a drawer, a modal, a page) is the host's
7870
+ * decision, not the card's.
7871
+ */
7872
+ actions?: {
7873
+ label: string;
7874
+ icon?: IconType;
7875
+ onClick: () => void;
7876
+ }[];
7877
+ };
7878
+
7879
+ /**
7880
+ * A call in the transcript, rendered as a card with a Join button. Like a system
7881
+ * message it has no author, no reactions and no replies — but unlike one it is
7882
+ * INTERACTIVE and it MUTATES: the same id carries the call from ringing to
7883
+ * ended, so the history keeps one line per call.
7884
+ *
7885
+ * factorial → a Stream message whose custom fields carry the call state, patched
7886
+ * in place from LiveKit's webhooks (see `F0Meeting/SPEC.md`).
7887
+ */
7888
+ declare type F0ChatCallMessage = {
7889
+ type: "call";
7890
+ id: string;
7891
+ /** ISO timestamp — participates in day separators and ordering. */
7892
+ createdAt: string;
7893
+ call: F0ChatCall;
7894
+ };
7895
+
7896
+ /**
7897
+ * Where a call is in its life. One call is ONE transcript item that moves
7898
+ * through these — not a live card plus a log line afterwards.
7899
+ */
7900
+ declare type F0ChatCallState = (typeof f0ChatCallStates)[number];
7901
+
7902
+ declare const f0ChatCallStates: readonly ["ringing", "live", "ended", "missed"];
7903
+
7708
7904
  /**
7709
7905
  * Per-channel permissions, one per verb. Everything is optional and defaults to
7710
7906
  * today's behavior, so hosts only express what their transport restricts
@@ -8111,8 +8307,14 @@ export declare type F0ChatHeaderAction = {
8111
8307
  label: string;
8112
8308
  icon?: IconType;
8113
8309
  /** The host decides what happens: call a runtime method (togglePin,
8114
- * toggleMute), open its own modal, navigate… */
8115
- onClick: (channel: F0ChatChannel) => void;
8310
+ * toggleMute), open its own modal, navigate…
8311
+ *
8312
+ * An `inline` action that returns a promise spins until it settles, and
8313
+ * refuses further presses while it does. Actions that only toggle something
8314
+ * local return nothing and never spin; the ones worth returning are those
8315
+ * that go to a server before anything visibly happens — starting a huddle
8316
+ * being the case this was built for. */
8317
+ onClick: (channel: F0ChatChannel) => void | Promise<unknown>;
8116
8318
  /** Where the action renders: inside the ellipsis overflow menu (default) or
8117
8319
  * as its own icon button next to it. Inline requires `icon` — an inline
8118
8320
  * action without one falls back to the menu. */
@@ -8146,7 +8348,7 @@ export declare type F0ChatImageAttachment = {
8146
8348
  };
8147
8349
 
8148
8350
  /** Anything that can appear in the transcript, oldest → newest. */
8149
- export declare type F0ChatItem = F0ChatMessage | F0ChatSystemMessage | F0ChatPost;
8351
+ export declare type F0ChatItem = F0ChatMessage | F0ChatSystemMessage | F0ChatPost | F0ChatCallMessage;
8150
8352
 
8151
8353
  /**
8152
8354
  * Open Graph preview of a URL in the body (WhatsApp-style card above the text).
@@ -8262,6 +8464,14 @@ export declare type F0ChatMessage = {
8262
8464
  * reactions/read updates), so it never false-positives the label.
8263
8465
  */
8264
8466
  editedAt?: string;
8467
+ /**
8468
+ * The message was drafted with One (Factorial's AI) and approved by its
8469
+ * author, who remains the sender. Drives the muted "drafted with One" label
8470
+ * after the body, next to the edited marker, so the reader and assistive
8471
+ * technology learn it on every surface that shows the content. The host maps
8472
+ * it from server-set message metadata, never from body text.
8473
+ */
8474
+ aiAssisted?: boolean;
8265
8475
  };
8266
8476
 
8267
8477
  export declare type F0ChatMessageReply = {
@@ -8508,6 +8718,14 @@ export declare type F0ChatProps = {
8508
8718
  * the array per render with the current label/icon.
8509
8719
  */
8510
8720
  headerActions?: F0ChatHeaderAction[] | ((channel: F0ChatChannel) => F0ChatHeaderAction[]);
8721
+ /**
8722
+ * Replaces the built-in header. `null` removes it entirely, for hosts that
8723
+ * already frame the chat with their own chrome — the in-call panel puts its
8724
+ * Chat / Transcript / Notes tabs where this header would be.
8725
+ *
8726
+ * Undefined keeps the default header, so no existing usage changes.
8727
+ */
8728
+ header?: ReactNode | null;
8511
8729
  };
8512
8730
 
8513
8731
  /**
@@ -10026,6 +10244,109 @@ export declare interface F0MapViewport {
10026
10244
  zoom?: number;
10027
10245
  }
10028
10246
 
10247
+ /**
10248
+ * Makes a meeting available to the app and renders its surface.
10249
+ *
10250
+ * `runtime: null` means there is no call: no window exists and the meeting hooks
10251
+ * throw, exactly as they do outside a provider. This mirrors `F0AiChatProvider`
10252
+ * — the host owns the transport, F0 owns the UI.
10253
+ *
10254
+ * The providers render whether or not there is a call, and ONLY the surface is
10255
+ * conditional. That is load-bearing: `children` here is the host's entire
10256
+ * application, and if it moved between the root and three providers deep as a
10257
+ * call starts, React would reconcile the two shapes as different elements and
10258
+ * remount the whole app — losing all component state and, for a host that
10259
+ * fetches on mount, re-issuing every request on screen.
10260
+ *
10261
+ * `children` cannot simply sit outside the providers instead: in `panel` mode
10262
+ * the room renders as side-panel content, which the host mounts from within its
10263
+ * own tree, so it has to be able to read the meeting from here.
10264
+ */
10265
+ export declare const F0Meeting: ({ runtime, actions, actionOrder, defaultMode, pictureInPicture, sidePanel, headerContent, overlay, children, }: F0MeetingProviderProps) => ReactNode;
10266
+
10267
+ export declare type F0MeetingAction = {
10268
+ /**
10269
+ * Reusing a core id (see {@link f0MeetingCoreActionIds}) PATCHES the
10270
+ * synthesized action instead of adding a new one, so a host can restyle the
10271
+ * mic button without reimplementing it.
10272
+ */
10273
+ id: string;
10274
+ /** Already localized. For toggles the host rebuilds it per render. */
10275
+ label: string;
10276
+ icon: IconType;
10277
+ /** Shown while `pressed` — mic → MicrophoneNegative. */
10278
+ activeIcon?: IconType;
10279
+ /** Toggle semantics: renders `aria-pressed` and the active treatment. */
10280
+ pressed?: boolean;
10281
+ onClick?: () => void;
10282
+ disabled?: boolean;
10283
+ /** Why it is disabled. Without this, a disabled button is a dead end. */
10284
+ disabledReason?: string;
10285
+ /** An operation is in flight: spinner, no double-fire. */
10286
+ pending?: boolean;
10287
+ variant?: "default" | "critical";
10288
+ /** Collapse priority; higher survives longer. */
10289
+ priority?: number;
10290
+ /** Never collapses into the overflow menu. */
10291
+ pinned?: boolean;
10292
+ /** Actions sharing a group render adjacent, separated by a hairline. */
10293
+ group?: string;
10294
+ badge?: number | "dot";
10295
+ /** Restrict to some surface modes. Omit for all. */
10296
+ modes?: F0MeetingSurfaceMode[];
10297
+ /**
10298
+ * Shortcut hint for the tooltip. F0 renders it but does NOT bind it — global
10299
+ * key binding belongs to the host, which owns the rest of the app.
10300
+ */
10301
+ shortcut?: string;
10302
+ /** Removes a synthesized core action entirely. */
10303
+ hidden?: boolean;
10304
+ };
10305
+
10306
+ export declare type F0MeetingActionInput = F0MeetingAction | F0MeetingActionPatch;
10307
+
10308
+ /**
10309
+ * A partial override of a synthesized core action. Patching only needs the id,
10310
+ * so relabelling the mic button does not mean restating its icon, priority and
10311
+ * pinning — which would be both noise and a chance to get them wrong.
10312
+ */
10313
+ export declare type F0MeetingActionPatch = Partial<Omit<F0MeetingAction, "id">> & {
10314
+ id: F0MeetingCoreActionId;
10315
+ };
10316
+
10317
+ export declare type F0MeetingActionsProp = F0MeetingActionInput[] | ((runtime: F0MeetingRuntime) => F0MeetingActionInput[]);
10318
+
10319
+ /**
10320
+ * Binds a media track to a DOM element that F0 owns. F0 calls `attach` in a
10321
+ * layout effect and the returned cleanup on unmount / track change.
10322
+ *
10323
+ * factorial → LiveKit: `(el) => { track.attach(el); return () => track.detach(el) }`.
10324
+ * Going through the transport's own attach (instead of assigning `srcObject`)
10325
+ * is what keeps adaptive stream and dynacast working: LiveKit observes the
10326
+ * attached element's size and visibility to pick the simulcast layer and to
10327
+ * pause off-screen tracks.
10328
+ */
10329
+ export declare type F0MeetingBinding = (element: HTMLMediaElement) => () => void;
10330
+
10331
+ /**
10332
+ * Per-room permissions, defaulting to allowed so hosts only express what their
10333
+ * token or role restricts.
10334
+ *
10335
+ * Capabilities are PERMISSIONS; the optional actions on the runtime are FEATURE
10336
+ * SUPPORT. Omitting `startRecording` means "this deployment has no recording";
10337
+ * `canRecord: false` means "recording exists but not for you".
10338
+ */
10339
+ export declare type F0MeetingCapabilities = {
10340
+ canPublishAudio?: boolean;
10341
+ canPublishVideo?: boolean;
10342
+ canShareScreen?: boolean;
10343
+ canRecord?: boolean;
10344
+ canMuteOthers?: boolean;
10345
+ canRemoveParticipants?: boolean;
10346
+ /** Per-participant moderation gate (e.g. you cannot remove another host). */
10347
+ canModerate?: (participant: F0MeetingParticipant) => boolean;
10348
+ };
10349
+
10029
10350
  /**
10030
10351
  * @experimental This is an experimental component, use it at your own risk.
10031
10352
  */
@@ -10097,6 +10418,506 @@ export declare interface F0MeetingCardProps extends WithDataTestIdProps, DataAtt
10097
10418
  secondaryActions?: CardSecondaryAction[];
10098
10419
  }
10099
10420
 
10421
+ export declare const f0MeetingConnectionQualities: readonly ["excellent", "good", "poor", "lost"];
10422
+
10423
+ export declare type F0MeetingConnectionQuality = (typeof f0MeetingConnectionQualities)[number];
10424
+
10425
+ export declare type F0MeetingCoreActionId = (typeof f0MeetingCoreActionIds)[number];
10426
+
10427
+ export declare const f0MeetingCoreActionIds: readonly ["core:microphone", "core:microphoneSettings", "core:camera", "core:cameraSettings", "core:screenShare", "core:raiseHand", "core:chat", "core:leave"];
10428
+
10429
+ export declare type F0MeetingDevice = {
10430
+ /** MediaDeviceInfo.deviceId. */
10431
+ id: string;
10432
+ label: string;
10433
+ /** Marks the OS default entry so the UI can render "System default". */
10434
+ isDefault?: boolean;
10435
+ };
10436
+
10437
+ /** Why the room ended — drives the copy of the end state. */
10438
+ export declare type F0MeetingDisconnectReason = (typeof f0MeetingDisconnectReasons)[number];
10439
+
10440
+ export declare const f0MeetingDisconnectReasons: readonly ["user-left", "removed", "room-closed", "duplicate-identity", "server-shutdown", "connection-lost", "unknown"];
10441
+
10442
+ /**
10443
+ * Which tile is in the spotlight, as an INTENT rather than a key.
10444
+ *
10445
+ * A plain `string | null` cannot tell "nobody has pinned anything" apart from
10446
+ * "the user dismissed the spotlight", so in a one-to-one the auto rule kept
10447
+ * re-applying itself and the pin button did nothing. The three states make
10448
+ * dismissal expressible.
10449
+ */
10450
+ export declare type F0MeetingFocusIntent = {
10451
+ type: "auto";
10452
+ } | {
10453
+ type: "pinned";
10454
+ key: string;
10455
+ } | {
10456
+ type: "none";
10457
+ };
10458
+
10459
+ /**
10460
+ * The local participant's media, split from the roster because it changes on a
10461
+ * different cadence and drives the control bar directly.
10462
+ */
10463
+ export declare type F0MeetingLocalMedia = {
10464
+ microphone: F0MeetingLocalSource;
10465
+ camera: F0MeetingLocalSource;
10466
+ /** Omit when the host cannot share (mobile web, restricted tenants). */
10467
+ screenShare?: F0MeetingLocalSource;
10468
+ /**
10469
+ * Browsers block autoplay of remote audio until a user gesture. When true,
10470
+ * F0 renders an affordance wired to `unlockAudio`.
10471
+ * factorial → LiveKit `room.canPlaybackAudio === false`, resolved by
10472
+ * `room.startAudio()`.
10473
+ */
10474
+ audioBlocked?: boolean;
10475
+ unlockAudio?: () => void | Promise<void>;
10476
+ };
10477
+
10478
+ /** State of one local media source. */
10479
+ export declare type F0MeetingLocalSource = {
10480
+ enabled: boolean;
10481
+ /** A toggle is in flight. Renders the pending state and blocks double-clicks. */
10482
+ pending?: boolean;
10483
+ permission?: F0MeetingPermission;
10484
+ /** Devices for the inline picker. Omit to hide the chevron entirely. */
10485
+ devices?: F0MeetingDevice[];
10486
+ selectedDeviceId?: string;
10487
+ selectDevice?: (deviceId: string) => void | Promise<void>;
10488
+ };
10489
+
10490
+ export declare type F0MeetingParticipant = {
10491
+ id: string;
10492
+ /** Display name, already resolved by the host. LiveKit `Participant.name`. */
10493
+ name: string;
10494
+ avatar?: AvatarVariant;
10495
+ isLocal: boolean;
10496
+ /**
10497
+ * Moderator flag — drives moderation affordances. Not a transport concept:
10498
+ * the host derives it from token grants or `participant.metadata`.
10499
+ */
10500
+ isHost?: boolean;
10501
+ /**
10502
+ * A bot rather than a person (AI notetaker, transcription bot). Renders with
10503
+ * a distinct treatment and never counts toward "N people in the call".
10504
+ */
10505
+ isAgent?: boolean;
10506
+ /** Joined but has not published anything yet. */
10507
+ isConnecting?: boolean;
10508
+ /**
10509
+ * Whether this person is actually in the room. Absent means `joined`, so a
10510
+ * host that only ever reports real participants needs no change.
10511
+ *
10512
+ * `invited` is someone the call is waiting for: they hold a tile that reads
10513
+ * "Waiting…", publish nothing, and do NOT count toward "N people in the
10514
+ * call". LiveKit does not know them — they come from the host's own list of
10515
+ * attendees, which is why this cannot be derived from the transport.
10516
+ */
10517
+ presence?: F0MeetingPresence;
10518
+ tracks: F0MeetingTrack[];
10519
+ /**
10520
+ * ISO timestamp the hand was raised, so "who raised first" has an order.
10521
+ * factorial → LiveKit participant attributes or a data-channel message.
10522
+ */
10523
+ raisedHandAt?: string;
10524
+ /** Free-form host label shown in the tile chip. F0 never parses it. */
10525
+ subtitle?: string;
10526
+ /**
10527
+ * Never crop this person's video: it is letterboxed inside its tile instead
10528
+ * of filling it.
10529
+ *
10530
+ * Tiles take the shape of their cell and crop the sides to fill it, which is
10531
+ * safe for a centred face and NOT safe for someone signing — it takes their
10532
+ * hands with it. Meet exposes the same escape hatch as "Show my full video to
10533
+ * others". The person owns the preference; F0 only honours it.
10534
+ */
10535
+ preventCrop?: boolean;
10536
+ };
10537
+
10538
+ /**
10539
+ * Media permission as the browser reports it. Modelling it explicitly is what
10540
+ * lets the UI explain a black tile instead of just showing one.
10541
+ *
10542
+ * factorial → the DOMException name from `getUserMedia`: `NotAllowedError` →
10543
+ * "denied", `NotFoundError` → "unavailable", `NotReadableError` → "in-use".
10544
+ */
10545
+ export declare type F0MeetingPermission = (typeof f0MeetingPermissions)[number];
10546
+
10547
+ export declare const f0MeetingPermissions: readonly ["granted", "prompt", "denied", "unavailable", "in-use"];
10548
+
10549
+ export declare type F0MeetingPresence = (typeof f0MeetingPresences)[number];
10550
+
10551
+ /**
10552
+ * A person in the room. `id` is the transport identity — the SAME string the
10553
+ * transport uses, so F0 never cross-references two id spaces internally. The
10554
+ * domain lookup (identity → employee name/avatar) belongs to the adapter.
10555
+ *
10556
+ * factorial → LiveKit `Participant.identity` (the employee id).
10557
+ */
10558
+ export declare const f0MeetingPresences: readonly ["invited", "joined"];
10559
+
10560
+ /**
10561
+ * Makes a {@link F0MeetingRuntime} available to the meeting UI. The host owns
10562
+ * the runtime (a mock in stories, a LiveKit adapter in factorial); F0 only
10563
+ * reads it and never imports the transport.
10564
+ */
10565
+ export declare const F0MeetingProvider: ({ runtime, children, }: {
10566
+ /**
10567
+ * `null` while there is no call. The provider still renders, so `children`
10568
+ * keep their position in the tree — see {@link F0Meeting}. Every hook below
10569
+ * throws on a null context, which keeps the contract intact: you may only read
10570
+ * a meeting from inside one.
10571
+ */
10572
+ runtime: F0MeetingRuntime | null;
10573
+ children: ReactNode;
10574
+ }) => ReactNode;
10575
+
10576
+ export declare type F0MeetingProviderProps = {
10577
+ /** `null` when there is no active meeting: nothing mounts. */
10578
+ runtime: F0MeetingRuntime | null;
10579
+ /** Host actions, merged by id with the synthesized core ones. */
10580
+ actions?: F0MeetingActionsProp;
10581
+ /** Explicit left-to-right order by action id. Unlisted ids keep their slot. */
10582
+ actionOrder?: string[];
10583
+ /** Surface mode when a meeting first appears. */
10584
+ defaultMode?: F0MeetingSurfaceDestination;
10585
+ /**
10586
+ * Whether the browser may move the call into its picture-in-picture window
10587
+ * when the user leaves the tab. On by default. There is no button for it,
10588
+ * and nothing happens on a browser without either PiP API.
10589
+ */
10590
+ pictureInPicture?: boolean;
10591
+ /**
10592
+ * The tabs of the in-call side panel (chat, transcript, notes). F0 renders
10593
+ * the bar and owns whether the panel is open, because the button that opens
10594
+ * it lives in F0's own control bar — a bare `ReactNode` could not supply the
10595
+ * tab labels or the unread badge that button needs.
10596
+ */
10597
+ sidePanel?: F0MeetingSidePanel;
10598
+ /** Host content rendered in the window header, before the mode controls. */
10599
+ headerContent?: React.ReactNode;
10600
+ /** Host content rendered above the whole room (banners, upsells). */
10601
+ overlay?: React.ReactNode;
10602
+ children?: React.ReactNode;
10603
+ };
10604
+
10605
+ export declare type F0MeetingReaction = {
10606
+ id: string;
10607
+ participantId: string;
10608
+ emoji: string;
10609
+ at: string;
10610
+ };
10611
+
10612
+ export declare type F0MeetingRecording = {
10613
+ active: boolean;
10614
+ startedBy?: F0MeetingParticipant;
10615
+ startedAt?: string;
10616
+ /** Legal copy is host-provided and already localized. */
10617
+ consentNotice?: string;
10618
+ };
10619
+
10620
+ /**
10621
+ * The room itself: grid, controls and state chrome. It knows nothing about the
10622
+ * window that hosts it — the surface owns position, size and mode, exactly as
10623
+ * `F0Chat` takes its window controls as callbacks.
10624
+ */
10625
+ export declare const F0MeetingRoom: ({ actions, actionOrder, sidePanel, overlay, }: F0MeetingRoomProps) => JSX_2.Element;
10626
+
10627
+ export declare type F0MeetingRoomInfo = {
10628
+ id: string;
10629
+ /** Shown in the header and in the minimized pill. */
10630
+ title: string;
10631
+ avatar?: AvatarVariant;
10632
+ /**
10633
+ * ISO timestamp the call started — drives the elapsed timer. LiveKit exposes
10634
+ * no room start time, so the adapter uses its own backend value and falls
10635
+ * back to local join time.
10636
+ */
10637
+ startedAt?: string;
10638
+ /** Where this meeting came from, so the header can link back to it. */
10639
+ origin?: {
10640
+ label: string;
10641
+ onNavigate: () => void;
10642
+ };
10643
+ };
10644
+
10645
+ export declare type F0MeetingRoomProps = {
10646
+ actions?: F0MeetingActionsProp;
10647
+ actionOrder?: string[];
10648
+ sidePanel?: F0MeetingSidePanel;
10649
+ overlay?: React.ReactNode;
10650
+ };
10651
+
10652
+ /** The roster slice: changes on join/leave/publish, not on every packet. */
10653
+ export declare type F0MeetingRoster = {
10654
+ room: F0MeetingRoomInfo;
10655
+ status: F0MeetingStatus;
10656
+ participants: F0MeetingParticipant[];
10657
+ localMedia: F0MeetingLocalMedia;
10658
+ recording?: F0MeetingRecording;
10659
+ signals?: F0MeetingSignalStore;
10660
+ };
10661
+
10662
+ /**
10663
+ * The data and actions a host provides to drive the meeting UI. F0 is headless:
10664
+ * it never touches the transport. A mock runtime powers Storybook; factorial
10665
+ * implements this against LiveKit.
10666
+ *
10667
+ * As in {@link F0ChatRuntime}, optional callbacks are CAPABILITY DETECTION:
10668
+ * omitting `setScreenShareEnabled` removes the screen-share button entirely.
10669
+ */
10670
+ export declare type F0MeetingRuntime = {
10671
+ room: F0MeetingRoomInfo;
10672
+ status: F0MeetingStatus;
10673
+ /** Only meaningful with `status: "disconnected"`. */
10674
+ disconnectReason?: F0MeetingDisconnectReason;
10675
+ /** Human-readable failure, already localized. `status: "error"` only. */
10676
+ errorMessage?: string;
10677
+ localParticipantId: string;
10678
+ /**
10679
+ * Everyone in the room INCLUDING the local participant, in a stable order the
10680
+ * host controls (join order). F0 owns speaker promotion and pagination on top
10681
+ * of this — a host that reorders on every speaker change makes the grid churn.
10682
+ */
10683
+ participants: F0MeetingParticipant[];
10684
+ localMedia: F0MeetingLocalMedia;
10685
+ /**
10686
+ * Volatile per-participant signals. Omit and F0 degrades gracefully to muted
10687
+ * flags with no waveform and no quality bars, which is what tests and simple
10688
+ * hosts want.
10689
+ */
10690
+ signals?: F0MeetingSignalStore;
10691
+ /**
10692
+ * Leave the room. F0 never calls this implicitly — not on unmount, not on
10693
+ * Escape, not on navigation. Hanging up must be an explicit user act.
10694
+ */
10695
+ leave: () => void | Promise<void>;
10696
+ setMicrophoneEnabled: (enabled: boolean) => void | Promise<void>;
10697
+ setCameraEnabled: (enabled: boolean) => void | Promise<void>;
10698
+ setScreenShareEnabled?: (enabled: boolean) => void | Promise<void>;
10699
+ startRecording?: () => void | Promise<void>;
10700
+ stopRecording?: () => void | Promise<void>;
10701
+ recording?: F0MeetingRecording;
10702
+ setHandRaised?: (raised: boolean) => void | Promise<void>;
10703
+ sendReaction?: (emoji: string) => void | Promise<void>;
10704
+ /** Transient reactions to animate; the host prunes them after a few seconds. */
10705
+ reactions?: F0MeetingReaction[];
10706
+ muteParticipant?: (participantId: string) => void | Promise<void>;
10707
+ removeParticipant?: (participantId: string) => void | Promise<void>;
10708
+ /**
10709
+ * Live transcription. Omit and the panel has no Transcript tab at all —
10710
+ * the usual capability-by-presence rule.
10711
+ */
10712
+ transcript?: F0MeetingTranscriptSegment[];
10713
+ /** Shared notes for the room. Present without `setNotes` means read-only. */
10714
+ notes?: string;
10715
+ setNotes?: (value: string) => void;
10716
+ /** Retry after a failed connect — wires the Rejoin action. */
10717
+ reconnect?: () => void | Promise<void>;
10718
+ capabilities?: F0MeetingCapabilities;
10719
+ };
10720
+
10721
+ export declare type F0MeetingSidePanel = {
10722
+ tabs: F0MeetingSidePanelTab[];
10723
+ defaultTabId?: string;
10724
+ };
10725
+
10726
+ /**
10727
+ * A tab in the room's side panel. F0 owns the bar, the selection and the close
10728
+ * button; the host owns what is inside each tab.
10729
+ */
10730
+ export declare type F0MeetingSidePanelTab = {
10731
+ id: string;
10732
+ label: string;
10733
+ content: React.ReactNode;
10734
+ /** Unread count on the tab and on the control-bar button that opens it. */
10735
+ badge?: number;
10736
+ };
10737
+
10738
+ /**
10739
+ * Per-participant volatile state. Read through `useSyncExternalStore`, never
10740
+ * through context: with a dozen participants LiveKit emits these at 10–20 Hz,
10741
+ * which would re-render the whole tree a hundred times a second.
10742
+ */
10743
+ export declare type F0MeetingSignal = {
10744
+ /** 0..1, smoothed by the host. LiveKit `Participant.audioLevel`. */
10745
+ audioLevel: number;
10746
+ isSpeaking: boolean;
10747
+ quality: F0MeetingConnectionQuality;
10748
+ };
10749
+
10750
+ /**
10751
+ * Minimal external-store protocol. F0 exports `createMeetingSignalStore` so
10752
+ * hosts never implement subscribe/getSnapshot themselves — the adapter just
10753
+ * calls the setters from its transport listeners.
10754
+ */
10755
+ export declare type F0MeetingSignalStore = {
10756
+ subscribe: (participantId: string, listener: () => void) => () => void;
10757
+ /**
10758
+ * MUST return the same object identity while nothing changed, or
10759
+ * `useSyncExternalStore` loops forever.
10760
+ */
10761
+ getSnapshot: (participantId: string) => F0MeetingSignal;
10762
+ subscribeSpeakers: (listener: () => void) => () => void;
10763
+ /** Identity changes only when the SET changes, so the grid doesn't churn. */
10764
+ getSpeakers: () => readonly string[];
10765
+ };
10766
+
10767
+ /**
10768
+ * Reserves space for the meeting on a dedicated page.
10769
+ *
10770
+ * It renders nothing: it is an empty box that publishes its viewport rect, and
10771
+ * the window — which stays in its portal — animates to that rect. Teleporting
10772
+ * by measurement instead of reparenting is what keeps the `<video>` elements
10773
+ * mounted across navigation, and it makes the fullscreen ↔ inline ↔ floating
10774
+ * transition a single animation of x/y/width/height.
10775
+ */
10776
+ export declare const F0MeetingSlot: () => JSX_2.Element;
10777
+
10778
+ /**
10779
+ * The slow-moving slice, behind a SEPARATE context with a stable value. LiveKit
10780
+ * emits audio levels, speaking changes and quality updates at 10–20 Hz per
10781
+ * participant; anything mounted per-tile that read the full runtime would
10782
+ * re-render on every one of them, defeating the tile memoization.
10783
+ *
10784
+ * The action callbacks are identity-stable delegates into the latest runtime.
10785
+ */
10786
+ export declare type F0MeetingStable = {
10787
+ roomId: string;
10788
+ localParticipantId: string;
10789
+ capabilities?: F0MeetingCapabilities;
10790
+ /**
10791
+ * Presence flags, not the callbacks themselves: optional runtime actions gate
10792
+ * UI affordances by PRESENCE, so the memo tracks presence, not identity.
10793
+ */
10794
+ hasScreenShare: boolean;
10795
+ hasRecording: boolean;
10796
+ hasReactions: boolean;
10797
+ hasRaiseHand: boolean;
10798
+ hasModeration: boolean;
10799
+ hasReconnect: boolean;
10800
+ setMicrophoneEnabled: (enabled: boolean) => void;
10801
+ setCameraEnabled: (enabled: boolean) => void;
10802
+ setScreenShareEnabled: (enabled: boolean) => void;
10803
+ setHandRaised: (raised: boolean) => void;
10804
+ sendReaction: (emoji: string) => void;
10805
+ muteParticipant: (participantId: string) => void;
10806
+ removeParticipant: (participantId: string) => void;
10807
+ reconnect: () => void;
10808
+ leave: () => void;
10809
+ };
10810
+
10811
+ /**
10812
+ * Room lifecycle. `reconnecting` shows a non-blocking banner and FREEZES the
10813
+ * last video frame instead of clearing tiles — clearing on a two-second ICE
10814
+ * restart is the worst thing a call UI can do.
10815
+ *
10816
+ * factorial → LiveKit `Room.state` plus the `Disconnected` event's reason.
10817
+ */
10818
+ export declare type F0MeetingStatus = (typeof f0MeetingStatuses)[number];
10819
+
10820
+ export declare const f0MeetingStatuses: readonly ["idle", "connecting", "connected", "reconnecting", "disconnected", "error"];
10821
+
10822
+ /**
10823
+ * Mounts the room into a portal on `document.body`, once, for the whole life of
10824
+ * the meeting — for every mode EXCEPT `panel` and `pip`.
10825
+ *
10826
+ * It lives outside the application frame on purpose. The frame's root is
10827
+ * `overflow-hidden` and contains transformed elements, and a transformed
10828
+ * ancestor breaks `position: fixed` for everything inside it — which is exactly
10829
+ * what a free-floating window needs. Portalling also keeps the frame's own
10830
+ * z-index map untouched: the surface only claims the 40–49 band at body level,
10831
+ * deliberately below Radix dialogs so a modal can still cover the call.
10832
+ *
10833
+ * `panel` is the one mode with no window at all: there the room is the side
10834
+ * panel's content (see `MeetingPanelContent`), so it needs no chrome of its own
10835
+ * and no rect — the panel supplies both. `pip` has a window, but it is the
10836
+ * browser's: the room portals into that window's document (see `PipFrame`).
10837
+ *
10838
+ * The body-level portal element stays mounted regardless, because two things
10839
+ * must never leave the main document: the live region announcing the call, and
10840
+ * the `<audio>` elements — a media element moved to another document stops
10841
+ * playing, and remounting one in the middle of a sentence is a dropped word.
10842
+ */
10843
+ export declare const F0MeetingSurface: () => JSX_2.Element | null;
10844
+
10845
+ export declare type F0MeetingSurfaceDestination = (typeof f0MeetingSurfaceDestinations)[number];
10846
+
10847
+ /** The places a call can be sent to — by the user, or by `defaultMode`. */
10848
+ export declare const f0MeetingSurfaceDestinations: readonly ["fullscreen", "panel", "floating", "minimized", "inline"];
10849
+
10850
+ export declare type F0MeetingSurfaceMode = (typeof f0MeetingSurfaceModes)[number];
10851
+
10852
+ /**
10853
+ * Everything the surface can render as. `pip` is the one mode nobody chooses:
10854
+ * the browser moves the call into its picture-in-picture window when the tab
10855
+ * is left, whatever mode it was in, and closing that window returns to it.
10856
+ * Never persisted, never a `defaultMode`, never a button.
10857
+ */
10858
+ export declare const f0MeetingSurfaceModes: readonly ["fullscreen", "panel", "floating", "minimized", "inline", "pip"];
10859
+
10860
+ /**
10861
+ * A rendered cell. One per published camera (with a placeholder when the camera
10862
+ * is off) plus one per screen share, so a participant who is sharing occupies
10863
+ * two tiles — the same behaviour as every other call product.
10864
+ */
10865
+ export declare type F0MeetingTile = {
10866
+ /** `${participantId}:${kind}` — stable across re-solves. */
10867
+ key: string;
10868
+ participant: F0MeetingParticipant;
10869
+ kind: Extract<F0MeetingTrackKind, "camera" | "screenShare">;
10870
+ /** Undefined when nothing is published: the tile renders the avatar. */
10871
+ track?: F0MeetingTrack;
10872
+ };
10873
+
10874
+ export declare type F0MeetingTrack = {
10875
+ /** Stable publication id. factorial → LiveKit `TrackPublication.trackSid`. */
10876
+ id: string;
10877
+ kind: F0MeetingTrackKind;
10878
+ /**
10879
+ * Identity key for the binding. F0 re-runs attach ONLY when this changes —
10880
+ * never on `binding`'s identity, which hosts rebuild on every transport
10881
+ * event. Republishing the same source (camera restart, device switch) MUST
10882
+ * change it. factorial → `${trackSid}:${track.mediaStreamID}`.
10883
+ */
10884
+ bindingKey: string;
10885
+ binding?: F0MeetingBinding;
10886
+ /** Muted at the source. factorial → LiveKit `TrackPublication.isMuted`. */
10887
+ muted: boolean;
10888
+ /** Subscribed and flowing. factorial → `isSubscribed && !isMuted`. */
10889
+ live: boolean;
10890
+ /**
10891
+ * Intrinsic dimensions, when known — lets the tile letterbox before the
10892
+ * first frame lands. Screen shares are rarely 16:9, so forcing them into it
10893
+ * is visible. factorial → LiveKit `TrackPublication.dimensions`.
10894
+ */
10895
+ width?: number;
10896
+ height?: number;
10897
+ };
10898
+
10899
+ export declare type F0MeetingTrackKind = (typeof f0MeetingTrackKinds)[number];
10900
+
10901
+ export declare const f0MeetingTrackKinds: readonly ["camera", "microphone", "screenShare", "screenShareAudio"];
10902
+
10903
+ /**
10904
+ * One line of live transcription.
10905
+ *
10906
+ * Modelled on LiveKit's own shape so the adapter is a rename: segments arrive
10907
+ * on the `lk.transcription` text-stream topic, and a non-final segment is
10908
+ * REPLACED by its final version under the same id rather than appended.
10909
+ * (`RoomEvent.TranscriptionReceived` is deprecated; do not wire it.)
10910
+ */
10911
+ export declare type F0MeetingTranscriptSegment = {
10912
+ /** LiveKit `lk.segment_id` — shared between the interim and final versions. */
10913
+ id: string;
10914
+ participantId: string;
10915
+ text: string;
10916
+ at: string;
10917
+ /** LiveKit `lk.transcription_final`. Interim lines render as provisional. */
10918
+ isFinal: boolean;
10919
+ };
10920
+
10100
10921
  declare type F0Message = {
10101
10922
  id: string;
10102
10923
  role: "user" | "assistant" | "system" | "tool";
@@ -10320,6 +11141,13 @@ export declare type F0ProgressSeriesSize = (typeof f0ProgressSeriesSizes)[number
10320
11141
 
10321
11142
  export declare const f0ProgressSeriesSizes: readonly ["sm", "md", "lg"];
10322
11143
 
11144
+ export declare type F0Rect = {
11145
+ x: number;
11146
+ y: number;
11147
+ width: number;
11148
+ height: number;
11149
+ };
11150
+
10323
11151
  /**
10324
11152
  * Header for a resource detail page: avatar, title, description, status,
10325
11153
  * metadata and its primary, secondary and overflow actions.
@@ -10913,6 +11741,29 @@ export declare interface F0VersionHistoryProps {
10913
11741
  activeVersionId?: "current" | (string & {});
10914
11742
  }
10915
11743
 
11744
+ export declare type F0WindowCorner = (typeof f0WindowCorners)[number];
11745
+
11746
+ export declare const f0WindowCorners: readonly ["tl", "tr", "bl", "br"];
11747
+
11748
+ /**
11749
+ * Anchored placement. Storing absolute coordinates is the classic mistake:
11750
+ * shrinking the browser leaves the window off-screen. Anchoring to a corner
11751
+ * keeps it where the user expects across viewport changes.
11752
+ *
11753
+ * It describes the FLOATING window only. `panel` mode stores nothing here: the
11754
+ * room is the side panel's content there, so its width is the panel's and is
11755
+ * persisted by the panel — which is also why docking and undocking returns the
11756
+ * exact floating size the user had chosen.
11757
+ */
11758
+ export declare type F0WindowPlacement = {
11759
+ corner: F0WindowCorner;
11760
+ /** Distance from the anchored corner, in px, always positive. */
11761
+ dx: number;
11762
+ dy: number;
11763
+ width: number;
11764
+ height: number;
11765
+ };
11766
+
10916
11767
  /**
10917
11768
  * @deprecated Renamed to `F0SearchInput` to match the F0 input vocabulary
10918
11769
  * (any component where the user types in a box has the `Input` suffix and
@@ -11566,6 +12417,55 @@ declare type GraphVisualizationSettings = {
11566
12417
  hidden?: string[];
11567
12418
  };
11568
12419
 
12420
+ /** One row of the grid. Rows may hold different counts, so also different widths. */
12421
+ declare type GridRow = {
12422
+ count: number;
12423
+ tileWidth: number;
12424
+ tileHeight: number;
12425
+ };
12426
+
12427
+ export declare type GridSolution = {
12428
+ rows: number;
12429
+ /** Widest row's tile count. */
12430
+ cols: number;
12431
+ /** The NARROWEST row's tile — the worst case, which is what limits are about. */
12432
+ tileWidth: number;
12433
+ tileHeight: number;
12434
+ /** Tiles that fit. `count - visibleCount` go to the overflow chip. */
12435
+ visibleCount: number;
12436
+ rowSpecs: GridRow[];
12437
+ /** Σ tile areas: how much of the container the layout actually covers. */
12438
+ totalArea: number;
12439
+ };
12440
+
12441
+ export declare type GridSolverInput = {
12442
+ count: number;
12443
+ width: number;
12444
+ height: number;
12445
+ gap: number;
12446
+ /**
12447
+ * How far a tile may stray from `preferredAspect` to fill its cell. Inside
12448
+ * this range the tile simply IS the cell, so the grid leaves no dead space.
12449
+ */
12450
+ minAspect: number;
12451
+ maxAspect: number;
12452
+ /** Only breaks ties between layouts of equal quality. */
12453
+ preferredAspect: number;
12454
+ /** Below this a tile stops being useful; the solver overflows instead. */
12455
+ minTileWidth: number;
12456
+ /**
12457
+ * The same floor for height. Optional, and 0 means "no floor" — a caller with
12458
+ * only a width in mind gets exactly the old behaviour.
12459
+ */
12460
+ minTileHeight?: number;
12461
+ /**
12462
+ * How much smaller than the best possible worst tile a layout may make its
12463
+ * smallest tile to compete on total coverage. `1` (the default) is pure
12464
+ * maximin; `COVERAGE_TOLERANCE` is what the room uses.
12465
+ */
12466
+ coverageTolerance?: number;
12467
+ };
12468
+
11569
12469
  /**
11570
12470
  * Symbol used to identify the groupId in the data
11571
12471
  */
@@ -12666,6 +13566,19 @@ export declare type lastIntentType = {
12666
13566
  /** Sentinel for {@link F0ChatRuntime.loadMessageContext} meaning "the live tail". */
12667
13567
  export declare const LATEST: "latest";
12668
13568
 
13569
+ /**
13570
+ * Places the solved tiles. Rows sit in equal-height slots that span the
13571
+ * container, and each row centres its own tiles inside its slot — so a row
13572
+ * that the aspect clamp shrank is centred rather than pinned to a corner.
13573
+ *
13574
+ * Returns absolute rects so the caller can animate x/y/width/height directly —
13575
+ * no `scale`, which is what would warp the video mid-flight.
13576
+ */
13577
+ export declare const layoutGrid: (solution: GridSolution, box: {
13578
+ width: number;
13579
+ height: number;
13580
+ }, gap: number) => F0Rect[];
13581
+
12669
13582
  declare type Level = (typeof levels)[number];
12670
13583
 
12671
13584
  declare const levels: readonly ["info", "warning", "critical", "positive"];
@@ -12988,8 +13901,15 @@ export declare type MeetingAttendee = {
12988
13901
  };
12989
13902
 
12990
13903
  export declare interface MeetingJoin {
12991
- /** Called when the Join button is pressed. */
12992
- onJoin?: () => void;
13904
+ /**
13905
+ * Called when the Join button is pressed.
13906
+ *
13907
+ * Return the promise and the button shows a spinner until it settles, which
13908
+ * is what joining actually needs: a room has to be asked for before there is
13909
+ * anything to walk into, and without this the card sits there looking as if
13910
+ * the press did nothing.
13911
+ */
13912
+ onJoin?: () => void | Promise<unknown>;
12993
13913
  /** Navigates to the meeting room instead of handling the click. */
12994
13914
  href?: string;
12995
13915
  /**
@@ -13006,6 +13926,93 @@ export declare interface MeetingJoin {
13006
13926
  label?: string;
13007
13927
  }
13008
13928
 
13929
+ /**
13930
+ * Shared notes for the room.
13931
+ *
13932
+ * Uncontrolled-looking but fully controlled: the value comes from the host, so
13933
+ * whatever it syncs the notes through — a data channel, a document, a poll —
13934
+ * arrives here as a re-render rather than as a second source of truth.
13935
+ */
13936
+ export declare const MeetingNotes: ({ value, onChange }: MeetingNotesProps) => JSX_2.Element;
13937
+
13938
+ declare type MeetingNotesProps = {
13939
+ value: string;
13940
+ /** Absent means read-only: the notes exist but this person cannot edit them. */
13941
+ onChange?: (value: string) => void;
13942
+ };
13943
+
13944
+ /**
13945
+ * The room AS THE SIDE PANEL'S CONTENT — not a window that covers it.
13946
+ *
13947
+ * Everything `FloatingWindow` would draw is deliberately absent: the card, the
13948
+ * border, the shadow, the rect, the drag handles and the resize seam all belong
13949
+ * to the panel, which already has one of each. What is left is the header row
13950
+ * and the room, filling whatever space the panel gives them.
13951
+ *
13952
+ * It takes no props ON PURPOSE. The element is handed to `present()` and stored
13953
+ * in the panel's state, so it must be referentially stable — a fresh element on
13954
+ * every render would re-present in a loop. Everything the chrome needs comes
13955
+ * from {@link useMeetingChrome} instead.
13956
+ *
13957
+ * ## Why it renders nothing when there is no call
13958
+ *
13959
+ * That same `present()` is what makes this OUTLIVE the call. The element lives
13960
+ * in the side panel's state, in the host's tree, so it is not removed with the
13961
+ * meeting surface — it is taken out by `MeetingPanelPresenter`'s effect cleanup,
13962
+ * a whole commit after the runtime became null. There is therefore exactly one
13963
+ * render where this is on screen with no call behind it, and reading the meeting
13964
+ * there used to throw and take the page down on hanging up.
13965
+ */
13966
+ export declare const MeetingPanelContent: () => ReactNode;
13967
+
13968
+ /**
13969
+ * The call's own chat, for the room's side panel.
13970
+ *
13971
+ * Deliberately plainer than `F0Chat`: this transport has no reactions, no
13972
+ * threads, no read receipts and no history — in production it is LiveKit's data
13973
+ * channel, which delivers a string and forgets it. Rendering it with the full
13974
+ * conversation surface would promise all of those.
13975
+ */
13976
+ export declare const MeetingRoomChat: ({ messages, onSend }: MeetingRoomChatProps) => JSX_2.Element;
13977
+
13978
+ declare type MeetingRoomChatMessage = {
13979
+ id: string;
13980
+ participantId: string;
13981
+ text: string;
13982
+ at: string;
13983
+ };
13984
+
13985
+ declare type MeetingRoomChatProps = {
13986
+ messages: MeetingRoomChatMessage[];
13987
+ onSend: (text: string) => void;
13988
+ };
13989
+
13990
+ /**
13991
+ * The in-call panel: chat, transcript and notes beside the grid.
13992
+ *
13993
+ * F0 owns the bar, the selection and the close button because the control that
13994
+ * opens the panel lives in F0's own action bar and needs the tab labels and
13995
+ * unread counts. What goes inside each tab is entirely the host's.
13996
+ *
13997
+ * Only one tab is mounted at a time. That is deliberate for the chat tab in
13998
+ * particular: its transcript is virtualized, and measuring rows inside a
13999
+ * display:none subtree yields zero heights that the list then has to correct
14000
+ * on reveal.
14001
+ */
14002
+ export declare const MeetingSidePanel: ({ panel }: MeetingSidePanelProps) => JSX_2.Element | null;
14003
+
14004
+ declare type MeetingSidePanelProps = {
14005
+ panel: F0MeetingSidePanel;
14006
+ };
14007
+
14008
+ export declare type MeetingSignalStore = F0MeetingSignalStore & {
14009
+ setAudioLevel: (participantId: string, level: number) => void;
14010
+ setSpeaking: (participantIds: readonly string[]) => void;
14011
+ setQuality: (participantId: string, quality: F0MeetingConnectionQuality) => void;
14012
+ remove: (participantId: string) => void;
14013
+ reset: () => void;
14014
+ };
14015
+
13009
14016
  export declare type MeetingState = (typeof meetingStates)[number];
13010
14017
 
13011
14018
  /**
@@ -13015,7 +14022,102 @@ export declare type MeetingState = (typeof meetingStates)[number];
13015
14022
  * the elapsed label, whether Join is actionable) is computed from `startsAt` and
13016
14023
  * `now`.
13017
14024
  */
13018
- export declare const meetingStates: readonly ["scheduled", "inProgress", "summarizing", "finished", "cancelled"];
14025
+ export declare const meetingStates: readonly ["ringing", "scheduled", "inProgress", "summarizing", "finished", "missed", "cancelled"];
14026
+
14027
+ declare type MeetingSurfaceContextValue = {
14028
+ /** What the user chose. Never mutated by the environment. */
14029
+ mode: F0MeetingSurfaceDestination;
14030
+ /**
14031
+ * What actually renders. A small viewport or an unmounted inline slot derives
14032
+ * a different mode without destroying the user's preference; so does an open
14033
+ * picture-in-picture window, which is `pip` for as long as it is open.
14034
+ */
14035
+ effectiveMode: F0MeetingSurfaceMode;
14036
+ setMode: (mode: F0MeetingSurfaceDestination) => void;
14037
+ /** Absolute rect the window animates to, in viewport coordinates. */
14038
+ rect: F0Rect;
14039
+ isDragging: boolean;
14040
+ setIsDragging: (dragging: boolean) => void;
14041
+ /** Commit an absolute rect after a drag (re-anchors and snaps). */
14042
+ settleRect: (rect: F0Rect) => void;
14043
+ /** Commit a resize, keeping the current anchor. */
14044
+ resizeRect: (rect: F0Rect) => void;
14045
+ /** True below the md breakpoint: no dragging, pill instead of window. */
14046
+ isCompactViewport: boolean;
14047
+ /**
14048
+ * The frame's content area. `panel` mode does NOT use it — there the room is
14049
+ * the side panel's content and the panel places itself. It is what a window
14050
+ * sizes itself against when it has to sit BESIDE the panel (see
14051
+ * `fitToContent`), and the viewport when no frame published one.
14052
+ */
14053
+ panelArea: F0Rect;
14054
+ /**
14055
+ * Whether there is a side panel for the call to be the content of.
14056
+ *
14057
+ * Registered by the application frame, the same way `F0MeetingSlot`
14058
+ * registers a rect for `inline`. A call rendered on its own — a story, a
14059
+ * test, a host with no frame — has nowhere to dock, so `panel` derives to
14060
+ * `floating` and the switch stops offering it.
14061
+ */
14062
+ hasPanelSlot: boolean;
14063
+ setPanelSlot: (available: boolean) => void;
14064
+ /** Registered by `F0MeetingSlot` so `inline` knows where to fly to. */
14065
+ setInlineRect: (rect: F0Rect | null) => void;
14066
+ /**
14067
+ * The application frame's content region, published by the frame itself. The
14068
+ * side panel lives inside it — between the navigation and the content —
14069
+ * rather than on top of the whole viewport.
14070
+ */
14071
+ setFrameRect: (rect: F0Rect | null) => void;
14072
+ /** What the user asked the spotlight to do, if anything. */
14073
+ focusIntent: F0MeetingFocusIntent;
14074
+ setFocusIntent: (intent: F0MeetingFocusIntent) => void;
14075
+ /** Whether the in-call side panel is open, and on which tab. */
14076
+ isSidePanelOpen: boolean;
14077
+ setSidePanelOpen: (open: boolean) => void;
14078
+ activeTabId: string | null;
14079
+ setActiveTabId: (id: string) => void;
14080
+ announce: (message: string) => void;
14081
+ liveMessage: string;
14082
+ /** What the browser offers for picture-in-picture, after the host's opt-out. */
14083
+ pipSupport: PipSupport;
14084
+ /** The Document PiP window, while the room renders inside it. */
14085
+ pipWindow: Window | null;
14086
+ /**
14087
+ * Move the call into the browser's picture-in-picture. Deliberately not a
14088
+ * destination of `setMode`: the only caller is the browser itself, from the
14089
+ * Media Session `enterpictureinpicture` action when the tab is left — so the
14090
+ * call follows you out of the tab the way Meet's does, and never by a button.
14091
+ */
14092
+ enterPictureInPicture: () => void;
14093
+ exitPictureInPicture: () => void;
14094
+ /**
14095
+ * Every live `<video>` by tile key, kept by `TileVideo`, so video PiP has an
14096
+ * element to hand to the browser.
14097
+ */
14098
+ registerVideo: (key: string, element: HTMLVideoElement | null) => void;
14099
+ };
14100
+
14101
+ export declare const MeetingSurfaceProvider: ({ defaultMode, roomId, pictureInPicture, children, }: {
14102
+ defaultMode?: F0MeetingSurfaceDestination;
14103
+ /** Scopes the remembered mode. A different meeting starts at `defaultMode`. */
14104
+ roomId?: string;
14105
+ pictureInPicture?: boolean;
14106
+ children: ReactNode;
14107
+ }) => ReactNode;
14108
+
14109
+ /**
14110
+ * Live transcription, newest at the bottom.
14111
+ *
14112
+ * Interim segments render at reduced emphasis and are REPLACED by their final
14113
+ * version under the same id — appending both is what turns a transcript into
14114
+ * the same sentence three times, each a word longer.
14115
+ */
14116
+ export declare const MeetingTranscript: ({ segments }: MeetingTranscriptProps) => JSX_2.Element;
14117
+
14118
+ declare type MeetingTranscriptProps = {
14119
+ segments: F0MeetingTranscriptSegment[];
14120
+ };
13019
14121
 
13020
14122
  export declare type MentionedUser = {
13021
14123
  id: string | number;
@@ -13345,6 +14447,9 @@ export declare type NavigationTarget = {
13345
14447
 
13346
14448
  declare type NavTarget = HTMLAttributeAnchorTarget;
13347
14449
 
14450
+ /** The quadrant the rect's centre falls into. */
14451
+ export declare const nearestCorner: (rect: F0Rect, viewport: Viewport) => F0WindowCorner;
14452
+
13348
14453
  /**
13349
14454
  * Utility type to extract all possible paths from nested object.
13350
14455
  * Generates hyphenated paths from nested object structure
@@ -14429,7 +15534,13 @@ declare interface PieComputation {
14429
15534
  limit?: number;
14430
15535
  }
14431
15536
 
15537
+ declare type PipSupport = "document" | "video" | "none";
15538
+
15539
+ /** Re-expresses an absolute rect against a given corner. */
15540
+ export declare const placementFromRect: (rect: F0Rect, corner: F0WindowCorner, viewport: Viewport) => F0WindowPlacement;
15541
+
14432
15542
  declare type PopoverContentProps = React_2.ComponentPropsWithoutRef<typeof PopoverPrimitive.Content> & {
15543
+ /** Portal target. Defaults to the nearest `PortalContainerProvider`. */
14433
15544
  container?: HTMLElement | null;
14434
15545
  };
14435
15546
 
@@ -14856,6 +15967,16 @@ declare type RelaxedNumericWithFormatter = Omit<NumericWithFormatter, "numericVa
14856
15967
 
14857
15968
  declare type RendererDefinition = ValueDisplayRendererDefinition;
14858
15969
 
15970
+ /**
15971
+ * Promotes off-page speakers into the visible page, replacing the tile that has
15972
+ * been quiet the longest rather than the first one — swapping into slot 0 makes
15973
+ * the grid jump on every turn.
15974
+ *
15975
+ * The hold window stops two people alternating from shuffling tiles on every
15976
+ * exchange, which is the single most distracting thing a video grid can do.
15977
+ */
15978
+ export declare const reorderForSpeakers: ({ tiles, speakerIds, pageSize, lastSpokenAt, now, holdMs, }: SpeakerOrderInput) => F0MeetingTile[];
15979
+
14859
15980
  export declare interface ReplaceBlockNotesTextEditorPageDocumentPatch {
14860
15981
  type: "replace_block";
14861
15982
  targetId: string;
@@ -14882,6 +16003,19 @@ declare type RequisitionProfile = {
14882
16003
  };
14883
16004
  };
14884
16005
 
16006
+ /**
16007
+ * Decides which tile is spotlighted. Pure, so every branch is a unit test.
16008
+ *
16009
+ * Priority: the user's pin, then a screen share we have not focused yet, then
16010
+ * the remote participant in a one-to-one.
16011
+ *
16012
+ * The `none` intent is what makes the pin button work in a one-to-one. With a
16013
+ * plain nullable key, dismissing the spotlight fell straight back into the
16014
+ * two-camera rule below and re-focused the same person in the same render, so
16015
+ * the control did nothing at all.
16016
+ */
16017
+ export declare const resolveAutoFocus: ({ tiles, intent, seenShareKeys, }: AutoFocusInput) => AutoFocusResult;
16018
+
14885
16019
  export declare type ResolvedRecordType<R> = R extends RecordType ? R : RecordType;
14886
16020
 
14887
16021
  /**
@@ -14891,6 +16025,14 @@ export declare type ResolvedRecordType<R> = R extends RecordType ? R : RecordTyp
14891
16025
  */
14892
16026
  export declare const resolveGranularityDefinition: (key: NavigationGranularityKey) => GranularityDefinition;
14893
16027
 
16028
+ /**
16029
+ * Resolves an anchored placement into an absolute rect, clamped into the
16030
+ * current viewport. Anchoring to a corner (rather than storing x/y) is what
16031
+ * keeps the window sensible when the browser is resized: it stays glued to
16032
+ * "its" corner instead of drifting off-screen.
16033
+ */
16034
+ export declare const resolvePlacement: (placement: F0WindowPlacement, viewport: Viewport, minWidth?: number, minHeight?: number) => F0Rect;
16035
+
14894
16036
  /** A renderer entry in its full form — a bare function is just its `render`. */
14895
16037
  export declare const resolveSlotRenderer: (entry: SlotRendererEntry | undefined) => {
14896
16038
  render: SlotRenderer;
@@ -15184,6 +16326,12 @@ export declare const selectSizes: readonly ["sm", "md"];
15184
16326
 
15185
16327
  export declare const selectVariants: readonly ["field", "inline"];
15186
16328
 
16329
+ /**
16330
+ * Placement after a drag: always re-anchored to the nearest corner, and pulled
16331
+ * flush to it when the window was dropped close enough.
16332
+ */
16333
+ export declare const settlePlacement: (rect: F0Rect, viewport: Viewport) => F0WindowPlacement;
16334
+
15187
16335
  /**
15188
16336
  * @experimental This is an experimental component use it at your own risk
15189
16337
  */
@@ -15958,6 +17106,30 @@ export declare type SlotWidgetProps = HomeWidgetChrome & {
15958
17106
  ctx?: HomeRenderCtx;
15959
17107
  };
15960
17108
 
17109
+ /**
17110
+ * Picks the row split for the most people the floors admit, each row filling
17111
+ * the container's width on its own.
17112
+ *
17113
+ * Two ideas do the work. A tile takes the shape of its own cell within
17114
+ * `[minAspect, maxAspect]`, so inside that range the tile IS the cell and the
17115
+ * block covers the container instead of letterboxing a fixed 16:9 box in every
17116
+ * cell. And rows are sized independently, so an incomplete last row spreads its
17117
+ * tiles over the full width rather than leaving the missing cells as a hole —
17118
+ * three people fill the room, they do not sit next to an empty square.
17119
+ *
17120
+ * Selection is coverage within a tolerance of maximin — see `pick`.
17121
+ *
17122
+ * Pure, so it is tested without rendering.
17123
+ */
17124
+ export declare const solveGrid: (input: GridSolverInput) => GridSolution;
17125
+
17126
+ /**
17127
+ * Spotlight plus a thumbnail strip. The strip is a PROPORTION of the container
17128
+ * rather than a fixed height: the previous implementation hard-coded 125px,
17129
+ * which is most of a small floating window.
17130
+ */
17131
+ export declare const solveSpotlight: ({ stripCount, width, height, gap, stripRange, spotlightRange, }: SpotlightSolverInput) => SpotlightSolution;
17132
+
15961
17133
  /**
15962
17134
  * Type helper to extract keys from a SortingsDefinition
15963
17135
  */
@@ -15982,6 +17154,20 @@ export declare type SortingsStateMultiple = {
15982
17154
 
15983
17155
  export declare type SortOrder = "asc" | "desc";
15984
17156
 
17157
+ declare type SpeakerOrderInput = {
17158
+ tiles: F0MeetingTile[];
17159
+ /** Participants currently speaking. */
17160
+ speakerIds: readonly string[];
17161
+ /** How many tiles the grid can show at once. */
17162
+ pageSize: number;
17163
+ /** participantId → epoch ms of their last turn. */
17164
+ lastSpokenAt: Readonly<Record<string, number>>;
17165
+ /** Now, injected so the function stays pure and testable. */
17166
+ now: number;
17167
+ /** A visible tile is only displaced after being quiet this long. */
17168
+ holdMs: number;
17169
+ };
17170
+
15985
17171
  /**
15986
17172
  * @experimental This is an experimental component use it at your own risk
15987
17173
  */
@@ -16033,6 +17219,43 @@ class?: never;
16033
17219
  className?: ClassValue;
16034
17220
  })) | undefined) => string> & RefAttributes<HTMLDivElement>, "ref"> & RefAttributes<HTMLElement | SVGElement>>;
16035
17221
 
17222
+ export declare type SpotlightSolution = {
17223
+ spotlight: F0Rect;
17224
+ strip: F0Rect[];
17225
+ /** Thumbnails that did not fit, for the "+N" chip. */
17226
+ stripOverflow: number;
17227
+ stripSide: "bottom" | "right" | "none";
17228
+ };
17229
+
17230
+ export declare type SpotlightSolverInput = {
17231
+ /** Participants that are NOT the spotlight. */
17232
+ stripCount: number;
17233
+ width: number;
17234
+ height: number;
17235
+ gap: number;
17236
+ /**
17237
+ * How far a thumbnail may stray to take the shape of its own slot. Uniform
17238
+ * across the strip, but derived from the container rather than fixed: a wide
17239
+ * room gets nearly square thumbnails, a side panel portrait ones.
17240
+ */
17241
+ stripRange: {
17242
+ min: number;
17243
+ max: number;
17244
+ };
17245
+ /**
17246
+ * Cameras pass a range: the spotlight takes the shape of its box, clamped, so
17247
+ * it crops the sides rather than leaving most of a tall panel empty.
17248
+ *
17249
+ * Screen shares omit it and the spotlight takes the WHOLE box, letterboxing
17250
+ * the presentation inside its own tile. That is what puts the black exactly
17251
+ * where the picture is missing instead of darkening the whole room.
17252
+ */
17253
+ spotlightRange?: {
17254
+ min: number;
17255
+ max: number;
17256
+ };
17257
+ };
17258
+
16036
17259
  export declare const Stack: ForwardRefExoticComponent<Omit<HTMLAttributes<HTMLDivElement> & VariantProps<(props?: ({
16037
17260
  overflow?: "hidden" | "auto" | undefined;
16038
17261
  paddingX?: "none" | "p-4" | "p-2" | "p-8" | "p-12" | "p-16" | undefined;
@@ -16598,6 +17821,11 @@ declare type TextCellConfig = {
16598
17821
  /** The HTML-ish input type of a text cell. Drives a default leading icon. */
16599
17822
  declare type TextCellInputType = "text" | "email" | "url" | "tel";
16600
17823
 
17824
+ /** Ratio to lay a tile out with: screen shares keep their intrinsic one. */
17825
+ export declare const tileAspectRatio: (tile: F0MeetingTile, fallback: number) => number;
17826
+
17827
+ export declare const tileKey: (participantId: string, kind: string) => string;
17828
+
16601
17829
  /** A button rendered in the footer at the bottom of the table of contents */
16602
17830
  export declare type TOCAction = {
16603
17831
  label: string;
@@ -17122,6 +18350,26 @@ declare interface UseExportActionProps<R extends RecordType, Filters extends Fil
17122
18350
  /** Read the chat runtime. Throws when used outside an {@link F0ChatProvider}. */
17123
18351
  export declare function useF0Chat(): F0ChatRuntime;
17124
18352
 
18353
+ export declare const useF0Meeting: () => F0MeetingRuntime;
18354
+
18355
+ export declare const useF0MeetingRoster: () => F0MeetingRoster;
18356
+
18357
+ export declare const useF0MeetingStable: () => F0MeetingStable;
18358
+
18359
+ /**
18360
+ * Whether there is a call at all.
18361
+ *
18362
+ * The providers render with or without one (see {@link F0Meeting}), so being
18363
+ * inside them does NOT imply a live runtime and the three hooks below still
18364
+ * throw. Ask this first from anywhere that can outlive the call.
18365
+ *
18366
+ * Which is not a rare corner: anything the HOST mounts — panel content, a slot,
18367
+ * its own chrome — is removed by an effect, a commit after the runtime went
18368
+ * null. That one render is enough to take the page down, and it is exactly what
18369
+ * hanging up out of `panel` mode used to do.
18370
+ */
18371
+ export declare const useHasF0Meeting: () => boolean;
18372
+
17125
18373
  export declare const useInfiniteScrollPagination: (paginationInfo: PaginationInfo | null, isLoading: boolean, isLoadingMore: boolean, loadMore: () => void) => {
17126
18374
  loadingIndicatorRef: RefObject<HTMLTableCellElement>;
17127
18375
  };
@@ -17133,6 +18381,34 @@ export declare const useInfiniteScrollPagination: (paginationInfo: PaginationInf
17133
18381
  */
17134
18382
  export declare const useIsBulkChange: (count: number, threshold?: number) => boolean;
17135
18383
 
18384
+ /**
18385
+ * Subscribes to one participant's volatile signals. Call it as deep in the tree
18386
+ * as possible — ideally in the leaf that draws the waveform — so an audio burst
18387
+ * repaints a handful of bars instead of the whole tile.
18388
+ *
18389
+ * With a `selector` the component re-renders only when the SELECTED value
18390
+ * changes: the quality bars can watch `quality` and sit out every audio level.
18391
+ * `isEqual` defaults to `Object.is`, which is all a primitive needs.
18392
+ *
18393
+ * When the host provides no signal store the hook returns a frozen default, so
18394
+ * the room degrades to muted flags with no waveform instead of breaking.
18395
+ */
18396
+ export declare function useMeetingSignal(participantId: string): F0MeetingSignal;
18397
+
18398
+ export declare function useMeetingSignal<T>(participantId: string, selector: (signal: F0MeetingSignal) => T, isEqual?: (previous: T, next: T) => boolean): T;
18399
+
18400
+ /** The set of currently speaking participants, for the layout's ordering. */
18401
+ export declare const useMeetingSpeakers: () => readonly string[];
18402
+
18403
+ export declare const useMeetingSurface: () => MeetingSurfaceContextValue;
18404
+
18405
+ /**
18406
+ * The same state, for consumers that render whether or not a call exists — the
18407
+ * application frame reads this to decide how much width to reserve, and there
18408
+ * is no provider at all when `runtime` is null.
18409
+ */
18410
+ export declare const useMeetingSurfaceOptional: () => MeetingSurfaceContextValue | null;
18411
+
17136
18412
  /**
17137
18413
  * Converts an item-navigation result into the `NavigationProps` shape that
17138
18414
  * `PageHeader` and `F0Dialog` accept (directly or via
@@ -17204,6 +18480,18 @@ export declare const useSidebarChats: () => SidebarChatStore;
17204
18480
  */
17205
18481
  export declare function useSidePanel(): SidePanelContextValue;
17206
18482
 
18483
+ /**
18484
+ * Attaches a track to a media element F0 owns.
18485
+ *
18486
+ * The effect depends on `bindingKey` and on WHETHER there is a binding — never
18487
+ * on the function's identity. Hosts rebuild the runtime, and with it every
18488
+ * `binding` closure, on each transport event, so depending on identity would
18489
+ * detach and re-attach the element around twenty times a second, black-flashing
18490
+ * the video continuously. The presence flag is what lets a binding that arrives
18491
+ * a commit after its key (a stream that opened late) still get attached.
18492
+ */
18493
+ export declare const useTrackBinding: <T extends HTMLMediaElement>(track: F0MeetingTrack | undefined) => React.RefObject<T>;
18494
+
17207
18495
  export declare const useWidgetIsWide: () => boolean;
17208
18496
 
17209
18497
  /**
@@ -17351,6 +18639,11 @@ declare interface VideoPlayerContent {
17351
18639
  descriptions?: Localized<string>;
17352
18640
  }
17353
18641
 
18642
+ declare type Viewport = {
18643
+ width: number;
18644
+ height: number;
18645
+ };
18646
+
17354
18647
  /**
17355
18648
  * Region of the canvas (in screen px) covered by external chrome — typically a
17356
18649
  * side panel / drawer opened over the graph. All fly-to paths shift their target
@@ -17369,6 +18662,9 @@ export declare interface ViewportInset {
17369
18662
  left?: number;
17370
18663
  }
17371
18664
 
18665
+ /** The whole viewport as a rect, for hosts that publish no content area. */
18666
+ export declare const viewportRect: (viewport: Viewport) => F0Rect;
18667
+
17372
18668
  /**
17373
18669
  * @experimental This is an experimental component use it at your own risk
17374
18670
  */