@ai-matrx/meet 0.7.78 → 0.7.79

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/react.cjs CHANGED
@@ -83,6 +83,7 @@ __export(react_exports, {
83
83
  LockIcon: () => LockIcon,
84
84
  MEETING_INVITE_KIND: () => MEETING_INVITE_KIND,
85
85
  MEETING_INVITE_VERSION: () => MEETING_INVITE_VERSION,
86
+ MEETING_RECORD_GRACE_MS: () => MEETING_RECORD_GRACE_MS,
86
87
  MEET_ANSWER_CONTEXT_EVENT: () => MEET_ANSWER_CONTEXT_EVENT,
87
88
  MEET_ANSWER_EVENTS: () => MEET_ANSWER_EVENTS,
88
89
  MEET_EVENTS: () => MEET_EVENTS,
@@ -160,6 +161,7 @@ __export(react_exports, {
160
161
  asParticipantIdentity: () => asParticipantIdentity,
161
162
  asRoomName: () => asRoomName,
162
163
  asUserId: () => asUserId,
164
+ assembleMeetingRecord: () => assembleMeetingRecord,
163
165
  assignedRoom: () => assignedRoom,
164
166
  attendanceCsv: () => attendanceCsv,
165
167
  attendanceReport: () => attendanceReport,
@@ -189,6 +191,7 @@ __export(react_exports, {
189
191
  createMeetRepository: () => createMeetRepository,
190
192
  createMeetStore: () => createMeetStore,
191
193
  createMeetingInviteHandler: () => createMeetingInviteHandler,
194
+ createMeetingRecordCache: () => createMeetingRecordCache,
192
195
  createMeetingSession: () => createMeetingSession,
193
196
  createMemoryDeviceStorage: () => createMemoryDeviceStorage,
194
197
  createRecordingController: () => createRecordingController,
@@ -223,12 +226,14 @@ __export(react_exports, {
223
226
  loadMeetingActivities: () => loadMeetingActivities,
224
227
  loadMeetingChat: () => loadMeetingChat,
225
228
  loadMeetingRecord: () => loadMeetingRecord,
229
+ loadMeetingRecordParts: () => loadMeetingRecordParts,
226
230
  mapConnectionState: () => mapConnectionState,
227
231
  mapQuality: () => mapQuality,
228
232
  meetingInviteAction: () => meetingInviteAction,
229
233
  meetingLength: () => meetingLength,
230
234
  meetingLink: () => meetingLink,
231
235
  meetingPath: () => meetingPath,
236
+ meetingRecordCache: () => meetingRecordCache,
232
237
  meetingSlug: () => meetingSlug,
233
238
  meetingTopic: () => meetingTopic,
234
239
  meetingWhen: () => meetingWhen,
@@ -2205,13 +2210,13 @@ function mergeCaption(list, caption) {
2205
2210
  return list.map((entry, position) => position === index ? caption : entry);
2206
2211
  }
2207
2212
  function mergeRecord(list, record) {
2208
- const byId = list.findIndex((entry) => entry.source === "record" && entry.id === record.id);
2209
- if (byId !== -1) {
2210
- const existing = list[byId];
2213
+ const byId2 = list.findIndex((entry) => entry.source === "record" && entry.id === record.id);
2214
+ if (byId2 !== -1) {
2215
+ const existing = list[byId2];
2211
2216
  if (existing !== void 0 && existing.text === record.text && existing.final === record.final) {
2212
2217
  return list;
2213
2218
  }
2214
- return list.map((entry, position) => position === byId ? record : entry);
2219
+ return list.map((entry, position) => position === byId2 ? record : entry);
2215
2220
  }
2216
2221
  const captionIndex = list.findIndex((entry) => supersedes(record, entry));
2217
2222
  if (captionIndex === -1) return [...list, record];
@@ -5786,49 +5791,7 @@ function useLocalUserId() {
5786
5791
 
5787
5792
  // src/react/hooks.ts
5788
5793
  var import_react3 = require("react");
5789
-
5790
- // src/core/layout.ts
5791
- var DEFAULT_PAGE_SIZE = 12;
5792
- function orderParticipants(participants, activeSpeakers) {
5793
- const speakerRank = /* @__PURE__ */ new Map();
5794
- activeSpeakers.forEach((identity, index) => speakerRank.set(identity, index));
5795
- return [...participants].sort((a, b) => {
5796
- const speakerA = speakerRank.get(a.identity) ?? Number.MAX_SAFE_INTEGER;
5797
- const speakerB = speakerRank.get(b.identity) ?? Number.MAX_SAFE_INTEGER;
5798
- if (speakerA !== speakerB) return speakerA - speakerB;
5799
- if (a.handRaised !== b.handRaised) return a.handRaised ? -1 : 1;
5800
- if (a.cameraEnabled !== b.cameraEnabled) return a.cameraEnabled ? -1 : 1;
5801
- if (a.joinedAt !== b.joinedAt) return a.joinedAt < b.joinedAt ? -1 : 1;
5802
- return a.identity < b.identity ? -1 : 1;
5803
- });
5804
- }
5805
- function computeStage(args) {
5806
- const pageSize = Math.max(1, args.pageSize ?? DEFAULT_PAGE_SIZE);
5807
- const ordered = orderParticipants(args.participants, args.activeSpeakers);
5808
- const sharer = ordered.find((participant) => participant.screenSharing) ?? null;
5809
- const pinnedParticipant = args.pinned === null ? null : ordered.find((participant) => participant.identity === args.pinned) ?? null;
5810
- const spotlit = args.spotlight == null ? null : ordered.find((participant) => participant.identity === args.spotlight) ?? null;
5811
- const feature = sharer !== null ? sharer : pinnedParticipant !== null && args.layout !== "grid" ? pinnedParticipant : spotlit !== null ? spotlit : args.layout === "grid" ? null : ordered[0] ?? null;
5812
- const rest = feature === null ? ordered : ordered.filter((participant) => participant.identity !== feature.identity);
5813
- const pageCount = Math.max(1, Math.ceil(rest.length / pageSize));
5814
- const page = Math.min(Math.max(0, args.page ?? 0), pageCount - 1);
5815
- const tiles = rest.slice(page * pageSize, page * pageSize + pageSize);
5816
- return {
5817
- feature,
5818
- tiles,
5819
- overflow: rest.length - tiles.length,
5820
- page,
5821
- pageCount,
5822
- presenting: sharer !== null
5823
- };
5824
- }
5825
- function gridColumns(count2) {
5826
- if (count2 <= 1) return 1;
5827
- if (count2 <= 4) return 2;
5828
- if (count2 <= 9) return 3;
5829
- if (count2 <= 16) return 4;
5830
- return 5;
5831
- }
5794
+ var import_react4 = require("@ai-matrx/realtime/react");
5832
5795
 
5833
5796
  // src/core/record.ts
5834
5797
  var import_format = require("@ai-matrx/kit/format");
@@ -5852,6 +5815,9 @@ function restrictionFor(part, error) {
5852
5815
  };
5853
5816
  }
5854
5817
  async function loadMeetingRecord(repository, meetingId, meeting, reader = { userId: null }) {
5818
+ return assembleMeetingRecord(meeting, await loadMeetingRecordParts(repository, meetingId, reader));
5819
+ }
5820
+ async function loadMeetingRecordParts(repository, meetingId, reader = { userId: null }) {
5855
5821
  const restrictions = [];
5856
5822
  async function attempt(part, run, empty) {
5857
5823
  if (reader.userId === null) {
@@ -5879,6 +5845,10 @@ async function loadMeetingRecord(repository, meetingId, meeting, reader = { user
5879
5845
  []
5880
5846
  )
5881
5847
  ]);
5848
+ return { attendees, notes, transcript, recordings, restrictions };
5849
+ }
5850
+ function assembleMeetingRecord(meeting, parts) {
5851
+ const { attendees, notes, transcript, recordings, restrictions } = parts;
5882
5852
  return {
5883
5853
  meeting,
5884
5854
  attendees,
@@ -6224,6 +6194,253 @@ async function setGuestRecordAccess(args) {
6224
6194
  }
6225
6195
  var GUEST_RECORD_NOT_SHARED = NOT_SHARED;
6226
6196
 
6197
+ // src/core/record-cache.ts
6198
+ var MEETING_RECORD_GRACE_MS = 6e4;
6199
+ var INITIAL = { loading: true, record: null, failure: null };
6200
+ function upsert2(rows, row, removedId, order) {
6201
+ const id = row?.id ?? removedId;
6202
+ if (id === void 0) return rows;
6203
+ const rest = rows.filter((existing) => existing.id !== id);
6204
+ return row === null ? rest : [...rest, row].sort(order);
6205
+ }
6206
+ var byId = (a, b) => a.id < b.id ? -1 : a.id > b.id ? 1 : 0;
6207
+ var ascending = (a, b) => a === b ? 0 : a === null ? 1 : b === null ? -1 : a < b ? -1 : 1;
6208
+ function createMeetingRecordCache(options = {}) {
6209
+ const graceMs = options.graceMs ?? MEETING_RECORD_GRACE_MS;
6210
+ const entries = /* @__PURE__ */ new Map();
6211
+ const listeners = /* @__PURE__ */ new Set();
6212
+ const notify = () => {
6213
+ for (const listener of listeners) listener();
6214
+ };
6215
+ const entryOf = (key) => {
6216
+ let entry = entries.get(key);
6217
+ if (!entry) {
6218
+ entry = {
6219
+ state: INITIAL,
6220
+ meeting: null,
6221
+ parts: null,
6222
+ fresh: false,
6223
+ seq: 0,
6224
+ inflight: null,
6225
+ holders: 0,
6226
+ close: null,
6227
+ owner: null,
6228
+ grace: null
6229
+ };
6230
+ entries.set(key, entry);
6231
+ }
6232
+ return entry;
6233
+ };
6234
+ const assemble = (entry) => {
6235
+ if (entry.meeting === null || entry.parts === null) return;
6236
+ entry.state = { loading: false, failure: null, record: assembleMeetingRecord(entry.meeting, entry.parts) };
6237
+ notify();
6238
+ };
6239
+ return {
6240
+ state: (key) => key === null ? INITIAL : entries.get(key)?.state ?? INITIAL,
6241
+ subscribe(listener) {
6242
+ listeners.add(listener);
6243
+ return () => {
6244
+ listeners.delete(listener);
6245
+ };
6246
+ },
6247
+ ensure(key, read, { force = false } = {}) {
6248
+ const entry = entryOf(key);
6249
+ if (!force) {
6250
+ if (entry.inflight !== null) return entry.inflight;
6251
+ if (entry.fresh) return Promise.resolve();
6252
+ }
6253
+ const seq = ++entry.seq;
6254
+ if (entry.state.failure !== null) {
6255
+ entry.state = { ...entry.state, loading: true, failure: null };
6256
+ notify();
6257
+ }
6258
+ const request = read().then(
6259
+ (answer) => {
6260
+ if (seq !== entry.seq) return;
6261
+ entry.fresh = true;
6262
+ if (answer.kind === "parts") {
6263
+ entry.meeting = answer.meeting;
6264
+ entry.parts = answer.parts;
6265
+ assemble(entry);
6266
+ } else {
6267
+ entry.meeting = answer.bundle.meeting;
6268
+ entry.parts = null;
6269
+ entry.state = { loading: false, failure: null, record: answer.bundle };
6270
+ notify();
6271
+ }
6272
+ },
6273
+ (thrown) => {
6274
+ if (seq !== entry.seq) return;
6275
+ entry.fresh = false;
6276
+ entry.state = {
6277
+ loading: false,
6278
+ record: entry.state.record,
6279
+ failure: {
6280
+ message: thrown?.message ?? String(thrown),
6281
+ remedy: thrown?.remedy ?? "The meeting record could not be read. Reload the page; nothing was changed."
6282
+ }
6283
+ };
6284
+ notify();
6285
+ }
6286
+ );
6287
+ const tracked = request.finally(() => {
6288
+ if (entry.inflight === tracked) entry.inflight = null;
6289
+ });
6290
+ entry.inflight = tracked;
6291
+ return tracked;
6292
+ },
6293
+ setMeeting(key, meeting) {
6294
+ const entry = entries.get(key);
6295
+ if (!entry || entry.meeting === meeting) return;
6296
+ if (entry.parts !== null) {
6297
+ entry.meeting = meeting;
6298
+ assemble(entry);
6299
+ } else if (entry.state.record !== null) {
6300
+ entry.meeting = meeting;
6301
+ entry.state = { ...entry.state, record: { ...entry.state.record, meeting } };
6302
+ notify();
6303
+ }
6304
+ },
6305
+ apply(key, change) {
6306
+ const entry = entries.get(key);
6307
+ const parts = entry?.parts;
6308
+ if (!entry || !parts) return;
6309
+ const refused = parts.restrictions.some((r) => r.part === change.part);
6310
+ if (refused) return;
6311
+ switch (change.part) {
6312
+ case "transcript":
6313
+ entry.parts = {
6314
+ ...parts,
6315
+ transcript: upsert2(
6316
+ parts.transcript,
6317
+ change.row,
6318
+ change.removedId,
6319
+ (a, b) => ascending(a.startedAt, b.startedAt) || byId(a, b)
6320
+ )
6321
+ };
6322
+ break;
6323
+ case "notes":
6324
+ entry.parts = {
6325
+ ...parts,
6326
+ notes: upsert2(
6327
+ parts.notes,
6328
+ change.row,
6329
+ change.removedId,
6330
+ (a, b) => ascending(a.createdAt, b.createdAt) || byId(a, b)
6331
+ )
6332
+ };
6333
+ break;
6334
+ case "attendance":
6335
+ entry.parts = {
6336
+ ...parts,
6337
+ attendees: upsert2(
6338
+ parts.attendees,
6339
+ change.row,
6340
+ change.removedId,
6341
+ (a, b) => ascending(a.joinedAt, b.joinedAt) || byId(a, b)
6342
+ )
6343
+ };
6344
+ break;
6345
+ case "recording":
6346
+ entry.parts = {
6347
+ ...parts,
6348
+ // Newest first, as the read orders them.
6349
+ recordings: upsert2(
6350
+ parts.recordings,
6351
+ change.row,
6352
+ change.removedId,
6353
+ (a, b) => ascending(b.startedAt, a.startedAt) || byId(a, b)
6354
+ )
6355
+ };
6356
+ break;
6357
+ }
6358
+ assemble(entry);
6359
+ },
6360
+ hold(key, open, owner = null) {
6361
+ const entry = entryOf(key);
6362
+ if (entry.grace !== null) {
6363
+ clearTimeout(entry.grace);
6364
+ entry.grace = null;
6365
+ }
6366
+ if (entry.close !== null && entry.owner !== owner) {
6367
+ entry.close();
6368
+ entry.close = null;
6369
+ }
6370
+ if (entry.close === null) {
6371
+ entry.close = open();
6372
+ entry.owner = owner;
6373
+ }
6374
+ entry.holders += 1;
6375
+ let released = false;
6376
+ return () => {
6377
+ if (released) return;
6378
+ released = true;
6379
+ entry.holders -= 1;
6380
+ if (entry.holders > 0) return;
6381
+ entry.grace = setTimeout(() => {
6382
+ entry.grace = null;
6383
+ if (entry.holders > 0) return;
6384
+ entry.close?.();
6385
+ entry.close = null;
6386
+ entry.fresh = false;
6387
+ }, graceMs);
6388
+ };
6389
+ },
6390
+ reset() {
6391
+ for (const entry of entries.values()) {
6392
+ if (entry.grace !== null) clearTimeout(entry.grace);
6393
+ entry.close?.();
6394
+ }
6395
+ entries.clear();
6396
+ notify();
6397
+ }
6398
+ };
6399
+ }
6400
+
6401
+ // src/core/layout.ts
6402
+ var DEFAULT_PAGE_SIZE = 12;
6403
+ function orderParticipants(participants, activeSpeakers) {
6404
+ const speakerRank = /* @__PURE__ */ new Map();
6405
+ activeSpeakers.forEach((identity, index) => speakerRank.set(identity, index));
6406
+ return [...participants].sort((a, b) => {
6407
+ const speakerA = speakerRank.get(a.identity) ?? Number.MAX_SAFE_INTEGER;
6408
+ const speakerB = speakerRank.get(b.identity) ?? Number.MAX_SAFE_INTEGER;
6409
+ if (speakerA !== speakerB) return speakerA - speakerB;
6410
+ if (a.handRaised !== b.handRaised) return a.handRaised ? -1 : 1;
6411
+ if (a.cameraEnabled !== b.cameraEnabled) return a.cameraEnabled ? -1 : 1;
6412
+ if (a.joinedAt !== b.joinedAt) return a.joinedAt < b.joinedAt ? -1 : 1;
6413
+ return a.identity < b.identity ? -1 : 1;
6414
+ });
6415
+ }
6416
+ function computeStage(args) {
6417
+ const pageSize = Math.max(1, args.pageSize ?? DEFAULT_PAGE_SIZE);
6418
+ const ordered = orderParticipants(args.participants, args.activeSpeakers);
6419
+ const sharer = ordered.find((participant) => participant.screenSharing) ?? null;
6420
+ const pinnedParticipant = args.pinned === null ? null : ordered.find((participant) => participant.identity === args.pinned) ?? null;
6421
+ const spotlit = args.spotlight == null ? null : ordered.find((participant) => participant.identity === args.spotlight) ?? null;
6422
+ const feature = sharer !== null ? sharer : pinnedParticipant !== null && args.layout !== "grid" ? pinnedParticipant : spotlit !== null ? spotlit : args.layout === "grid" ? null : ordered[0] ?? null;
6423
+ const rest = feature === null ? ordered : ordered.filter((participant) => participant.identity !== feature.identity);
6424
+ const pageCount = Math.max(1, Math.ceil(rest.length / pageSize));
6425
+ const page = Math.min(Math.max(0, args.page ?? 0), pageCount - 1);
6426
+ const tiles = rest.slice(page * pageSize, page * pageSize + pageSize);
6427
+ return {
6428
+ feature,
6429
+ tiles,
6430
+ overflow: rest.length - tiles.length,
6431
+ page,
6432
+ pageCount,
6433
+ presenting: sharer !== null
6434
+ };
6435
+ }
6436
+ function gridColumns(count2) {
6437
+ if (count2 <= 1) return 1;
6438
+ if (count2 <= 4) return 2;
6439
+ if (count2 <= 9) return 3;
6440
+ if (count2 <= 16) return 4;
6441
+ return 5;
6442
+ }
6443
+
6227
6444
  // src/react/hooks.ts
6228
6445
  var EMPTY_NAMES = {};
6229
6446
  var NO_RUNTIME_REMEDY = "Render this inside a <MeetProvider> that has a Supabase client, an organization id and an identity (userId or guestName). The provider stays inert until all three are real, and while it is inert this package's components render nothing rather than a dead control.";
@@ -6809,16 +7026,22 @@ function useMeeting() {
6809
7026
  function useMeetingId() {
6810
7027
  return useMeetSnapshot()?.meeting?.id ?? null;
6811
7028
  }
7029
+ var RECORD_CACHE_KEY = /* @__PURE__ */ Symbol.for("@ai-matrx/meet/meeting-record-cache");
7030
+ function meetingRecordCache() {
7031
+ const holder = globalThis;
7032
+ let cache = holder[RECORD_CACHE_KEY];
7033
+ if (cache === void 0) {
7034
+ cache = createMeetingRecordCache();
7035
+ holder[RECORD_CACHE_KEY] = cache;
7036
+ }
7037
+ return cache;
7038
+ }
7039
+ var rowIdOf = (row) => typeof row["id"] === "string" ? row["id"] : void 0;
6812
7040
  function useMeetingRecord(meeting) {
6813
7041
  const host = useMeetHost();
6814
7042
  const repository = host?.repository ?? null;
6815
7043
  const ai = host?.ai ?? null;
6816
7044
  const readerId = host?.identity.userId ?? null;
6817
- const [recordReaderId, setRecordReaderId] = (0, import_react3.useState)(readerId);
6818
- const [record, setRecord] = (0, import_react3.useState)(null);
6819
- const [loading, setLoading] = (0, import_react3.useState)(true);
6820
- const [failure, setFailure] = (0, import_react3.useState)(null);
6821
- const [nonce, setNonce] = (0, import_react3.useState)(0);
6822
7045
  const [asking, setAsking] = (0, import_react3.useState)(false);
6823
7046
  const [answer, setAnswer] = (0, import_react3.useState)(null);
6824
7047
  const [coverage, setCoverage] = (0, import_react3.useState)(null);
@@ -6827,57 +7050,107 @@ function useMeetingRecord(meeting) {
6827
7050
  const api = host?.api ?? null;
6828
7051
  const tokens = host?.tokens ?? null;
6829
7052
  const roomName = meeting?.roomName ?? null;
6830
- (0, import_react3.useEffect)(() => {
6831
- if (repository === null || meeting === null || meetingId === null) return void 0;
6832
- let live = true;
6833
- setLoading(true);
6834
- setFailure(null);
6835
- setRecord(null);
6836
- const load = readerId === null && api !== null && tokens !== null && roomName !== null ? loadGuestMeetingRecord({
6837
- api,
6838
- repository,
6839
- meetingId,
6840
- meeting,
6841
- roomToken: tokens.remembered(roomName)
6842
- }) : loadMeetingRecord(repository, meetingId, meeting, { userId: readerId }).then(
6843
- (direct) => (
6844
- // A SIGNED-IN OUTSIDER IS NOT A STRANGER (2026-10-01). The database
6845
- // answers members and invitees; a person from another organization
6846
- // the host admitted — or a guest who later made an account — reads
6847
- // the guest share through the server, with their session as the pass.
6848
- api !== null && direct.restrictions.some((part) => part.part === "notes") ? loadGuestMeetingRecord({
7053
+ const manager = (0, import_react4.useRealtimeManager)();
7054
+ const cache = meetingRecordCache();
7055
+ const key = repository !== null && meetingId !== null ? `${readerId ?? `guest:${roomName ?? "-"}`}|${meetingId}` : null;
7056
+ const state = (0, import_react3.useSyncExternalStore)(
7057
+ cache.subscribe,
7058
+ () => cache.state(key),
7059
+ () => cache.state(null)
7060
+ );
7061
+ const readRef = (0, import_react3.useRef)(() => Promise.reject(new Error("not ready")));
7062
+ readRef.current = async () => {
7063
+ if (repository === null || meeting === null || meetingId === null) throw new Error("No meeting to read.");
7064
+ if (readerId === null && api !== null && tokens !== null && roomName !== null) {
7065
+ return {
7066
+ kind: "bundle",
7067
+ bundle: await loadGuestMeetingRecord({
6849
7068
  api,
6850
7069
  repository,
6851
7070
  meetingId,
6852
7071
  meeting,
6853
- roomToken: null,
6854
- session: true
6855
- }).then(
6856
- (viaServer) => viaServer.restrictions.length < direct.restrictions.length ? viaServer : direct
6857
- ) : direct
6858
- )
6859
- );
6860
- void load.then((bundle) => {
6861
- if (!live) return;
6862
- setRecordReaderId(readerId);
6863
- setRecord(bundle);
6864
- setLoading(false);
6865
- }).catch((thrown) => {
6866
- if (!live) return;
6867
- setFailure({
6868
- message: thrown.message,
6869
- remedy: thrown.remedy ?? "The meeting record could not be read. Reload the page; nothing was changed."
7072
+ roomToken: tokens.remembered(roomName)
7073
+ })
7074
+ };
7075
+ }
7076
+ const parts = await loadMeetingRecordParts(repository, meetingId, { userId: readerId });
7077
+ if (api !== null && parts.restrictions.some((part) => part.part === "notes")) {
7078
+ const viaServer = await loadGuestMeetingRecord({
7079
+ api,
7080
+ repository,
7081
+ meetingId,
7082
+ meeting,
7083
+ roomToken: null,
7084
+ session: true
6870
7085
  });
6871
- setLoading(false);
7086
+ if (viaServer.restrictions.length < parts.restrictions.length) return { kind: "bundle", bundle: viaServer };
7087
+ }
7088
+ return { kind: "parts", meeting, parts };
7089
+ };
7090
+ (0, import_react3.useEffect)(() => {
7091
+ if (key === null) return;
7092
+ void cache.ensure(key, () => readRef.current());
7093
+ }, [cache, key]);
7094
+ (0, import_react3.useEffect)(() => {
7095
+ if (key !== null && meeting !== null) cache.setMeeting(key, meeting);
7096
+ }, [cache, key, meeting]);
7097
+ (0, import_react3.useEffect)(() => {
7098
+ if (key === null || manager === null || repository === null || meetingId === null || readerId === null) {
7099
+ return void 0;
7100
+ }
7101
+ const repo = repository;
7102
+ const binding = (table, apply) => ({
7103
+ event: "*",
7104
+ schema: repo.schema,
7105
+ table,
7106
+ filter: `meeting_id=eq.${meetingId}`,
7107
+ rowId: rowIdOf,
7108
+ onChange: ({ row, payload }) => {
7109
+ if (row === null) return;
7110
+ const gone = payload.eventType === "DELETE";
7111
+ apply(gone ? null : row, gone ? rowIdOf(row) : void 0);
7112
+ }
6872
7113
  });
6873
- return () => {
6874
- live = false;
6875
- };
6876
- }, [repository, meeting, meetingId, readerId, api, tokens, roomName, nonce]);
7114
+ return cache.hold(
7115
+ key,
7116
+ () => {
7117
+ const handle = manager.open({
7118
+ topic: meetingTopic(meetingId),
7119
+ postgresChanges: [
7120
+ binding(
7121
+ repo.tables.notes,
7122
+ (row, removedId) => cache.apply(key, { part: "notes", row: row && repo.projectNote(row), removedId })
7123
+ ),
7124
+ binding(
7125
+ repo.tables.transcripts,
7126
+ (row, removedId) => cache.apply(key, { part: "transcript", row: row && repo.projectSegment(row), removedId })
7127
+ ),
7128
+ binding(
7129
+ repo.tables.participants,
7130
+ (row, removedId) => cache.apply(key, { part: "attendance", row: row && repo.projectAttendee(row), removedId })
7131
+ ),
7132
+ binding(
7133
+ repo.tables.recordings,
7134
+ (row, removedId) => (
7135
+ // An archived recording is off the record (the read filters it the same way).
7136
+ row !== null && row["deleted_at"] != null ? cache.apply(key, { part: "recording", row: null, removedId: rowIdOf(row) }) : cache.apply(key, { part: "recording", row: row && repo.projectRecording(row), removedId })
7137
+ )
7138
+ )
7139
+ ],
7140
+ // Realtime has no replay: a socket that blipped reads the record again.
7141
+ onBackfill: async () => {
7142
+ await cache.ensure(key, () => readRef.current(), { force: true });
7143
+ }
7144
+ });
7145
+ return () => handle.close();
7146
+ },
7147
+ manager
7148
+ );
7149
+ }, [cache, key, manager, repository, meetingId, readerId]);
6877
7150
  return {
6878
- loading: loading && failure === null,
6879
- record: recordReaderId === readerId ? record : null,
6880
- failure,
7151
+ loading: state.loading && state.failure === null,
7152
+ record: state.record,
7153
+ failure: state.failure,
6881
7154
  asking,
6882
7155
  answer,
6883
7156
  coverage,
@@ -6908,7 +7181,9 @@ function useMeetingRecord(meeting) {
6908
7181
  },
6909
7182
  [ai, meetingId]
6910
7183
  ),
6911
- reload: (0, import_react3.useCallback)(() => setNonce((value) => value + 1), [])
7184
+ reload: (0, import_react3.useCallback)(() => {
7185
+ if (key !== null) void cache.ensure(key, () => readRef.current(), { force: true });
7186
+ }, [cache, key])
6912
7187
  };
6913
7188
  }
6914
7189
  function useMeetings(limit = 25, organizationFilter = null) {
@@ -6995,7 +7270,7 @@ function useGuestRecordAccess(meeting) {
6995
7270
  }
6996
7271
 
6997
7272
  // src/react/components.tsx
6998
- var import_react5 = require("react");
7273
+ var import_react6 = require("react");
6999
7274
  var import_design_system = require("@ai-matrx/design-system");
7000
7275
  var import_format4 = require("@ai-matrx/kit/format");
7001
7276
 
@@ -7440,7 +7715,7 @@ function countLabel(count2, singular, plural = `${singular}s`) {
7440
7715
  }
7441
7716
 
7442
7717
  // src/react/collab.tsx
7443
- var import_react4 = require("react");
7718
+ var import_react5 = require("react");
7444
7719
  var import_format3 = require("@ai-matrx/kit/format");
7445
7720
 
7446
7721
  // src/react/icons.tsx
@@ -7717,8 +7992,8 @@ function useCollab() {
7717
7992
  };
7718
7993
  }
7719
7994
  function useNotice() {
7720
- const [notice, setNotice] = (0, import_react4.useState)(null);
7721
- (0, import_react4.useEffect)(() => {
7995
+ const [notice, setNotice] = (0, import_react5.useState)(null);
7996
+ (0, import_react5.useEffect)(() => {
7722
7997
  if (notice === null) return void 0;
7723
7998
  const timer = setTimeout(() => setNotice(null), notice.failed ? 7e3 : 3e3);
7724
7999
  return () => clearTimeout(timer);
@@ -7764,7 +8039,7 @@ function ActivitiesControl({
7764
8039
  active,
7765
8040
  onOpen
7766
8041
  }) {
7767
- const [open, setOpen] = (0, import_react4.useState)(false);
8042
+ const [open, setOpen] = (0, import_react5.useState)(false);
7768
8043
  const available = useAvailableActivities();
7769
8044
  const attention = useActivityAttention();
7770
8045
  if (available.length === 0) return null;
@@ -7792,11 +8067,11 @@ function ActivitiesControl({
7792
8067
  }
7793
8068
  function PollComposer({ maxOptions, onDone }) {
7794
8069
  const { act } = useCollab();
7795
- const [question, setQuestion] = (0, import_react4.useState)("");
7796
- const [options, setOptions] = (0, import_react4.useState)(["", ""]);
7797
- const [multiple, setMultiple] = (0, import_react4.useState)(false);
7798
- const [anonymous, setAnonymous] = (0, import_react4.useState)(false);
7799
- const [busy, setBusy] = (0, import_react4.useState)(false);
8070
+ const [question, setQuestion] = (0, import_react5.useState)("");
8071
+ const [options, setOptions] = (0, import_react5.useState)(["", ""]);
8072
+ const [multiple, setMultiple] = (0, import_react5.useState)(false);
8073
+ const [anonymous, setAnonymous] = (0, import_react5.useState)(false);
8074
+ const [busy, setBusy] = (0, import_react5.useState)(false);
7800
8075
  const [notice, show] = useNotice();
7801
8076
  const filled = options.map((o) => o.trim()).filter(Boolean);
7802
8077
  const ready = question.trim().length > 0 && filled.length >= 2;
@@ -7877,9 +8152,9 @@ function PollComposer({ maxOptions, onDone }) {
7877
8152
  }
7878
8153
  function PollCard({ poll }) {
7879
8154
  const { act, isHost } = useCollab();
7880
- const [picked, setPicked] = (0, import_react4.useState)(poll.myVote.slice());
8155
+ const [picked, setPicked] = (0, import_react5.useState)(poll.myVote.slice());
7881
8156
  const [notice, show] = useNotice();
7882
- (0, import_react4.useEffect)(() => setPicked(poll.myVote.slice()), [poll.myVote]);
8157
+ (0, import_react5.useEffect)(() => setPicked(poll.myVote.slice()), [poll.myVote]);
7883
8158
  const counts = poll.options.some((o) => o.votes !== void 0);
7884
8159
  const total = poll.options.reduce((sum, o) => sum + (o.votes ?? 0), 0);
7885
8160
  const canVote = poll.state === "live";
@@ -7948,7 +8223,7 @@ function PollCard({ poll }) {
7948
8223
  }
7949
8224
  function PollsPanel({ onClose }) {
7950
8225
  const { collab, isHost } = useCollab();
7951
- const [composing, setComposing] = (0, import_react4.useState)(false);
8226
+ const [composing, setComposing] = (0, import_react5.useState)(false);
7952
8227
  const polls = collab?.polls ?? [];
7953
8228
  const maxOptions = collab?.settings?.pollMaxOptions ?? 10;
7954
8229
  const ordered = [...polls].reverse();
@@ -7962,8 +8237,8 @@ function PollsPanel({ onClose }) {
7962
8237
  }
7963
8238
  function QuestionCard({ question }) {
7964
8239
  const { act, isHost } = useCollab();
7965
- const [answering, setAnswering] = (0, import_react4.useState)(false);
7966
- const [reply, setReply] = (0, import_react4.useState)("");
8240
+ const [answering, setAnswering] = (0, import_react5.useState)(false);
8241
+ const [reply, setReply] = (0, import_react5.useState)("");
7967
8242
  const [notice, show] = useNotice();
7968
8243
  return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("li", { className: `mx-meet__collab-card mx-meet__question mx-meet__question--${question.state}`, children: [
7969
8244
  /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "mx-meet__question-row", children: [
@@ -8041,9 +8316,9 @@ function QuestionCard({ question }) {
8041
8316
  }
8042
8317
  function QaPanel({ onClose }) {
8043
8318
  const { collab, act } = useCollab();
8044
- const [draft, setDraft] = (0, import_react4.useState)("");
8045
- const [anonymous, setAnonymous] = (0, import_react4.useState)(false);
8046
- const [order, setOrder] = (0, import_react4.useState)("popular");
8319
+ const [draft, setDraft] = (0, import_react5.useState)("");
8320
+ const [anonymous, setAnonymous] = (0, import_react5.useState)(false);
8321
+ const [order, setOrder] = (0, import_react5.useState)("popular");
8047
8322
  const [notice, show] = useNotice();
8048
8323
  const allowAnonymous = collab?.settings?.qaAnonymousAllowed === true;
8049
8324
  const questions = [...collab?.questions ?? []];
@@ -8132,12 +8407,12 @@ function StrokeView({ stroke, onPick }) {
8132
8407
  }
8133
8408
  function WhiteboardPanel({ onClose }) {
8134
8409
  const { collab, act, isHost, localIdentity } = useCollab();
8135
- const [tool, setTool] = (0, import_react4.useState)("pen");
8136
- const [color, setColor] = (0, import_react4.useState)(INK[0]);
8137
- const [drawing, setDrawing] = (0, import_react4.useState)(null);
8138
- const [pending, setPending] = (0, import_react4.useState)([]);
8410
+ const [tool, setTool] = (0, import_react5.useState)("pen");
8411
+ const [color, setColor] = (0, import_react5.useState)(INK[0]);
8412
+ const [drawing, setDrawing] = (0, import_react5.useState)(null);
8413
+ const [pending, setPending] = (0, import_react5.useState)([]);
8139
8414
  const [notice, show] = useNotice();
8140
- const svgRef = (0, import_react4.useRef)(null);
8415
+ const svgRef = (0, import_react5.useRef)(null);
8141
8416
  const strokes = collab?.strokes ?? [];
8142
8417
  const mine = strokes.filter((s) => s.by === localIdentity);
8143
8418
  const point = (event) => {
@@ -8278,8 +8553,8 @@ function clock(ms2) {
8278
8553
  }
8279
8554
  var BREAKOUT_EXPIRE_RETRY_MS = 5e3;
8280
8555
  function useNow(active) {
8281
- const [now, setNow] = (0, import_react4.useState)(() => Date.now());
8282
- (0, import_react4.useEffect)(() => {
8556
+ const [now, setNow] = (0, import_react5.useState)(() => Date.now());
8557
+ (0, import_react5.useEffect)(() => {
8283
8558
  if (!active) return void 0;
8284
8559
  const timer = setInterval(() => setNow(Date.now()), 1e3);
8285
8560
  return () => clearInterval(timer);
@@ -8293,11 +8568,11 @@ function BreakoutSetup() {
8293
8568
  const people = (snapshot?.participants ?? []).filter(
8294
8569
  (p) => !p.isAgent && p.role !== "host" && p.role !== "cohost"
8295
8570
  );
8296
- const [count2, setCount] = (0, import_react4.useState)(Math.min(Math.max(2, Math.ceil(people.length / 4)), settings?.breakoutMaxRooms ?? 20));
8297
- const [mode, setMode] = (0, import_react4.useState)("auto");
8298
- const [minutes, setMinutes] = (0, import_react4.useState)(settings?.breakoutDefaultMinutes ?? 15);
8299
- const [manual, setManual] = (0, import_react4.useState)({});
8300
- const [busy, setBusy] = (0, import_react4.useState)(false);
8571
+ const [count2, setCount] = (0, import_react5.useState)(Math.min(Math.max(2, Math.ceil(people.length / 4)), settings?.breakoutMaxRooms ?? 20));
8572
+ const [mode, setMode] = (0, import_react5.useState)("auto");
8573
+ const [minutes, setMinutes] = (0, import_react5.useState)(settings?.breakoutDefaultMinutes ?? 15);
8574
+ const [manual, setManual] = (0, import_react5.useState)({});
8575
+ const [busy, setBusy] = (0, import_react5.useState)(false);
8301
8576
  const [notice, show] = useNotice();
8302
8577
  const max = settings?.breakoutMaxRooms ?? 20;
8303
8578
  const rooms = Math.min(Math.max(1, count2), max);
@@ -8413,7 +8688,7 @@ function BreakoutRun({ plan }) {
8413
8688
  const snapshot = useMeetSnapshot();
8414
8689
  const now = useNow(plan.endsAt !== null);
8415
8690
  const remaining = breakoutRemainingMs(plan, now);
8416
- const [broadcast, setBroadcast] = (0, import_react4.useState)("");
8691
+ const [broadcast, setBroadcast] = (0, import_react5.useState)("");
8417
8692
  const [notice, show] = useNotice();
8418
8693
  const nameOf = (identity) => displayNameFor(snapshot?.names ?? {}, identity, snapshot?.participants.find((p) => p.identity === identity)?.displayName);
8419
8694
  const mine = assignedRoom(plan, localIdentity);
@@ -8523,11 +8798,11 @@ function BreakoutBanner() {
8523
8798
  const now = useNow(open?.endsAt != null);
8524
8799
  const remaining = breakoutRemainingMs(open, now);
8525
8800
  const assigned = assignedRoom(open, localIdentity);
8526
- const [dismissedBroadcast, setDismissedBroadcast] = (0, import_react4.useState)(null);
8527
- const [failure, setFailure] = (0, import_react4.useState)(null);
8528
- const expireAsked = (0, import_react4.useRef)(null);
8801
+ const [dismissedBroadcast, setDismissedBroadcast] = (0, import_react5.useState)(null);
8802
+ const [failure, setFailure] = (0, import_react5.useState)(null);
8803
+ const expireAsked = (0, import_react5.useRef)(null);
8529
8804
  const moveAt = collab?.moveRequest?.at ?? null;
8530
- (0, import_react4.useEffect)(() => {
8805
+ (0, import_react5.useEffect)(() => {
8531
8806
  const request = host?.store.snapshot().collab.moveRequest ?? null;
8532
8807
  if (request === null || host === null) return;
8533
8808
  host.store.patch({ collab: { ...host.store.snapshot().collab, moveRequest: null } });
@@ -8535,7 +8810,7 @@ function BreakoutBanner() {
8535
8810
  if ((here ?? null) === request.roomId) return;
8536
8811
  void moveTo(request.roomId).then((result) => setFailure(result.ok ? null : result.message));
8537
8812
  }, [moveAt, host, moveTo]);
8538
- (0, import_react4.useEffect)(() => {
8813
+ (0, import_react5.useEffect)(() => {
8539
8814
  if (open === null || remaining === null || remaining > 0) return;
8540
8815
  const asked = expireAsked.current;
8541
8816
  if (asked !== null && asked.id === open.id && now - asked.at < BREAKOUT_EXPIRE_RETRY_MS) return;
@@ -8585,13 +8860,13 @@ function useCaptionTranslation(lines) {
8585
8860
  const snapshot = useMeetSnapshot();
8586
8861
  const collab = snapshot?.collab ?? null;
8587
8862
  const language = collab?.captionLanguage ?? null;
8588
- const asked = (0, import_react4.useRef)(/* @__PURE__ */ new Set());
8863
+ const asked = (0, import_react5.useRef)(/* @__PURE__ */ new Set());
8589
8864
  const translations = collab?.captionTranslations ?? {};
8590
8865
  const missing = lines.filter((line) => line.final && translations[line.id] === void 0).map(({ id, text: text3 }) => ({ id, text: text3 }));
8591
8866
  const key = `${language ?? ""}|${missing.map((l) => l.id).join(",")}`;
8592
- const pending = (0, import_react4.useRef)(missing);
8867
+ const pending = (0, import_react5.useRef)(missing);
8593
8868
  pending.current = missing;
8594
- (0, import_react4.useEffect)(() => {
8869
+ (0, import_react5.useEffect)(() => {
8595
8870
  if (host === null || language === null) return;
8596
8871
  const batch = pending.current.filter((line) => !asked.current.has(`${language}:${line.id}`)).slice(-20);
8597
8872
  if (batch.length === 0) return;
@@ -8646,9 +8921,9 @@ function CaptionLanguageSettings() {
8646
8921
  function useCollabPanelRequest(onRequest) {
8647
8922
  const host = useMeetHost();
8648
8923
  const at = useMeetSnapshot()?.collab.panelRequest?.at ?? null;
8649
- const callback = (0, import_react4.useRef)(onRequest);
8924
+ const callback = (0, import_react5.useRef)(onRequest);
8650
8925
  callback.current = onRequest;
8651
- (0, import_react4.useEffect)(() => {
8926
+ (0, import_react5.useEffect)(() => {
8652
8927
  const request = host?.store.snapshot().collab.panelRequest ?? null;
8653
8928
  if (request === null || host === null) return;
8654
8929
  host.store.patch({ collab: { ...host.store.snapshot().collab, panelRequest: null } });
@@ -8757,7 +9032,7 @@ function ParticipantTile({
8757
9032
  const host = useMeetHost();
8758
9033
  const snapshot = useMeetSnapshot();
8759
9034
  const queue = useHandQueue();
8760
- const [menu, setMenu] = (0, import_react5.useState)(false);
9035
+ const [menu, setMenu] = (0, import_react6.useState)(false);
8761
9036
  const videoRef = useTrack(
8762
9037
  participant.identity,
8763
9038
  participant.screenSharing ? "screen" : "camera"
@@ -8871,17 +9146,17 @@ var MEET_REACTIONS = ["\u{1F496}", "\u{1F44D}", "\u{1F389}", "\u{1F44F}", "\u{1F
8871
9146
  function ControlBar({ onLeave }) {
8872
9147
  const room = useRoom();
8873
9148
  const recording = useRecording();
8874
- const [panel, setPanel] = (0, import_react5.useState)("none");
8875
- const [chatTo, setChatTo] = (0, import_react5.useState)(null);
8876
- const [popover, setPopover] = (0, import_react5.useState)("none");
8877
- const [shortcuts, setShortcuts] = (0, import_react5.useState)(false);
9149
+ const [panel, setPanel] = (0, import_react6.useState)("none");
9150
+ const [chatTo, setChatTo] = (0, import_react6.useState)(null);
9151
+ const [popover, setPopover] = (0, import_react6.useState)("none");
9152
+ const [shortcuts, setShortcuts] = (0, import_react6.useState)(false);
8878
9153
  const snapshot = useMeetSnapshot();
8879
9154
  const lobby = useLobby();
8880
9155
  const controls = useHostControls();
8881
9156
  const queue = useHandQueue();
8882
9157
  const micUnavailable = useLocalMedia().microphone.unavailable;
8883
9158
  const unread = useUnreadChat(panel === "chat");
8884
- const [whiteboard, setWhiteboard] = (0, import_react5.useState)(false);
9159
+ const [whiteboard, setWhiteboard] = (0, import_react6.useState)(false);
8885
9160
  const toggle = (next) => setPanel(panel === next ? "none" : next);
8886
9161
  const openActivity = (activity) => {
8887
9162
  if (activity === "whiteboard") setWhiteboard(true);
@@ -8892,7 +9167,7 @@ function ControlBar({ onLeave }) {
8892
9167
  });
8893
9168
  const privateChatAt = snapshot?.privateChatRequest?.at ?? null;
8894
9169
  const meetStore = useMeetHost()?.store ?? null;
8895
- (0, import_react5.useEffect)(() => {
9170
+ (0, import_react6.useEffect)(() => {
8896
9171
  const request = meetStore?.snapshot().privateChatRequest ?? null;
8897
9172
  if (request === null) return;
8898
9173
  setChatTo(request.to);
@@ -9130,13 +9405,13 @@ function useUnreadChat(open) {
9130
9405
  const snapshot = useMeetSnapshot();
9131
9406
  const chat = snapshot?.chat ?? [];
9132
9407
  const local = snapshot?.localIdentity ?? null;
9133
- const [seen, setSeen] = (0, import_react5.useState)(0);
9134
- (0, import_react5.useEffect)(() => {
9408
+ const [seen, setSeen] = (0, import_react6.useState)(0);
9409
+ (0, import_react6.useEffect)(() => {
9135
9410
  if (open) setSeen(chat.length);
9136
9411
  }, [open, chat.length]);
9137
9412
  const loaded = snapshot?.chatLoaded === true;
9138
- const [baseline, setBaseline] = (0, import_react5.useState)(null);
9139
- (0, import_react5.useEffect)(() => {
9413
+ const [baseline, setBaseline] = (0, import_react6.useState)(null);
9414
+ (0, import_react6.useEffect)(() => {
9140
9415
  if (loaded && baseline === null) setBaseline(chat.length);
9141
9416
  }, [loaded, baseline, chat.length]);
9142
9417
  if (open) return 0;
@@ -9149,14 +9424,14 @@ function Popover({
9149
9424
  children,
9150
9425
  placement = "up"
9151
9426
  }) {
9152
- const ref = (0, import_react5.useRef)(null);
9153
- const [style, setStyle] = (0, import_react5.useState)({
9427
+ const ref = (0, import_react6.useRef)(null);
9428
+ const [style, setStyle] = (0, import_react6.useState)({
9154
9429
  position: "fixed",
9155
9430
  top: 0,
9156
9431
  left: 0,
9157
9432
  visibility: "hidden"
9158
9433
  });
9159
- (0, import_react5.useLayoutEffect)(() => {
9434
+ (0, import_react6.useLayoutEffect)(() => {
9160
9435
  const element = ref.current;
9161
9436
  const anchor = element?.parentElement ?? null;
9162
9437
  if (element === null || anchor === null) return;
@@ -9201,7 +9476,7 @@ function Popover({
9201
9476
  window.removeEventListener("scroll", place, true);
9202
9477
  };
9203
9478
  }, [placement]);
9204
- (0, import_react5.useEffect)(() => {
9479
+ (0, import_react6.useEffect)(() => {
9205
9480
  const onKey = (event) => {
9206
9481
  if (event.key === "Escape") onClose();
9207
9482
  };
@@ -9235,7 +9510,7 @@ function MenuItem({
9235
9510
  );
9236
9511
  }
9237
9512
  function Toast({ text: text3, failed, onDone }) {
9238
- (0, import_react5.useEffect)(() => {
9513
+ (0, import_react6.useEffect)(() => {
9239
9514
  const timer = setTimeout(onDone, failed ? 7e3 : 4e3);
9240
9515
  return () => clearTimeout(timer);
9241
9516
  }, [text3, failed, onDone]);
@@ -9274,7 +9549,7 @@ function MeetingOverlays() {
9274
9549
  ] });
9275
9550
  }
9276
9551
  function ShortcutsSheet({ onClose }) {
9277
- (0, import_react5.useEffect)(() => {
9552
+ (0, import_react6.useEffect)(() => {
9278
9553
  const onKey = (event) => {
9279
9554
  if (event.key === "Escape") onClose();
9280
9555
  };
@@ -9312,8 +9587,8 @@ function PanelShell({
9312
9587
  onClose,
9313
9588
  children
9314
9589
  }) {
9315
- const ref = (0, import_react5.useRef)(null);
9316
- (0, import_react5.useLayoutEffect)(() => {
9590
+ const ref = (0, import_react6.useRef)(null);
9591
+ (0, import_react6.useLayoutEffect)(() => {
9317
9592
  const panel = ref.current;
9318
9593
  const root = panel?.closest(".mx-meet");
9319
9594
  const header = root?.querySelector(":scope > .mx-meet__header") ?? null;
@@ -9340,17 +9615,17 @@ function ChatPanel({
9340
9615
  }) {
9341
9616
  const snapshot = useMeetSnapshot();
9342
9617
  const room = useRoom();
9343
- const [draft, setDraft] = (0, import_react5.useState)("");
9344
- const [recipient, setRecipient] = (0, import_react5.useState)(to);
9345
- const listRef = (0, import_react5.useRef)(null);
9618
+ const [draft, setDraft] = (0, import_react6.useState)("");
9619
+ const [recipient, setRecipient] = (0, import_react6.useState)(to);
9620
+ const listRef = (0, import_react6.useRef)(null);
9346
9621
  const messages = snapshot?.chat ?? [];
9347
9622
  const local = snapshot?.localIdentity ?? null;
9348
9623
  const people = (snapshot?.participants ?? []).filter(
9349
9624
  (participant) => !participant.isLocal && !participant.isAgent
9350
9625
  );
9351
9626
  const recipientName = recipient === null ? null : people.find((p) => p.identity === recipient)?.displayName ?? null;
9352
- (0, import_react5.useEffect)(() => setRecipient(to), [to]);
9353
- (0, import_react5.useEffect)(() => {
9627
+ (0, import_react6.useEffect)(() => setRecipient(to), [to]);
9628
+ (0, import_react6.useEffect)(() => {
9354
9629
  const list = listRef.current;
9355
9630
  if (list !== null) list.scrollTop = list.scrollHeight;
9356
9631
  }, [messages.length]);
@@ -9456,8 +9731,8 @@ function PeoplePanel({
9456
9731
  const snapshot = useMeetSnapshot();
9457
9732
  const controls = useHostControls();
9458
9733
  const queue = useHandQueue();
9459
- const [query, setQuery] = (0, import_react5.useState)("");
9460
- const [menuFor, setMenuFor] = (0, import_react5.useState)(null);
9734
+ const [query, setQuery] = (0, import_react6.useState)("");
9735
+ const [menuFor, setMenuFor] = (0, import_react6.useState)(null);
9461
9736
  const myRole = useRole();
9462
9737
  const local = snapshot?.localIdentity ?? null;
9463
9738
  const everyone = snapshot?.participants ?? [];
@@ -9582,14 +9857,14 @@ function PersonMenu({
9582
9857
  const host = useMeetHost();
9583
9858
  const snapshot = useMeetSnapshot();
9584
9859
  const controls = useHostControls();
9585
- const [mode, setMode] = (0, import_react5.useState)("menu");
9586
- const [text3, setText] = (0, import_react5.useState)("");
9860
+ const [mode, setMode] = (0, import_react6.useState)("menu");
9861
+ const [text3, setText] = (0, import_react6.useState)("");
9587
9862
  const self = participant.identity === snapshot?.localIdentity;
9588
9863
  const moderate = controls.canModerate(participant);
9589
9864
  const pinned = snapshot?.pinned === participant.identity;
9590
9865
  const spotlit = snapshot?.spotlight === participant.identity;
9591
9866
  const hasAccount = participant.userId !== null;
9592
- const [refused, setRefused] = (0, import_react5.useState)(false);
9867
+ const [refused, setRefused] = (0, import_react6.useState)(false);
9593
9868
  const act = (request) => {
9594
9869
  setRefused(false);
9595
9870
  void controls.run(request).then((result) => {
@@ -9805,8 +10080,8 @@ function DeviceSettings({
9805
10080
  const speaker = devices?.selected.audiooutput ?? host.devices.resolve("audiooutput");
9806
10081
  const stream = useMicrophoneStream(microphone);
9807
10082
  const level = useAudioLevel(stream);
9808
- const [testing, setTesting] = (0, import_react5.useState)(false);
9809
- (0, import_react5.useEffect)(() => {
10083
+ const [testing, setTesting] = (0, import_react6.useState)(false);
10084
+ (0, import_react6.useEffect)(() => {
9810
10085
  void host.devices.refresh();
9811
10086
  }, [host]);
9812
10087
  return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("section", { className: "mx-meet__section", "aria-label": "Devices", children: [
@@ -9835,8 +10110,8 @@ function DeviceSettings({
9835
10110
  }
9836
10111
  function EffectsSettings({ preview }) {
9837
10112
  const effects = useMediaEffects();
9838
- const [error, setError] = (0, import_react5.useState)(null);
9839
- const fileRef = (0, import_react5.useRef)(null);
10113
+ const [error, setError] = (0, import_react6.useState)(null);
10114
+ const fileRef = (0, import_react6.useRef)(null);
9840
10115
  const supported = effects.backgroundsSupported || preview != null || videoEffectsSupported();
9841
10116
  const apply = (next) => {
9842
10117
  setError(null);
@@ -9958,8 +10233,8 @@ async function playTestSound(sinkId) {
9958
10233
  await context.close();
9959
10234
  }
9960
10235
  function useMicrophoneStream(deviceId2) {
9961
- const [stream, setStream] = (0, import_react5.useState)(null);
9962
- (0, import_react5.useEffect)(() => {
10236
+ const [stream, setStream] = (0, import_react6.useState)(null);
10237
+ (0, import_react6.useEffect)(() => {
9963
10238
  if (deviceId2 === "off") return void 0;
9964
10239
  let live = true;
9965
10240
  let opened = null;
@@ -10060,7 +10335,7 @@ function NoteList({
10060
10335
  }
10061
10336
  function AiPanel({ onClose }) {
10062
10337
  const ai = useMeetAi();
10063
- const [question, setQuestion] = (0, import_react5.useState)("");
10338
+ const [question, setQuestion] = (0, import_react6.useState)("");
10064
10339
  return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(PanelShell, { title: "Meeting assistant", onClose, children: [
10065
10340
  /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(NoteTakerRow, {}),
10066
10341
  ai.summary !== null ? /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("section", { className: "mx-meet__note-group", "aria-label": "Summary", children: [
@@ -10216,16 +10491,16 @@ function PreJoinBody({
10216
10491
  const recording = useRecording();
10217
10492
  const effects = useMediaEffects();
10218
10493
  const { notice } = useModerationNotice();
10219
- const [mode, setMode] = (0, import_react5.useState)("video");
10220
- const [microphone, setMicrophone] = (0, import_react5.useState)(true);
10221
- const [joining, setJoining] = (0, import_react5.useState)(false);
10494
+ const [mode, setMode] = (0, import_react6.useState)("video");
10495
+ const [microphone, setMicrophone] = (0, import_react6.useState)(true);
10496
+ const [joining, setJoining] = (0, import_react6.useState)(false);
10222
10497
  const camera = devices?.selected.videoinput ?? host.devices.resolve("videoinput");
10223
10498
  const mic = devices?.selected.audioinput ?? host.devices.resolve("audioinput");
10224
10499
  const preview = useCameraPreview(mode === "video" ? camera : void 0, effects.effects);
10225
10500
  const micStream = useMicrophoneStream(microphone ? mic : "off");
10226
10501
  const level = useAudioLevel(micStream);
10227
- const [speakerTesting, setSpeakerTesting] = (0, import_react5.useState)(false);
10228
- (0, import_react5.useEffect)(() => {
10502
+ const [speakerTesting, setSpeakerTesting] = (0, import_react6.useState)(false);
10503
+ (0, import_react6.useEffect)(() => {
10229
10504
  void host.devices.refresh();
10230
10505
  }, [host]);
10231
10506
  return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "mx-meet__prejoin", children: [
@@ -10341,12 +10616,12 @@ function PreJoinBody({
10341
10616
  ] });
10342
10617
  }
10343
10618
  function useCameraPreview(deviceId2, effects) {
10344
- const [preview, setPreview] = (0, import_react5.useState)(null);
10345
- const [error, setError] = (0, import_react5.useState)(null);
10346
- const current = (0, import_react5.useRef)(null);
10347
- const initialEffect = (0, import_react5.useRef)(toVideoEffect(effects));
10619
+ const [preview, setPreview] = (0, import_react6.useState)(null);
10620
+ const [error, setError] = (0, import_react6.useState)(null);
10621
+ const current = (0, import_react6.useRef)(null);
10622
+ const initialEffect = (0, import_react6.useRef)(toVideoEffect(effects));
10348
10623
  initialEffect.current = toVideoEffect(effects);
10349
- (0, import_react5.useEffect)(() => {
10624
+ (0, import_react6.useEffect)(() => {
10350
10625
  if (deviceId2 === void 0) return void 0;
10351
10626
  let live = true;
10352
10627
  setError(null);
@@ -10432,16 +10707,16 @@ function MeetingRoomBody({
10432
10707
  const isHost = useIsHost();
10433
10708
  const elapsed = useElapsed(snapshot?.meeting?.startedAt ?? null);
10434
10709
  const phase = snapshot?.phase ?? "idle";
10435
- (0, import_react5.useEffect)(() => {
10710
+ (0, import_react6.useEffect)(() => {
10436
10711
  host.meetings.watch(roomName);
10437
10712
  }, [host, roomName]);
10438
- (0, import_react5.useEffect)(() => {
10713
+ (0, import_react6.useEffect)(() => {
10439
10714
  if (given === void 0) return;
10440
10715
  if (host.store.snapshot().meeting !== null) return;
10441
10716
  host.store.patch({ meeting: given, locked: given.locked });
10442
10717
  }, [host, given]);
10443
10718
  const watchedId = meetingId ?? snapshot?.meeting?.id ?? null;
10444
- (0, import_react5.useEffect)(() => {
10719
+ (0, import_react6.useEffect)(() => {
10445
10720
  if (watchedId === null) return;
10446
10721
  host.meetings.watchMeeting(asMeetingId(watchedId));
10447
10722
  }, [host, watchedId]);
@@ -10486,8 +10761,8 @@ function MeetingRoomBody({
10486
10761
  ] });
10487
10762
  }
10488
10763
  function WaitingRoom({ roomName, host }) {
10489
- const knocked = (0, import_react5.useRef)(false);
10490
- (0, import_react5.useEffect)(() => {
10764
+ const knocked = (0, import_react6.useRef)(false);
10765
+ (0, import_react6.useEffect)(() => {
10491
10766
  if (knocked.current) return;
10492
10767
  knocked.current = true;
10493
10768
  host.meetings.knock(roomName);
@@ -10506,7 +10781,7 @@ function HostMenu() {
10506
10781
  const host = useRequiredMeetHost();
10507
10782
  const snapshot = useMeetSnapshot();
10508
10783
  const end = useEndMeeting();
10509
- const [confirmingEnd, setConfirmingEnd] = (0, import_react5.useState)(false);
10784
+ const [confirmingEnd, setConfirmingEnd] = (0, import_react6.useState)(false);
10510
10785
  const locked = snapshot?.locked === true;
10511
10786
  return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "mx-meet__host-menu", children: [
10512
10787
  /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
@@ -10612,13 +10887,13 @@ function ScheduleMeetingBody({
10612
10887
  onScheduled,
10613
10888
  host
10614
10889
  }) {
10615
- const [title, setTitle] = (0, import_react5.useState)("");
10616
- const [when2, setWhen] = (0, import_react5.useState)("");
10617
- const [kind, setKind] = (0, import_react5.useState)("scheduled");
10618
- const [busy, setBusy] = (0, import_react5.useState)(false);
10619
- const [link, setLink] = (0, import_react5.useState)(null);
10620
- const [error, setError] = (0, import_react5.useState)(null);
10621
- const disabled = (0, import_react5.useMemo)(() => title.trim().length === 0 || busy, [title, busy]);
10890
+ const [title, setTitle] = (0, import_react6.useState)("");
10891
+ const [when2, setWhen] = (0, import_react6.useState)("");
10892
+ const [kind, setKind] = (0, import_react6.useState)("scheduled");
10893
+ const [busy, setBusy] = (0, import_react6.useState)(false);
10894
+ const [link, setLink] = (0, import_react6.useState)(null);
10895
+ const [error, setError] = (0, import_react6.useState)(null);
10896
+ const disabled = (0, import_react6.useMemo)(() => title.trim().length === 0 || busy, [title, busy]);
10622
10897
  return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
10623
10898
  "form",
10624
10899
  {
@@ -10755,8 +11030,8 @@ function MeetingRecordBody({
10755
11030
  }) {
10756
11031
  const host = useRequiredMeetHost();
10757
11032
  const record = useMeetingRecord(meeting);
10758
- const [question, setQuestion] = (0, import_react5.useState)("");
10759
- const [tab, setTab] = (0, import_react5.useState)("summary");
11033
+ const [question, setQuestion] = (0, import_react6.useState)("");
11034
+ const [tab, setTab] = (0, import_react6.useState)("summary");
10760
11035
  if (record.failure !== null) {
10761
11036
  return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "mx-meet mx-meet--record", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_design_system.ErrorBox, { as: "div", className: "mx-meet__empty", error: record.failure, operation: "Open this meeting record", origin: "meet/components", children: [
10762
11037
  /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { className: "mx-meet__empty-title", children: "This meeting record could not be opened" }),
@@ -11011,10 +11286,10 @@ function RecordingPlayer(props) {
11011
11286
  return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(RecordingPlayerBody, { ...props, host });
11012
11287
  }
11013
11288
  function RecordingPlayerBody({ fileId, host }) {
11014
- const [media, setMedia] = (0, import_react5.useState)(null);
11015
- const [loading, setLoading] = (0, import_react5.useState)(false);
11016
- const [error, setError] = (0, import_react5.useState)(null);
11017
- (0, import_react5.useEffect)(() => {
11289
+ const [media, setMedia] = (0, import_react6.useState)(null);
11290
+ const [loading, setLoading] = (0, import_react6.useState)(false);
11291
+ const [error, setError] = (0, import_react6.useState)(null);
11292
+ (0, import_react6.useEffect)(() => {
11018
11293
  if (media === null || media.kind !== "bytes") return void 0;
11019
11294
  const url = media.url;
11020
11295
  return () => URL.revokeObjectURL(url);