@ai-matrx/meet 0.7.7 → 0.7.9
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/CHANGELOG.md +51 -0
- package/dist/index.cjs +145 -56
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +80 -50
- package/dist/index.d.ts +80 -50
- package/dist/index.js +145 -56
- package/dist/index.js.map +1 -1
- package/dist/react.cjs +842 -706
- package/dist/react.cjs.map +1 -1
- package/dist/react.d.cts +1 -1
- package/dist/react.d.ts +1 -1
- package/dist/react.js +818 -682
- package/dist/react.js.map +1 -1
- package/dist/styles.css +18 -0
- package/package.json +1 -1
package/dist/react.cjs
CHANGED
|
@@ -148,6 +148,7 @@ __export(react_exports, {
|
|
|
148
148
|
attendanceCsv: () => attendanceCsv,
|
|
149
149
|
attendanceReport: () => attendanceReport,
|
|
150
150
|
attendanceTotals: () => attendanceTotals,
|
|
151
|
+
attendeeKey: () => attendeeKey,
|
|
151
152
|
callInviteAction: () => callInviteAction,
|
|
152
153
|
callInviteText: () => callInviteText,
|
|
153
154
|
callStateLabel: () => callStateLabel,
|
|
@@ -251,6 +252,7 @@ __export(react_exports, {
|
|
|
251
252
|
summarizeMeetingRecordRestrictions: () => summarizeMeetingRecordRestrictions,
|
|
252
253
|
transcriptDocument: () => transcriptDocument,
|
|
253
254
|
unavailableFrom: () => unavailableFrom,
|
|
255
|
+
uniqueAttendees: () => uniqueAttendees,
|
|
254
256
|
useAudioLevel: () => useAudioLevel,
|
|
255
257
|
useCalls: () => useCalls,
|
|
256
258
|
useCanModerate: () => useCanModerate,
|
|
@@ -319,6 +321,20 @@ var MeetError = class extends Error {
|
|
|
319
321
|
return this.code === "permission-denied" || this.code === "device-not-found" || this.code === "device-in-use" || this.code === "insecure-context";
|
|
320
322
|
}
|
|
321
323
|
};
|
|
324
|
+
var SERVER_FAILED_SENTENCE = "The server hit an unexpected problem and nothing was changed.";
|
|
325
|
+
function isRawServerText(value) {
|
|
326
|
+
const text3 = value.trim();
|
|
327
|
+
return /^internal server error\b/i.test(text3) || /^[A-Z][A-Za-z0-9]*(Error|Exception)$/.test(text3) || /^[a-z][a-z0-9]*(_[a-z0-9]+)+$/.test(text3);
|
|
328
|
+
}
|
|
329
|
+
function humanDatabaseSentence(message) {
|
|
330
|
+
const text3 = message.trim();
|
|
331
|
+
if (text3.length === 0 || isRawServerText(text3)) return null;
|
|
332
|
+
if (/permission denied for|violates|relation "|does not exist|syntax error|column "|jwt|pgrst|duplicate key|null value in/i.test(
|
|
333
|
+
text3
|
|
334
|
+
))
|
|
335
|
+
return null;
|
|
336
|
+
return text3;
|
|
337
|
+
}
|
|
322
338
|
var FORBIDDEN_CODES = /* @__PURE__ */ new Set(["42501", "PGRST301", "PGRST302"]);
|
|
323
339
|
var NOT_FOUND_CODES = /* @__PURE__ */ new Set(["PGRST116", "PGRST205"]);
|
|
324
340
|
var CONFLICT_CODES = /* @__PURE__ */ new Set(["23505", "40001", "55000"]);
|
|
@@ -346,8 +362,8 @@ function normalizeMeetError(error, operation) {
|
|
|
346
362
|
if (code !== void 0 && FORBIDDEN_CODES.has(code)) {
|
|
347
363
|
return new MeetError(
|
|
348
364
|
"forbidden",
|
|
349
|
-
|
|
350
|
-
"
|
|
365
|
+
humanDatabaseSentence(message) ?? "You don't have access to do that in this meeting.",
|
|
366
|
+
"Ask the meeting's host to do it, or to give you access.",
|
|
351
367
|
error
|
|
352
368
|
);
|
|
353
369
|
}
|
|
@@ -362,15 +378,16 @@ function normalizeMeetError(error, operation) {
|
|
|
362
378
|
if (code !== void 0 && CONFLICT_CODES.has(code)) {
|
|
363
379
|
return new MeetError(
|
|
364
380
|
"conflict",
|
|
365
|
-
|
|
366
|
-
"
|
|
381
|
+
humanDatabaseSentence(message) ?? "This meeting changed a moment ago, so nothing was saved.",
|
|
382
|
+
"Reload to see the latest, then try again.",
|
|
367
383
|
error
|
|
368
384
|
);
|
|
369
385
|
}
|
|
386
|
+
const hint = error?.hint;
|
|
370
387
|
return new MeetError(
|
|
371
388
|
"transport",
|
|
372
|
-
|
|
373
|
-
|
|
389
|
+
/failed to fetch|networkerror|load failed|network request failed/i.test(message) ? "The server could not be reached." : humanDatabaseSentence(message) ?? SERVER_FAILED_SENTENCE,
|
|
390
|
+
typeof hint === "string" && humanDatabaseSentence(hint) !== null ? hint.trim() : "Nothing was changed \u2014 retry in a moment; if it keeps happening, reload the page.",
|
|
374
391
|
error
|
|
375
392
|
);
|
|
376
393
|
}
|
|
@@ -778,6 +795,24 @@ function readText(data) {
|
|
|
778
795
|
}
|
|
779
796
|
return "";
|
|
780
797
|
}
|
|
798
|
+
async function refusalFromResponse(response, operation, fallbackRemedy) {
|
|
799
|
+
let body = null;
|
|
800
|
+
try {
|
|
801
|
+
body = await response.json();
|
|
802
|
+
} catch {
|
|
803
|
+
body = null;
|
|
804
|
+
}
|
|
805
|
+
const said = readServerRefusal(body);
|
|
806
|
+
const code = response.status === 403 || response.status === 401 ? "forbidden" : response.status === 404 ? "not-found" : response.status === 409 ? "conflict" : "transport";
|
|
807
|
+
const error = new MeetError(
|
|
808
|
+
code,
|
|
809
|
+
said?.reason ?? SERVER_FAILED_SENTENCE,
|
|
810
|
+
said !== null && said.remedy.length > 0 ? said.remedy : fallbackRemedy
|
|
811
|
+
);
|
|
812
|
+
error.operation = operation;
|
|
813
|
+
error.status = response.status;
|
|
814
|
+
return error;
|
|
815
|
+
}
|
|
781
816
|
function readServerRefusal(body) {
|
|
782
817
|
if (typeof body === "string") {
|
|
783
818
|
return body.trim().length === 0 ? null : { reason: body.trim(), remedy: "" };
|
|
@@ -796,7 +831,8 @@ function readServerRefusal(body) {
|
|
|
796
831
|
return messages.length === 0 ? null : { reason: messages.join("; "), remedy: "" };
|
|
797
832
|
}
|
|
798
833
|
if (typeof detail === "object" && detail !== null) return readServerRefusal(detail);
|
|
799
|
-
const
|
|
834
|
+
const humanOrNull = (value) => typeof value === "string" && value.trim().length > 0 && !isRawServerText(value) ? value.trim() : null;
|
|
835
|
+
const reason = humanOrNull(row["reason"]) ?? humanOrNull(row["message"]) ?? humanOrNull(row["user_message"]) ?? (typeof detail === "string" ? humanOrNull(detail) : null) ?? (typeof row["message"] === "string" || typeof row["error"] === "string" || typeof detail === "string" ? SERVER_FAILED_SENTENCE : null);
|
|
800
836
|
if (reason === null) return null;
|
|
801
837
|
const remedy = typeof row["remedy"] === "string" ? row["remedy"] : "";
|
|
802
838
|
if (remedy.length > 0) return { reason, remedy };
|
|
@@ -1850,6 +1886,7 @@ var EMPTY = {
|
|
|
1850
1886
|
spotlight: null,
|
|
1851
1887
|
moderationNotice: null,
|
|
1852
1888
|
actionNotice: null,
|
|
1889
|
+
privateChatRequest: null,
|
|
1853
1890
|
effects: DEFAULT_EFFECTS,
|
|
1854
1891
|
transcript: [],
|
|
1855
1892
|
notes: [],
|
|
@@ -4039,6 +4076,10 @@ function createRoomEngine(options) {
|
|
|
4039
4076
|
}
|
|
4040
4077
|
},
|
|
4041
4078
|
disconnected(reason) {
|
|
4079
|
+
if (current !== null) {
|
|
4080
|
+
tokens.invalidate(current.roomName);
|
|
4081
|
+
current = null;
|
|
4082
|
+
}
|
|
4042
4083
|
store.patch({ phase: "left" });
|
|
4043
4084
|
options.onLeft?.(reason);
|
|
4044
4085
|
}
|
|
@@ -4341,19 +4382,8 @@ function createInRoomClient(options) {
|
|
|
4341
4382
|
const token = options.resolveRoomToken();
|
|
4342
4383
|
return token === null ? void 0 : { [ROOM_TOKEN_HEADER]: token };
|
|
4343
4384
|
}
|
|
4344
|
-
|
|
4345
|
-
|
|
4346
|
-
try {
|
|
4347
|
-
body = await response.json();
|
|
4348
|
-
} catch {
|
|
4349
|
-
body = null;
|
|
4350
|
-
}
|
|
4351
|
-
const said = readServerRefusal(body);
|
|
4352
|
-
return new MeetError(
|
|
4353
|
-
response.status === 403 || response.status === 401 ? "forbidden" : response.status === 404 ? "not-found" : response.status === 409 ? "conflict" : "transport",
|
|
4354
|
-
said?.reason ?? `${operation}: the server answered ${response.status}`,
|
|
4355
|
-
said?.remedy !== void 0 && said.remedy.length > 0 ? said.remedy : remedy
|
|
4356
|
-
);
|
|
4385
|
+
function refusal2(response, operation, remedy) {
|
|
4386
|
+
return refusalFromResponse(response, operation, remedy);
|
|
4357
4387
|
}
|
|
4358
4388
|
return {
|
|
4359
4389
|
async moderate(request) {
|
|
@@ -6313,216 +6343,617 @@ function useGuestRecordAccess(meeting) {
|
|
|
6313
6343
|
// src/react/components.tsx
|
|
6314
6344
|
var import_react4 = require("react");
|
|
6315
6345
|
var import_design_system = require("@ai-matrx/design-system");
|
|
6316
|
-
var
|
|
6346
|
+
var import_format3 = require("@ai-matrx/kit/format");
|
|
6317
6347
|
|
|
6318
|
-
// src/core/
|
|
6319
|
-
|
|
6320
|
-
|
|
6321
|
-
if (
|
|
6322
|
-
if (
|
|
6323
|
-
if (
|
|
6324
|
-
|
|
6325
|
-
|
|
6326
|
-
function callStateLabel(state, outgoing) {
|
|
6327
|
-
switch (state) {
|
|
6328
|
-
case "dialing":
|
|
6329
|
-
case "ringing":
|
|
6330
|
-
return outgoing ? "Calling\u2026" : "Incoming call";
|
|
6331
|
-
case "connecting":
|
|
6332
|
-
return "Connecting\u2026";
|
|
6333
|
-
case "active":
|
|
6334
|
-
return "In call";
|
|
6335
|
-
case "declined":
|
|
6336
|
-
return outgoing ? "Declined" : "You declined";
|
|
6337
|
-
case "missed":
|
|
6338
|
-
return outgoing ? "No answer" : "Missed call";
|
|
6339
|
-
case "busy":
|
|
6340
|
-
return outgoing ? "On another call" : "You were busy";
|
|
6341
|
-
case "cancelled":
|
|
6342
|
-
return outgoing ? "Cancelled" : "Cancelled by caller";
|
|
6343
|
-
case "ended":
|
|
6344
|
-
return "Call ended";
|
|
6345
|
-
case "failed":
|
|
6346
|
-
return "Call failed";
|
|
6347
|
-
default:
|
|
6348
|
-
return "Call";
|
|
6349
|
-
}
|
|
6348
|
+
// src/core/after.ts
|
|
6349
|
+
var import_format2 = require("@ai-matrx/kit/format");
|
|
6350
|
+
function kindOf(person) {
|
|
6351
|
+
if (person.isAgent) return "assistant";
|
|
6352
|
+
if (person.role === "host") return "host";
|
|
6353
|
+
if (person.role === "cohost") return "cohost";
|
|
6354
|
+
if (person.role === "guest") return person.userId !== null ? "signed-in guest" : "guest";
|
|
6355
|
+
return "member";
|
|
6350
6356
|
}
|
|
6351
|
-
function
|
|
6352
|
-
|
|
6357
|
+
function ms(iso) {
|
|
6358
|
+
if (iso === null) return null;
|
|
6359
|
+
const at = new Date(iso).getTime();
|
|
6360
|
+
return Number.isNaN(at) ? null : at;
|
|
6353
6361
|
}
|
|
6354
|
-
|
|
6355
|
-
|
|
6356
|
-
|
|
6357
|
-
|
|
6358
|
-
return
|
|
6359
|
-
|
|
6360
|
-
|
|
6361
|
-
|
|
6362
|
-
|
|
6363
|
-
|
|
6364
|
-
|
|
6365
|
-
|
|
6366
|
-
|
|
6367
|
-
|
|
6368
|
-
strokeLinejoin: "round",
|
|
6369
|
-
"aria-hidden": "true",
|
|
6370
|
-
focusable: "false",
|
|
6371
|
-
...props,
|
|
6372
|
-
children
|
|
6362
|
+
function attendeeKey(person, names = {}) {
|
|
6363
|
+
if (person.userId !== null) return `user:${person.userId}`;
|
|
6364
|
+
if (person.isAgent) return `agent:${person.identity}`;
|
|
6365
|
+
const name = displayNameFor(names, person.identity, person.displayName).trim().toLowerCase();
|
|
6366
|
+
return name.length > 0 ? `guest-name:${name}` : `identity:${person.identity}`;
|
|
6367
|
+
}
|
|
6368
|
+
function uniqueAttendees(attendees, names = {}) {
|
|
6369
|
+
const byKey = /* @__PURE__ */ new Map();
|
|
6370
|
+
for (const person of attendees) {
|
|
6371
|
+
const key = attendeeKey(person, names);
|
|
6372
|
+
const kept = byKey.get(key);
|
|
6373
|
+
if (kept === void 0) {
|
|
6374
|
+
byKey.set(key, person);
|
|
6375
|
+
continue;
|
|
6373
6376
|
}
|
|
6374
|
-
|
|
6375
|
-
|
|
6376
|
-
|
|
6377
|
-
|
|
6378
|
-
|
|
6379
|
-
|
|
6380
|
-
|
|
6381
|
-
|
|
6377
|
+
const keptJoin = ms(kept.joinedAt);
|
|
6378
|
+
const join = ms(person.joinedAt);
|
|
6379
|
+
if (join !== null && (keptJoin === null || join < keptJoin)) byKey.set(key, person);
|
|
6380
|
+
}
|
|
6381
|
+
return [...byKey.values()];
|
|
6382
|
+
}
|
|
6383
|
+
var KIND_RANK = {
|
|
6384
|
+
host: 0,
|
|
6385
|
+
cohost: 1,
|
|
6386
|
+
member: 2,
|
|
6387
|
+
"signed-in guest": 3,
|
|
6388
|
+
guest: 4,
|
|
6389
|
+
assistant: 5
|
|
6390
|
+
};
|
|
6391
|
+
function coveredMs(intervals) {
|
|
6392
|
+
const sorted = [...intervals].sort((a, b) => a[0] - b[0]);
|
|
6393
|
+
let total = 0;
|
|
6394
|
+
let from = -Infinity;
|
|
6395
|
+
let to = -Infinity;
|
|
6396
|
+
for (const [start, end] of sorted) {
|
|
6397
|
+
if (start > to) {
|
|
6398
|
+
if (to > from) total += to - from;
|
|
6399
|
+
from = start;
|
|
6400
|
+
to = end;
|
|
6401
|
+
} else if (end > to) {
|
|
6402
|
+
to = end;
|
|
6403
|
+
}
|
|
6404
|
+
}
|
|
6405
|
+
if (to > from) total += to - from;
|
|
6406
|
+
return total;
|
|
6382
6407
|
}
|
|
6383
|
-
function
|
|
6384
|
-
|
|
6385
|
-
|
|
6386
|
-
|
|
6387
|
-
|
|
6388
|
-
|
|
6389
|
-
|
|
6390
|
-
|
|
6408
|
+
function attendanceReport(meeting, attendees, names = {}) {
|
|
6409
|
+
const end = ms(meeting.endedAt);
|
|
6410
|
+
const perJoin = attendees.map((person) => {
|
|
6411
|
+
const joined = ms(person.joinedAt);
|
|
6412
|
+
const left = ms(person.leftAt);
|
|
6413
|
+
const until = left ?? end;
|
|
6414
|
+
const duration = joined !== null && until !== null && until >= joined ? until - joined : null;
|
|
6415
|
+
return {
|
|
6416
|
+
id: person.id,
|
|
6417
|
+
identity: person.identity,
|
|
6418
|
+
name: displayNameFor(names, person.identity, person.displayName),
|
|
6419
|
+
userId: person.userId,
|
|
6420
|
+
kind: kindOf(person),
|
|
6421
|
+
joinedAt: person.joinedAt,
|
|
6422
|
+
leftAt: person.leftAt,
|
|
6423
|
+
durationMs: duration,
|
|
6424
|
+
durationToEnd: duration !== null && left === null,
|
|
6425
|
+
admission: person.admissionState,
|
|
6426
|
+
joins: 1
|
|
6427
|
+
};
|
|
6428
|
+
});
|
|
6429
|
+
const groups = /* @__PURE__ */ new Map();
|
|
6430
|
+
attendees.forEach((person, index) => {
|
|
6431
|
+
const key = attendeeKey(person, names);
|
|
6432
|
+
const group = groups.get(key) ?? { rows: [], people: [] };
|
|
6433
|
+
group.rows.push(perJoin[index]);
|
|
6434
|
+
group.people.push(person);
|
|
6435
|
+
groups.set(key, group);
|
|
6436
|
+
});
|
|
6437
|
+
const rows = [...groups.values()].map(({ rows: joins }) => {
|
|
6438
|
+
if (joins.length === 1) return joins[0];
|
|
6439
|
+
const came = joins.filter((row) => row.joinedAt !== null);
|
|
6440
|
+
const first = [...came].sort((a, b) => (ms(a.joinedAt) ?? 0) - (ms(b.joinedAt) ?? 0))[0] ?? joins[0];
|
|
6441
|
+
const kind = joins.reduce(
|
|
6442
|
+
(best, row) => KIND_RANK[row.kind] < KIND_RANK[best] ? row.kind : best,
|
|
6443
|
+
first.kind
|
|
6444
|
+
);
|
|
6445
|
+
const open = came.some((row) => row.leftAt === null);
|
|
6446
|
+
const lastLeave = came.reduce((latest, row) => {
|
|
6447
|
+
const left = ms(row.leftAt);
|
|
6448
|
+
return left !== null && (latest === null || left > latest) ? left : latest;
|
|
6449
|
+
}, null);
|
|
6450
|
+
const intervals = came.flatMap((row) => {
|
|
6451
|
+
const joined = ms(row.joinedAt);
|
|
6452
|
+
return joined !== null && row.durationMs !== null ? [[joined, joined + row.durationMs]] : [];
|
|
6453
|
+
});
|
|
6454
|
+
return {
|
|
6455
|
+
...first,
|
|
6456
|
+
kind,
|
|
6457
|
+
leftAt: open ? null : lastLeave === null ? null : new Date(lastLeave).toISOString(),
|
|
6458
|
+
durationMs: intervals.length === 0 ? null : coveredMs(intervals),
|
|
6459
|
+
durationToEnd: came.some((row) => row.durationToEnd),
|
|
6460
|
+
joins: came.length === 0 ? joins.length : came.length
|
|
6461
|
+
};
|
|
6462
|
+
});
|
|
6463
|
+
const rank = KIND_RANK;
|
|
6464
|
+
return rows.sort((a, b) => {
|
|
6465
|
+
if (rank[a.kind] !== rank[b.kind]) return rank[a.kind] - rank[b.kind];
|
|
6466
|
+
return (ms(a.joinedAt) ?? Number.MAX_SAFE_INTEGER) - (ms(b.joinedAt) ?? Number.MAX_SAFE_INTEGER);
|
|
6467
|
+
});
|
|
6391
6468
|
}
|
|
6392
|
-
function
|
|
6393
|
-
|
|
6394
|
-
|
|
6395
|
-
|
|
6396
|
-
|
|
6469
|
+
function attendanceTotals(rows) {
|
|
6470
|
+
const humans = rows.filter((row) => row.kind !== "assistant");
|
|
6471
|
+
const attended = humans.filter((row) => row.joinedAt !== null);
|
|
6472
|
+
const timed = attended.filter((row) => row.durationMs !== null);
|
|
6473
|
+
return {
|
|
6474
|
+
people: attended.length,
|
|
6475
|
+
guests: attended.filter((row) => row.kind === "guest" || row.kind === "signed-in guest").length,
|
|
6476
|
+
neverAdmitted: humans.filter((row) => row.joinedAt === null).length,
|
|
6477
|
+
averageMs: timed.length === 0 ? null : Math.round(timed.reduce((sum, row) => sum + (row.durationMs ?? 0), 0) / timed.length)
|
|
6478
|
+
};
|
|
6397
6479
|
}
|
|
6398
|
-
function
|
|
6399
|
-
|
|
6400
|
-
|
|
6401
|
-
|
|
6402
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M16 16a2 2 0 0 1-2 2H3a2 2 0 0 1-2-2V7a2 2 0 0 1 2-2h2" })
|
|
6403
|
-
] });
|
|
6480
|
+
function csvCell(value) {
|
|
6481
|
+
if (value === null) return "";
|
|
6482
|
+
const text3 = String(value);
|
|
6483
|
+
return /[",\n\r]/.test(text3) ? `"${text3.replace(/"/g, '""')}"` : text3;
|
|
6404
6484
|
}
|
|
6405
|
-
|
|
6406
|
-
|
|
6407
|
-
|
|
6408
|
-
|
|
6409
|
-
|
|
6410
|
-
|
|
6485
|
+
var ATTENDANCE_CSV_HEADER = [
|
|
6486
|
+
"Meeting",
|
|
6487
|
+
"Meeting started",
|
|
6488
|
+
"Name",
|
|
6489
|
+
"Attended as",
|
|
6490
|
+
"Joined",
|
|
6491
|
+
"Left",
|
|
6492
|
+
"Minutes in the meeting",
|
|
6493
|
+
"Admission"
|
|
6494
|
+
];
|
|
6495
|
+
function attendanceCsv(reports) {
|
|
6496
|
+
const lines = [ATTENDANCE_CSV_HEADER.join(",")];
|
|
6497
|
+
for (const { meeting, rows } of reports) {
|
|
6498
|
+
for (const row of rows) {
|
|
6499
|
+
lines.push(
|
|
6500
|
+
[
|
|
6501
|
+
meeting.title,
|
|
6502
|
+
meeting.startedAt ?? meeting.scheduledFor,
|
|
6503
|
+
row.name,
|
|
6504
|
+
row.kind,
|
|
6505
|
+
row.joinedAt,
|
|
6506
|
+
row.leftAt,
|
|
6507
|
+
row.durationMs === null ? null : Math.round(row.durationMs / 6e3) / 10,
|
|
6508
|
+
row.admission
|
|
6509
|
+
].map(csvCell).join(",")
|
|
6510
|
+
);
|
|
6511
|
+
}
|
|
6512
|
+
}
|
|
6513
|
+
return `${lines.join("\r\n")}\r
|
|
6514
|
+
`;
|
|
6411
6515
|
}
|
|
6412
|
-
function
|
|
6413
|
-
|
|
6516
|
+
function recordingOffsetMs(at, recording) {
|
|
6517
|
+
if (recording === null || recording.state !== "available") return null;
|
|
6518
|
+
const start = ms(recording.startedAt);
|
|
6519
|
+
const moment = ms(at);
|
|
6520
|
+
if (start === null || moment === null || moment < start) return null;
|
|
6521
|
+
const end = ms(recording.endedAt);
|
|
6522
|
+
if (end !== null && moment > end) return null;
|
|
6523
|
+
return moment - start;
|
|
6414
6524
|
}
|
|
6415
|
-
function
|
|
6416
|
-
|
|
6417
|
-
|
|
6418
|
-
|
|
6419
|
-
|
|
6420
|
-
|
|
6525
|
+
function clockPosition(offsetMs) {
|
|
6526
|
+
const total = Math.max(0, Math.floor(offsetMs / 1e3));
|
|
6527
|
+
const h = Math.floor(total / 3600);
|
|
6528
|
+
const m = Math.floor(total % 3600 / 60);
|
|
6529
|
+
const s = total % 60;
|
|
6530
|
+
const ss = String(s).padStart(2, "0");
|
|
6531
|
+
return h > 0 ? `${h}:${String(m).padStart(2, "0")}:${ss}` : `${m}:${ss}`;
|
|
6421
6532
|
}
|
|
6422
|
-
function
|
|
6423
|
-
|
|
6424
|
-
|
|
6425
|
-
|
|
6426
|
-
|
|
6427
|
-
|
|
6428
|
-
|
|
6533
|
+
function searchTranscript(segments, query, names = {}) {
|
|
6534
|
+
const needle = fold(query.trim());
|
|
6535
|
+
if (needle.length === 0) return [];
|
|
6536
|
+
const out = [];
|
|
6537
|
+
for (const segment of segments) {
|
|
6538
|
+
const hay = fold(segment.text);
|
|
6539
|
+
const ranges = [];
|
|
6540
|
+
let from = 0;
|
|
6541
|
+
for (; ; ) {
|
|
6542
|
+
const at = hay.indexOf(needle, from);
|
|
6543
|
+
if (at < 0) break;
|
|
6544
|
+
ranges.push([at, at + needle.length]);
|
|
6545
|
+
from = at + needle.length;
|
|
6546
|
+
}
|
|
6547
|
+
const speaker = fold(displayNameFor(names, segment.identity, segment.speaker));
|
|
6548
|
+
if (ranges.length > 0 || speaker.includes(needle)) {
|
|
6549
|
+
out.push({ segmentId: segment.id, ranges });
|
|
6550
|
+
}
|
|
6551
|
+
}
|
|
6552
|
+
return out;
|
|
6429
6553
|
}
|
|
6430
|
-
function
|
|
6431
|
-
return
|
|
6554
|
+
function fold(value) {
|
|
6555
|
+
return value.normalize("NFD").replace(/[̀-ͯ]/g, (mark) => mark.length === 1 ? "" : mark).toLowerCase();
|
|
6432
6556
|
}
|
|
6433
|
-
function
|
|
6434
|
-
|
|
6435
|
-
|
|
6436
|
-
|
|
6437
|
-
|
|
6438
|
-
|
|
6439
|
-
|
|
6440
|
-
|
|
6441
|
-
|
|
6442
|
-
}
|
|
6443
|
-
function SparklesIcon(props) {
|
|
6444
|
-
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(Icon, { ...props, children: [
|
|
6445
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M12 3l1.9 4.6L18.5 9.5l-4.6 1.9L12 16l-1.9-4.6L5.5 9.5l4.6-1.9L12 3Z" }),
|
|
6446
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M19 15l.9 2.1 2.1.9-2.1.9-.9 2.1-.9-2.1-2.1-.9 2.1-.9L19 15Z" })
|
|
6447
|
-
] });
|
|
6448
|
-
}
|
|
6449
|
-
function LockIcon(props) {
|
|
6450
|
-
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(Icon, { ...props, children: [
|
|
6451
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("rect", { x: "4", y: "10", width: "16", height: "11", rx: "2" }),
|
|
6452
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M8 10V7a4 4 0 0 1 8 0v3" })
|
|
6453
|
-
] });
|
|
6454
|
-
}
|
|
6455
|
-
function LayoutIcon(props) {
|
|
6456
|
-
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(Icon, { ...props, children: [
|
|
6457
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("rect", { x: "3", y: "3", width: "18", height: "18", rx: "2" }),
|
|
6458
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M3 9h18M9 21V9" })
|
|
6459
|
-
] });
|
|
6557
|
+
function clockOfDay(iso, options) {
|
|
6558
|
+
const at = new Date(iso);
|
|
6559
|
+
if (Number.isNaN(at.getTime())) return "";
|
|
6560
|
+
return at.toLocaleTimeString(options.locale, {
|
|
6561
|
+
hour: "numeric",
|
|
6562
|
+
minute: "2-digit",
|
|
6563
|
+
second: "2-digit",
|
|
6564
|
+
...options.timeZone !== void 0 ? { timeZone: options.timeZone } : {}
|
|
6565
|
+
});
|
|
6460
6566
|
}
|
|
6461
|
-
function
|
|
6462
|
-
|
|
6463
|
-
|
|
6464
|
-
|
|
6465
|
-
|
|
6567
|
+
function vttStamp(offsetMs) {
|
|
6568
|
+
const total = Math.max(0, Math.round(offsetMs));
|
|
6569
|
+
const h = Math.floor(total / 36e5);
|
|
6570
|
+
const m = Math.floor(total % 36e5 / 6e4);
|
|
6571
|
+
const s = Math.floor(total % 6e4 / 1e3);
|
|
6572
|
+
const milli = total % 1e3;
|
|
6573
|
+
return `${String(h).padStart(2, "0")}:${String(m).padStart(2, "0")}:${String(s).padStart(2, "0")}.${String(milli).padStart(3, "0")}`;
|
|
6466
6574
|
}
|
|
6467
|
-
function
|
|
6468
|
-
|
|
6575
|
+
function transcriptDocument(bundle, format, options = {}) {
|
|
6576
|
+
const names = options.names ?? bundle.names;
|
|
6577
|
+
const lines = bundle.transcript.filter((line) => line.text.trim().length > 0);
|
|
6578
|
+
const speaker = (line) => displayNameFor(names, line.identity, line.speaker);
|
|
6579
|
+
if (format === "vtt") {
|
|
6580
|
+
const zeroIso = bundle.recording?.state === "available" && bundle.recording.startedAt !== null ? bundle.recording.startedAt : bundle.meeting.startedAt ?? lines[0]?.startedAt ?? null;
|
|
6581
|
+
const zero = ms(zeroIso) ?? 0;
|
|
6582
|
+
const cues = ["WEBVTT", ""];
|
|
6583
|
+
lines.forEach((line, index) => {
|
|
6584
|
+
const start = (ms(line.startedAt) ?? zero) - zero;
|
|
6585
|
+
if (start < 0) return;
|
|
6586
|
+
const next = lines[index + 1];
|
|
6587
|
+
const nextStart = next !== void 0 ? (ms(next.startedAt) ?? zero) - zero : start + 4e3;
|
|
6588
|
+
const end = Math.max(start + 500, Math.min(nextStart, start + 1e4));
|
|
6589
|
+
cues.push(`${vttStamp(start)} --> ${vttStamp(end)}`);
|
|
6590
|
+
cues.push(`<v ${speaker(line).replace(/[<>]/g, "")}>${line.text.replace(/-->/g, "->")}`);
|
|
6591
|
+
cues.push("");
|
|
6592
|
+
});
|
|
6593
|
+
return cues.join("\n");
|
|
6594
|
+
}
|
|
6595
|
+
const out = [];
|
|
6596
|
+
const title = bundle.meeting.title;
|
|
6597
|
+
out.push(format === "md" ? `# ${title} \u2014 transcript` : `${title} \u2014 transcript`);
|
|
6598
|
+
out.push("");
|
|
6599
|
+
let last = null;
|
|
6600
|
+
for (const line of lines) {
|
|
6601
|
+
if (line.identity !== last) {
|
|
6602
|
+
if (last !== null) out.push("");
|
|
6603
|
+
const who = speaker(line);
|
|
6604
|
+
const clock = clockOfDay(line.startedAt, options);
|
|
6605
|
+
out.push(format === "md" ? `**${who}** \xB7 ${clock}` : `${who} (${clock})`);
|
|
6606
|
+
last = line.identity;
|
|
6607
|
+
}
|
|
6608
|
+
out.push(line.text.trim());
|
|
6609
|
+
}
|
|
6610
|
+
if (lines.length === 0) out.push("No transcript was captured for this meeting.");
|
|
6611
|
+
return `${out.join("\n")}
|
|
6612
|
+
`;
|
|
6469
6613
|
}
|
|
6470
|
-
function
|
|
6471
|
-
|
|
6614
|
+
function bullet(note) {
|
|
6615
|
+
const owner = note.assigneeDisplayName !== null ? ` \u2014 ${note.assigneeDisplayName}` : "";
|
|
6616
|
+
return `- ${note.text.trim()}${owner}`;
|
|
6472
6617
|
}
|
|
6473
|
-
function
|
|
6474
|
-
|
|
6475
|
-
|
|
6476
|
-
|
|
6477
|
-
|
|
6618
|
+
function notesDocument(bundle, options = {}) {
|
|
6619
|
+
const out = [`# ${bundle.meeting.title}`, ""];
|
|
6620
|
+
if (options.when) out.push(`_${options.when}_`, "");
|
|
6621
|
+
const people = uniqueAttendees(
|
|
6622
|
+
bundle.attendees.filter((person) => !person.isAgent && person.joinedAt !== null),
|
|
6623
|
+
bundle.names
|
|
6624
|
+
).map((person) => displayNameFor(bundle.names, person.identity, person.displayName));
|
|
6625
|
+
if (people.length > 0) out.push(`**Attended:** ${people.join(", ")}`, "");
|
|
6626
|
+
out.push("## Summary", "", bundle.summary?.text.trim() || "No summary was written.", "");
|
|
6627
|
+
out.push("## Decisions", "");
|
|
6628
|
+
out.push(...bundle.decisions.length > 0 ? bundle.decisions.map(bullet) : ["None recorded."], "");
|
|
6629
|
+
out.push("## Action items", "");
|
|
6630
|
+
out.push(
|
|
6631
|
+
...bundle.actionItems.length > 0 ? bundle.actionItems.map(bullet) : ["None recorded."],
|
|
6632
|
+
""
|
|
6633
|
+
);
|
|
6634
|
+
if (options.link) out.push(`[Open the meeting record](${options.link})`, "");
|
|
6635
|
+
return out.join("\n");
|
|
6478
6636
|
}
|
|
6479
|
-
function
|
|
6480
|
-
|
|
6481
|
-
|
|
6482
|
-
|
|
6483
|
-
|
|
6637
|
+
function meetingLength(meeting) {
|
|
6638
|
+
const start = ms(meeting.startedAt);
|
|
6639
|
+
const end = ms(meeting.endedAt);
|
|
6640
|
+
if (start === null || end === null || end < start) return null;
|
|
6641
|
+
return (0, import_format2.formatDurationMs)(end - start, { style: "long" });
|
|
6484
6642
|
}
|
|
6485
|
-
function
|
|
6486
|
-
|
|
6487
|
-
|
|
6488
|
-
|
|
6489
|
-
|
|
6490
|
-
|
|
6491
|
-
|
|
6643
|
+
async function loadMeetingChat(api, meetingId, roomToken = null) {
|
|
6644
|
+
const operation = "meet.loadMeetingChat";
|
|
6645
|
+
let response;
|
|
6646
|
+
try {
|
|
6647
|
+
response = await api.request({
|
|
6648
|
+
path: `${MEET_ROUTES.chat}?meeting_id=${encodeURIComponent(meetingId)}`,
|
|
6649
|
+
method: "GET",
|
|
6650
|
+
operation,
|
|
6651
|
+
requireAuth: roomToken === null,
|
|
6652
|
+
...roomToken !== null ? { headers: { [ROOM_TOKEN_HEADER]: roomToken } } : {}
|
|
6653
|
+
});
|
|
6654
|
+
} catch (error) {
|
|
6655
|
+
return {
|
|
6656
|
+
messages: [],
|
|
6657
|
+
refusal: {
|
|
6658
|
+
message: `The chat could not be read (${error.message}).`,
|
|
6659
|
+
remedy: error.remedy ?? "Reload the page."
|
|
6660
|
+
}
|
|
6661
|
+
};
|
|
6662
|
+
}
|
|
6663
|
+
if (!response.ok) {
|
|
6664
|
+
let body = null;
|
|
6665
|
+
try {
|
|
6666
|
+
body = await response.json();
|
|
6667
|
+
} catch {
|
|
6668
|
+
body = null;
|
|
6669
|
+
}
|
|
6670
|
+
const said = readServerRefusal(body);
|
|
6671
|
+
return {
|
|
6672
|
+
messages: [],
|
|
6673
|
+
refusal: {
|
|
6674
|
+
message: said?.reason ?? `The chat could not be read (the server answered ${response.status}).`,
|
|
6675
|
+
remedy: said?.remedy || "Reload the page."
|
|
6676
|
+
}
|
|
6677
|
+
};
|
|
6678
|
+
}
|
|
6679
|
+
const payload = await response.json();
|
|
6680
|
+
const rows = Array.isArray(payload.messages) ? payload.messages : [];
|
|
6681
|
+
return {
|
|
6682
|
+
messages: rows.map(
|
|
6683
|
+
(row) => typeof row === "object" && row !== null ? chatFromRow(row) : null
|
|
6684
|
+
).filter((message) => message !== null),
|
|
6685
|
+
refusal: null
|
|
6686
|
+
};
|
|
6492
6687
|
}
|
|
6493
|
-
function
|
|
6494
|
-
return
|
|
6495
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("circle", { cx: "12", cy: "12", r: "1" }),
|
|
6496
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("circle", { cx: "19", cy: "12", r: "1" }),
|
|
6497
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("circle", { cx: "5", cy: "12", r: "1" })
|
|
6498
|
-
] });
|
|
6688
|
+
function refusalOf(response, operation) {
|
|
6689
|
+
return refusalFromResponse(response, operation, "Nothing was sent. Try again.");
|
|
6499
6690
|
}
|
|
6500
|
-
function
|
|
6501
|
-
|
|
6502
|
-
|
|
6503
|
-
|
|
6504
|
-
|
|
6691
|
+
function asRecipient(row) {
|
|
6692
|
+
const sources = Array.isArray(row["sources"]) ? row["sources"] : [];
|
|
6693
|
+
return {
|
|
6694
|
+
key: String(row["key"] ?? ""),
|
|
6695
|
+
name: String(row["name"] ?? ""),
|
|
6696
|
+
email: typeof row["email"] === "string" ? row["email"] : null,
|
|
6697
|
+
userId: typeof row["user_id"] === "string" ? row["user_id"] : null,
|
|
6698
|
+
sources: sources.filter((s) => s === "invitee" || s === "attendee"),
|
|
6699
|
+
reachable: row["reachable"] !== false
|
|
6700
|
+
};
|
|
6505
6701
|
}
|
|
6506
|
-
function
|
|
6507
|
-
|
|
6508
|
-
|
|
6509
|
-
|
|
6510
|
-
|
|
6702
|
+
async function loadFollowUpDraft(api, meetingId) {
|
|
6703
|
+
const operation = "meet.loadFollowUpDraft";
|
|
6704
|
+
const response = await api.request({
|
|
6705
|
+
path: meetingPath(MEET_ROUTES.meetingFollowUp, meetingId),
|
|
6706
|
+
method: "GET",
|
|
6707
|
+
requireAuth: true,
|
|
6708
|
+
operation
|
|
6709
|
+
});
|
|
6710
|
+
if (!response.ok) throw await refusalOf(response, operation);
|
|
6711
|
+
const body = await response.json();
|
|
6712
|
+
const last = body["last_sent"];
|
|
6713
|
+
return {
|
|
6714
|
+
subject: String(body["subject"] ?? ""),
|
|
6715
|
+
body: String(body["body"] ?? ""),
|
|
6716
|
+
recipients: (Array.isArray(body["recipients"]) ? body["recipients"] : []).map(
|
|
6717
|
+
(row) => asRecipient(row)
|
|
6718
|
+
),
|
|
6719
|
+
wrapUpWritten: body["wrap_up_written"] === true,
|
|
6720
|
+
lastSent: last && typeof last["at"] === "string" ? { at: last["at"], count: typeof last["count"] === "number" ? last["count"] : 0 } : null
|
|
6721
|
+
};
|
|
6511
6722
|
}
|
|
6512
|
-
function
|
|
6513
|
-
|
|
6723
|
+
async function sendFollowUp(api, meetingId, input) {
|
|
6724
|
+
const operation = "meet.sendFollowUp";
|
|
6725
|
+
const response = await api.request({
|
|
6726
|
+
path: meetingPath(MEET_ROUTES.meetingFollowUp, meetingId),
|
|
6727
|
+
method: "POST",
|
|
6728
|
+
requireAuth: true,
|
|
6729
|
+
operation,
|
|
6730
|
+
body: {
|
|
6731
|
+
subject: input.subject,
|
|
6732
|
+
body: input.body,
|
|
6733
|
+
recipient_keys: [...input.recipientKeys],
|
|
6734
|
+
dry_run: input.dryRun === true
|
|
6735
|
+
}
|
|
6736
|
+
});
|
|
6737
|
+
if (!response.ok) throw await refusalOf(response, operation);
|
|
6738
|
+
const body = await response.json();
|
|
6739
|
+
return {
|
|
6740
|
+
dryRun: body["dry_run"] === true,
|
|
6741
|
+
queued: Array.isArray(body["queued"]) ? body["queued"] : [],
|
|
6742
|
+
skipped: Array.isArray(body["skipped"]) ? body["skipped"].map((row) => ({
|
|
6743
|
+
key: String(row["key"] ?? ""),
|
|
6744
|
+
reason: String(row["reason"] ?? "")
|
|
6745
|
+
})) : []
|
|
6746
|
+
};
|
|
6514
6747
|
}
|
|
6515
|
-
|
|
6516
|
-
|
|
6517
|
-
|
|
6518
|
-
|
|
6519
|
-
|
|
6520
|
-
|
|
6521
|
-
] }
|
|
6748
|
+
|
|
6749
|
+
// src/core/format.ts
|
|
6750
|
+
function participantSummary(participants) {
|
|
6751
|
+
const names = participants.filter((p) => !p.isLocal).map((p) => p.displayName);
|
|
6752
|
+
if (names.length === 0) return "Just you";
|
|
6753
|
+
if (names.length === 1) return names[0] ?? "";
|
|
6754
|
+
if (names.length === 2) return `${names[0]} and ${names[1]}`;
|
|
6755
|
+
return `${names[0]}, ${names[1]} and ${names.length - 2} ${names.length - 2 === 1 ? "other" : "others"}`;
|
|
6522
6756
|
}
|
|
6523
|
-
function
|
|
6524
|
-
|
|
6525
|
-
|
|
6757
|
+
function callStateLabel(state, outgoing) {
|
|
6758
|
+
switch (state) {
|
|
6759
|
+
case "dialing":
|
|
6760
|
+
case "ringing":
|
|
6761
|
+
return outgoing ? "Calling\u2026" : "Incoming call";
|
|
6762
|
+
case "connecting":
|
|
6763
|
+
return "Connecting\u2026";
|
|
6764
|
+
case "active":
|
|
6765
|
+
return "In call";
|
|
6766
|
+
case "declined":
|
|
6767
|
+
return outgoing ? "Declined" : "You declined";
|
|
6768
|
+
case "missed":
|
|
6769
|
+
return outgoing ? "No answer" : "Missed call";
|
|
6770
|
+
case "busy":
|
|
6771
|
+
return outgoing ? "On another call" : "You were busy";
|
|
6772
|
+
case "cancelled":
|
|
6773
|
+
return outgoing ? "Cancelled" : "Cancelled by caller";
|
|
6774
|
+
case "ended":
|
|
6775
|
+
return "Call ended";
|
|
6776
|
+
case "failed":
|
|
6777
|
+
return "Call failed";
|
|
6778
|
+
default:
|
|
6779
|
+
return "Call";
|
|
6780
|
+
}
|
|
6781
|
+
}
|
|
6782
|
+
function countLabel(count2, singular, plural = `${singular}s`) {
|
|
6783
|
+
return `${count2} ${count2 === 1 ? singular : plural}`;
|
|
6784
|
+
}
|
|
6785
|
+
|
|
6786
|
+
// src/react/icons.tsx
|
|
6787
|
+
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
6788
|
+
function Icon({ children, ...props }) {
|
|
6789
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
6790
|
+
"svg",
|
|
6791
|
+
{
|
|
6792
|
+
width: "20",
|
|
6793
|
+
height: "20",
|
|
6794
|
+
viewBox: "0 0 24 24",
|
|
6795
|
+
fill: "none",
|
|
6796
|
+
stroke: "currentColor",
|
|
6797
|
+
strokeWidth: "1.75",
|
|
6798
|
+
strokeLinecap: "round",
|
|
6799
|
+
strokeLinejoin: "round",
|
|
6800
|
+
"aria-hidden": "true",
|
|
6801
|
+
focusable: "false",
|
|
6802
|
+
...props,
|
|
6803
|
+
children
|
|
6804
|
+
}
|
|
6805
|
+
);
|
|
6806
|
+
}
|
|
6807
|
+
function MicIcon(props) {
|
|
6808
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(Icon, { ...props, children: [
|
|
6809
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M12 2a3 3 0 0 0-3 3v7a3 3 0 0 0 6 0V5a3 3 0 0 0-3-3Z" }),
|
|
6810
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M19 10v2a7 7 0 0 1-14 0v-2" }),
|
|
6811
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M12 19v3" })
|
|
6812
|
+
] });
|
|
6813
|
+
}
|
|
6814
|
+
function MicOffIcon(props) {
|
|
6815
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(Icon, { ...props, children: [
|
|
6816
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M2 2l20 20" }),
|
|
6817
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M9 9v3a3 3 0 0 0 5.12 2.12" }),
|
|
6818
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M15 9.34V5a3 3 0 0 0-5.94-.6" }),
|
|
6819
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M19 10v2a7 7 0 0 1-.11 1.23M5 10v2a7 7 0 0 0 11 5.66" }),
|
|
6820
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M12 19v3" })
|
|
6821
|
+
] });
|
|
6822
|
+
}
|
|
6823
|
+
function CameraIcon(props) {
|
|
6824
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(Icon, { ...props, children: [
|
|
6825
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "m23 7-7 5 7 5V7Z" }),
|
|
6826
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("rect", { x: "1", y: "5", width: "15", height: "14", rx: "2" })
|
|
6827
|
+
] });
|
|
6828
|
+
}
|
|
6829
|
+
function CameraOffIcon(props) {
|
|
6830
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(Icon, { ...props, children: [
|
|
6831
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M2 2l20 20" }),
|
|
6832
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M10.66 5H14a2 2 0 0 1 2 2v3.34l1 1L23 7v10" }),
|
|
6833
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M16 16a2 2 0 0 1-2 2H3a2 2 0 0 1-2-2V7a2 2 0 0 1 2-2h2" })
|
|
6834
|
+
] });
|
|
6835
|
+
}
|
|
6836
|
+
function ScreenShareIcon(props) {
|
|
6837
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(Icon, { ...props, children: [
|
|
6838
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("rect", { x: "2", y: "3", width: "20", height: "14", rx: "2" }),
|
|
6839
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M8 21h8M12 17v4" }),
|
|
6840
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M12 13V8m0 0-2.5 2.5M12 8l2.5 2.5" })
|
|
6841
|
+
] });
|
|
6842
|
+
}
|
|
6843
|
+
function PhoneIcon(props) {
|
|
6844
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Icon, { ...props, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M22 16.92v3a2 2 0 0 1-2.18 2 19.79 19.79 0 0 1-8.63-3.07 19.5 19.5 0 0 1-6-6A19.79 19.79 0 0 1 2.12 4.18 2 2 0 0 1 4.11 2h3a2 2 0 0 1 2 1.72c.13.96.36 1.9.7 2.81a2 2 0 0 1-.45 2.11L8.09 9.91a16 16 0 0 0 6 6l1.27-1.27a2 2 0 0 1 2.11-.45c.9.34 1.85.57 2.81.7A2 2 0 0 1 22 16.92Z" }) });
|
|
6845
|
+
}
|
|
6846
|
+
function PhoneOffIcon(props) {
|
|
6847
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(Icon, { ...props, children: [
|
|
6848
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M2 2l20 20" }),
|
|
6849
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M10.68 13.31a16 16 0 0 0 3.41 2.6l1.27-1.27a2 2 0 0 1 2.11-.45c.9.34 1.85.57 2.81.7A2 2 0 0 1 22 16.92v3a2 2 0 0 1-2.18 2 19.79 19.79 0 0 1-8.63-3.07 19.42 19.42 0 0 1-3.33-2.67" }),
|
|
6850
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M5.53 5.53A19.79 19.79 0 0 1 2.12 4.18 2 2 0 0 1 4.11 2h3a2 2 0 0 1 2 1.72c.13.96.36 1.9.7 2.81a2 2 0 0 1-.45 2.11L8.09 9.91" })
|
|
6851
|
+
] });
|
|
6852
|
+
}
|
|
6853
|
+
function HandIcon(props) {
|
|
6854
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(Icon, { ...props, children: [
|
|
6855
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M18 11V6a2 2 0 0 0-4 0v5" }),
|
|
6856
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M14 10V4a2 2 0 0 0-4 0v7" }),
|
|
6857
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M10 10.5V6a2 2 0 0 0-4 0v8" }),
|
|
6858
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M18 8a2 2 0 1 1 4 0v6a8 8 0 0 1-8 8h-2a8 8 0 0 1-8-8v-1a2 2 0 1 1 4 0" })
|
|
6859
|
+
] });
|
|
6860
|
+
}
|
|
6861
|
+
function ChatIcon(props) {
|
|
6862
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Icon, { ...props, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2Z" }) });
|
|
6863
|
+
}
|
|
6864
|
+
function PeopleIcon(props) {
|
|
6865
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(Icon, { ...props, children: [
|
|
6866
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2" }),
|
|
6867
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("circle", { cx: "9", cy: "7", r: "4" }),
|
|
6868
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M22 21v-2a4 4 0 0 0-3-3.87M16 3.13a4 4 0 0 1 0 7.75" })
|
|
6869
|
+
] });
|
|
6870
|
+
}
|
|
6871
|
+
function RecordIcon(props) {
|
|
6872
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Icon, { ...props, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("circle", { cx: "12", cy: "12", r: "8", fill: "currentColor", stroke: "none" }) });
|
|
6873
|
+
}
|
|
6874
|
+
function SparklesIcon(props) {
|
|
6875
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(Icon, { ...props, children: [
|
|
6876
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M12 3l1.9 4.6L18.5 9.5l-4.6 1.9L12 16l-1.9-4.6L5.5 9.5l4.6-1.9L12 3Z" }),
|
|
6877
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M19 15l.9 2.1 2.1.9-2.1.9-.9 2.1-.9-2.1-2.1-.9 2.1-.9L19 15Z" })
|
|
6878
|
+
] });
|
|
6879
|
+
}
|
|
6880
|
+
function LockIcon(props) {
|
|
6881
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(Icon, { ...props, children: [
|
|
6882
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("rect", { x: "4", y: "10", width: "16", height: "11", rx: "2" }),
|
|
6883
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M8 10V7a4 4 0 0 1 8 0v3" })
|
|
6884
|
+
] });
|
|
6885
|
+
}
|
|
6886
|
+
function LayoutIcon(props) {
|
|
6887
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(Icon, { ...props, children: [
|
|
6888
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("rect", { x: "3", y: "3", width: "18", height: "18", rx: "2" }),
|
|
6889
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M3 9h18M9 21V9" })
|
|
6890
|
+
] });
|
|
6891
|
+
}
|
|
6892
|
+
function SettingsIcon(props) {
|
|
6893
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(Icon, { ...props, children: [
|
|
6894
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("circle", { cx: "12", cy: "12", r: "3" }),
|
|
6895
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 1 1-2.83 2.83l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 1 1-4 0v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 1 1-2.83-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 1 1 0-4h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 1 1 2.83-2.83l.06.06A1.65 1.65 0 0 0 9 4.6a1.65 1.65 0 0 0 1-1.51V3a2 2 0 1 1 4 0v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 1 1 2.83 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 1 1 0 4h-.09a1.65 1.65 0 0 0-1.51 1Z" })
|
|
6896
|
+
] });
|
|
6897
|
+
}
|
|
6898
|
+
function CheckIcon(props) {
|
|
6899
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Icon, { ...props, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M20 6 9 17l-5-5" }) });
|
|
6900
|
+
}
|
|
6901
|
+
function CloseIcon(props) {
|
|
6902
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Icon, { ...props, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M18 6 6 18M6 6l12 12" }) });
|
|
6903
|
+
}
|
|
6904
|
+
function PinIcon(props) {
|
|
6905
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(Icon, { ...props, children: [
|
|
6906
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M12 17v5" }),
|
|
6907
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M9 2h6l-1 6 3.5 3.5V14H6.5v-2.5L10 8 9 2Z" })
|
|
6908
|
+
] });
|
|
6909
|
+
}
|
|
6910
|
+
function WifiLowIcon(props) {
|
|
6911
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(Icon, { ...props, children: [
|
|
6912
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M12 20h.01" }),
|
|
6913
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M8.5 16.5a5 5 0 0 1 7 0" })
|
|
6914
|
+
] });
|
|
6915
|
+
}
|
|
6916
|
+
function SmileIcon(props) {
|
|
6917
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(Icon, { ...props, children: [
|
|
6918
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("circle", { cx: "12", cy: "12", r: "10" }),
|
|
6919
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M8 14s1.5 2 4 2 4-2 4-2" }),
|
|
6920
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("line", { x1: "9", x2: "9.01", y1: "9", y2: "9" }),
|
|
6921
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("line", { x1: "15", x2: "15.01", y1: "9", y2: "9" })
|
|
6922
|
+
] });
|
|
6923
|
+
}
|
|
6924
|
+
function MoreIcon(props) {
|
|
6925
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(Icon, { ...props, children: [
|
|
6926
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("circle", { cx: "12", cy: "12", r: "1" }),
|
|
6927
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("circle", { cx: "19", cy: "12", r: "1" }),
|
|
6928
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("circle", { cx: "5", cy: "12", r: "1" })
|
|
6929
|
+
] });
|
|
6930
|
+
}
|
|
6931
|
+
function SearchIcon(props) {
|
|
6932
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(Icon, { ...props, children: [
|
|
6933
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("circle", { cx: "11", cy: "11", r: "8" }),
|
|
6934
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "m21 21-4.3-4.3" })
|
|
6935
|
+
] });
|
|
6936
|
+
}
|
|
6937
|
+
function KeyboardIcon(props) {
|
|
6938
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(Icon, { ...props, children: [
|
|
6939
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M10 8h.01M12 12h.01M14 8h.01M16 12h.01M18 8h.01M6 8h.01M7 16h10M8 12h.01" }),
|
|
6940
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("rect", { width: "20", height: "16", x: "2", y: "4", rx: "2" })
|
|
6941
|
+
] });
|
|
6942
|
+
}
|
|
6943
|
+
function SpotlightIcon(props) {
|
|
6944
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Icon, { ...props, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M12 2 15 8.5 22 9.3 17 14 18.2 21 12 17.6 5.8 21 7 14 2 9.3 9 8.5Z" }) });
|
|
6945
|
+
}
|
|
6946
|
+
function SignalIcon({ bars, ...props }) {
|
|
6947
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(Icon, { ...props, children: [
|
|
6948
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M4 20v-2", opacity: bars >= 1 ? 1 : 0.3 }),
|
|
6949
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M10 20v-6", opacity: bars >= 2 ? 1 : 0.3 }),
|
|
6950
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M16 20V9", opacity: bars >= 3 ? 1 : 0.3 }),
|
|
6951
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M22 20V4", opacity: bars >= 3 ? 1 : 0.3 })
|
|
6952
|
+
] });
|
|
6953
|
+
}
|
|
6954
|
+
function SpeakerIcon(props) {
|
|
6955
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(Icon, { ...props, children: [
|
|
6956
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M11 5 6 9H2v6h4l5 4V5Z" }),
|
|
6526
6957
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M15.54 8.46a5 5 0 0 1 0 7.07" }),
|
|
6527
6958
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M19.07 4.93a10 10 0 0 1 0 14.14" })
|
|
6528
6959
|
] });
|
|
@@ -6553,7 +6984,7 @@ function Avatar({
|
|
|
6553
6984
|
url,
|
|
6554
6985
|
size = 40
|
|
6555
6986
|
}) {
|
|
6556
|
-
const tint = (0,
|
|
6987
|
+
const tint = (0, import_format3.avatarPaletteIndex)(name);
|
|
6557
6988
|
if (url != null && url.length > 0) {
|
|
6558
6989
|
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
6559
6990
|
"img",
|
|
@@ -6578,7 +7009,7 @@ function Avatar({
|
|
|
6578
7009
|
background: `var(--mx-meet-avatar-${tint})`
|
|
6579
7010
|
},
|
|
6580
7011
|
"aria-hidden": "true",
|
|
6581
|
-
children: (0,
|
|
7012
|
+
children: (0, import_format3.getInitials)(name)
|
|
6582
7013
|
}
|
|
6583
7014
|
);
|
|
6584
7015
|
}
|
|
@@ -6763,6 +7194,15 @@ function ControlBar({ onLeave }) {
|
|
|
6763
7194
|
const micUnavailable = useLocalMedia().microphone.unavailable;
|
|
6764
7195
|
const unread = useUnreadChat(panel === "chat");
|
|
6765
7196
|
const toggle = (next) => setPanel(panel === next ? "none" : next);
|
|
7197
|
+
const privateChatAt = snapshot?.privateChatRequest?.at ?? null;
|
|
7198
|
+
const meetStore = useMeetHost()?.store ?? null;
|
|
7199
|
+
(0, import_react4.useEffect)(() => {
|
|
7200
|
+
const request = meetStore?.snapshot().privateChatRequest ?? null;
|
|
7201
|
+
if (request === null) return;
|
|
7202
|
+
setChatTo(request.to);
|
|
7203
|
+
setPanel("chat");
|
|
7204
|
+
meetStore?.patch({ privateChatRequest: null });
|
|
7205
|
+
}, [privateChatAt, meetStore]);
|
|
6766
7206
|
useMeetShortcuts({
|
|
6767
7207
|
mic: () => void room.toggleMicrophone(),
|
|
6768
7208
|
camera: () => void room.toggleCamera(),
|
|
@@ -7002,7 +7442,12 @@ function Popover({
|
|
|
7002
7442
|
placement = "up"
|
|
7003
7443
|
}) {
|
|
7004
7444
|
const ref = (0, import_react4.useRef)(null);
|
|
7005
|
-
const [style, setStyle] = (0, import_react4.useState)({
|
|
7445
|
+
const [style, setStyle] = (0, import_react4.useState)({
|
|
7446
|
+
position: "fixed",
|
|
7447
|
+
top: 0,
|
|
7448
|
+
left: 0,
|
|
7449
|
+
visibility: "hidden"
|
|
7450
|
+
});
|
|
7006
7451
|
(0, import_react4.useLayoutEffect)(() => {
|
|
7007
7452
|
const element = ref.current;
|
|
7008
7453
|
const anchor = element?.parentElement ?? null;
|
|
@@ -7018,14 +7463,35 @@ function Popover({
|
|
|
7018
7463
|
);
|
|
7019
7464
|
const fitsBelow = rect.bottom + gutter + height <= window.innerHeight;
|
|
7020
7465
|
const fitsAbove = rect.top - gutter - height >= 0;
|
|
7466
|
+
if (!fitsBelow && !fitsAbove) {
|
|
7467
|
+
const top = Math.max(gutter, window.innerHeight - height - gutter);
|
|
7468
|
+
setStyle({
|
|
7469
|
+
position: "fixed",
|
|
7470
|
+
left,
|
|
7471
|
+
top,
|
|
7472
|
+
maxHeight: window.innerHeight - top - gutter,
|
|
7473
|
+
overflowY: "auto"
|
|
7474
|
+
});
|
|
7475
|
+
return;
|
|
7476
|
+
}
|
|
7021
7477
|
const down = placement === "down" ? fitsBelow || !fitsAbove : !fitsAbove && fitsBelow;
|
|
7022
7478
|
setStyle(
|
|
7023
7479
|
down ? { position: "fixed", left, top: rect.bottom + gutter } : { position: "fixed", left, bottom: window.innerHeight - rect.top + gutter }
|
|
7024
7480
|
);
|
|
7025
7481
|
};
|
|
7026
7482
|
place();
|
|
7483
|
+
const frame = requestAnimationFrame(place);
|
|
7484
|
+
const observer = typeof ResizeObserver === "undefined" ? null : new ResizeObserver(() => place());
|
|
7485
|
+
observer?.observe(document.body);
|
|
7486
|
+
observer?.observe(element);
|
|
7027
7487
|
window.addEventListener("resize", place);
|
|
7028
|
-
|
|
7488
|
+
window.addEventListener("scroll", place, true);
|
|
7489
|
+
return () => {
|
|
7490
|
+
cancelAnimationFrame(frame);
|
|
7491
|
+
observer?.disconnect();
|
|
7492
|
+
window.removeEventListener("resize", place);
|
|
7493
|
+
window.removeEventListener("scroll", place, true);
|
|
7494
|
+
};
|
|
7029
7495
|
}, [placement]);
|
|
7030
7496
|
(0, import_react4.useEffect)(() => {
|
|
7031
7497
|
const onKey = (event) => {
|
|
@@ -7402,11 +7868,15 @@ function PersonMenu({
|
|
|
7402
7868
|
const pinned = snapshot?.pinned === participant.identity;
|
|
7403
7869
|
const spotlit = snapshot?.spotlight === participant.identity;
|
|
7404
7870
|
const hasAccount = participant.userId !== null;
|
|
7871
|
+
const [refused, setRefused] = (0, import_react4.useState)(false);
|
|
7405
7872
|
const act = (request) => {
|
|
7873
|
+
setRefused(false);
|
|
7406
7874
|
void controls.run(request).then((result) => {
|
|
7407
7875
|
if (result !== null) onClose();
|
|
7876
|
+
else setRefused(true);
|
|
7408
7877
|
});
|
|
7409
7878
|
};
|
|
7879
|
+
const refusal2 = refused && controls.notice?.failed === true ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_design_system.ErrorBox, { as: "p", className: "mx-meet__error", message: controls.notice.text, operation: "Change this participant", origin: "meet/components", children: controls.notice.text }) : null;
|
|
7410
7880
|
if (mode === "rename" || mode === "report") {
|
|
7411
7881
|
const renaming = mode === "rename";
|
|
7412
7882
|
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Popover, { label: renaming ? "Rename" : "Report", onClose, placement, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
|
|
@@ -7444,7 +7914,7 @@ function PersonMenu({
|
|
|
7444
7914
|
}
|
|
7445
7915
|
)
|
|
7446
7916
|
] }),
|
|
7447
|
-
|
|
7917
|
+
refusal2,
|
|
7448
7918
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "mx-meet__menu-actions", children: [
|
|
7449
7919
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { type: "button", className: "mx-meet__link", onClick: () => setMode("menu"), children: "Back" }),
|
|
7450
7920
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { type: "submit", className: "mx-meet__send", disabled: text3.trim().length === 0 || controls.busy, children: renaming ? "Rename" : "Report" })
|
|
@@ -7457,6 +7927,7 @@ function PersonMenu({
|
|
|
7457
7927
|
const removing = mode === "remove";
|
|
7458
7928
|
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Popover, { label: removing ? "Remove" : "Make host", onClose, placement, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "mx-meet__menu-form", children: [
|
|
7459
7929
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { className: "mx-meet__confirm-text", children: removing ? `Remove ${participant.displayName}? They leave immediately and cannot rejoin this meeting.` : `Make ${participant.displayName} the host? They get every host control and own this meeting. You stay on as a co-host.` }),
|
|
7930
|
+
refusal2,
|
|
7460
7931
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "mx-meet__menu-actions", children: [
|
|
7461
7932
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { type: "button", className: "mx-meet__link", onClick: () => setMode("menu"), children: "Cancel" }),
|
|
7462
7933
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
@@ -7487,11 +7958,15 @@ function PersonMenu({
|
|
|
7487
7958
|
]
|
|
7488
7959
|
}
|
|
7489
7960
|
),
|
|
7490
|
-
!self && !participant.isAgent && onMessage !== void 0 ? /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
|
|
7961
|
+
!self && !participant.isAgent && (onMessage !== void 0 || host !== null) ? /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
|
|
7491
7962
|
MenuItem,
|
|
7492
7963
|
{
|
|
7493
7964
|
onSelect: () => {
|
|
7494
|
-
onMessage(participant.identity);
|
|
7965
|
+
if (onMessage !== void 0) onMessage(participant.identity);
|
|
7966
|
+
else
|
|
7967
|
+
host?.store.patch({
|
|
7968
|
+
privateChatRequest: { to: participant.identity, at: Date.now() }
|
|
7969
|
+
});
|
|
7495
7970
|
onClose();
|
|
7496
7971
|
},
|
|
7497
7972
|
children: [
|
|
@@ -8118,7 +8593,7 @@ function PreJoinBody({
|
|
|
8118
8593
|
microphone,
|
|
8119
8594
|
...meetingId !== void 0 ? { meetingId } : {},
|
|
8120
8595
|
...slug !== void 0 ? { slug } : {}
|
|
8121
|
-
}).then(() => onJoined?.()).finally(() => setJoining(false));
|
|
8596
|
+
}).then(() => onJoined?.()).catch(() => void 0).finally(() => setJoining(false));
|
|
8122
8597
|
},
|
|
8123
8598
|
children: joining ? "Joining\u2026" : "Join now"
|
|
8124
8599
|
}
|
|
@@ -8254,7 +8729,7 @@ function MeetingRoomBody({
|
|
|
8254
8729
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("h1", { className: "mx-meet__title", children: snapshot?.meeting?.title ?? "Meeting" }),
|
|
8255
8730
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("span", { className: "mx-meet__meta", children: [
|
|
8256
8731
|
participantSummary(snapshot?.participants ?? []),
|
|
8257
|
-
elapsed > 0 ? ` \xB7 ${(0,
|
|
8732
|
+
elapsed > 0 ? ` \xB7 ${(0, import_format3.formatDurationMs)(elapsed)}` : ""
|
|
8258
8733
|
] }),
|
|
8259
8734
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(RecordingIndicator, {}),
|
|
8260
8735
|
snapshot?.locked === true ? /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("span", { className: "mx-meet__badge", children: [
|
|
@@ -8560,7 +9035,10 @@ function MeetingRecordBody({
|
|
|
8560
9035
|
const blocks = groupTranscript(bundle.transcript, bundle.names);
|
|
8561
9036
|
const notice = recordingNotice(bundle.recording);
|
|
8562
9037
|
const fileId = playableRecordingFileId(bundle.recording);
|
|
8563
|
-
const attended =
|
|
9038
|
+
const attended = uniqueAttendees(
|
|
9039
|
+
bundle.attendees.filter((person) => person.admissionState !== "denied"),
|
|
9040
|
+
bundle.names
|
|
9041
|
+
);
|
|
8564
9042
|
const restrictionNotices = summarizeMeetingRecordRestrictions(bundle.restrictions);
|
|
8565
9043
|
const guestFinishedMeeting = host.identity.userId === null && meeting.endedAt !== null;
|
|
8566
9044
|
const everyRecordPartRefused = ["attendance", "notes", "transcript", "recording"].every(
|
|
@@ -8869,514 +9347,172 @@ function RecordingPlayerBody({ fileId, host }) {
|
|
|
8869
9347
|
const failure = thrown;
|
|
8870
9348
|
setError({
|
|
8871
9349
|
message: failure.message ?? "The recording could not be loaded.",
|
|
8872
|
-
remedy: failure.remedy ?? PLAYBACK_RETRY_REMEDY
|
|
8873
|
-
});
|
|
8874
|
-
} finally {
|
|
8875
|
-
setLoading(false);
|
|
8876
|
-
}
|
|
8877
|
-
})();
|
|
8878
|
-
},
|
|
8879
|
-
children: loading ? "Loading the recording\u2026" : "Play the recording"
|
|
8880
|
-
}
|
|
8881
|
-
)
|
|
8882
|
-
] });
|
|
8883
|
-
}
|
|
8884
|
-
function clockOf(iso) {
|
|
8885
|
-
const at = new Date(iso);
|
|
8886
|
-
if (Number.isNaN(at.getTime())) return "";
|
|
8887
|
-
return at.toLocaleTimeString(void 0, { hour: "numeric", minute: "2-digit" });
|
|
8888
|
-
}
|
|
8889
|
-
function MeetingList({ baseUrl }) {
|
|
8890
|
-
const host = useMeetHost();
|
|
8891
|
-
const list = useMeetings();
|
|
8892
|
-
if (host === null) return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(InertNotice, { what: "Your meetings" });
|
|
8893
|
-
if (list.failure !== null) {
|
|
8894
|
-
return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_design_system.ErrorBox, { as: "div", className: "mx-meet__empty", error: list.failure, operation: "List your meetings", origin: "meet/components", children: [
|
|
8895
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { className: "mx-meet__empty-title", children: "Your meetings could not be listed" }),
|
|
8896
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { className: "mx-meet__empty-body", children: list.failure.message }),
|
|
8897
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { className: "mx-meet__empty-body", children: list.failure.remedy })
|
|
8898
|
-
] });
|
|
8899
|
-
}
|
|
8900
|
-
if (list.loading) {
|
|
8901
|
-
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "mx-meet__empty", role: "status", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { className: "mx-meet__empty-title", children: "Loading your meetings\u2026" }) });
|
|
8902
|
-
}
|
|
8903
|
-
if (list.meetings.length === 0) {
|
|
8904
|
-
return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "mx-meet__empty", role: "status", children: [
|
|
8905
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { className: "mx-meet__empty-title", children: "No meetings yet" }),
|
|
8906
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { className: "mx-meet__empty-body", children: "Create one above. Its link works before, during and after the meeting \u2014 afterwards it opens the record." })
|
|
8907
|
-
] });
|
|
8908
|
-
}
|
|
8909
|
-
const origin = baseUrl ?? (typeof window === "undefined" ? "" : window.location.origin);
|
|
8910
|
-
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("ul", { className: "mx-meet-list", children: list.meetings.map((meeting) => {
|
|
8911
|
-
const ended = meeting.endedAt !== null;
|
|
8912
|
-
return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("li", { className: "mx-meet-list__row", children: [
|
|
8913
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "mx-meet-list__main", children: [
|
|
8914
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("span", { className: "mx-meet-list__title", children: [
|
|
8915
|
-
meeting.title,
|
|
8916
|
-
ended ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "mx-meet__badge", children: "Ended" }) : null
|
|
8917
|
-
] }),
|
|
8918
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "mx-meet-list__when", children: meetingWhen(meeting) })
|
|
8919
|
-
] }),
|
|
8920
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("a", { className: "mx-meet-list__action", href: meetingLink(origin, meeting.slug), children: ended ? "Open the record" : "Join" })
|
|
8921
|
-
] }, meeting.id);
|
|
8922
|
-
}) });
|
|
8923
|
-
}
|
|
8924
|
-
|
|
8925
|
-
// src/core/invitations.ts
|
|
8926
|
-
var CALL_INVITE_KIND = "meet.call-invite";
|
|
8927
|
-
var CALL_INVITE_VERSION = 1;
|
|
8928
|
-
function callInviteAction(invite) {
|
|
8929
|
-
return {
|
|
8930
|
-
kind: CALL_INVITE_KIND,
|
|
8931
|
-
version: CALL_INVITE_VERSION,
|
|
8932
|
-
payload: {
|
|
8933
|
-
invite_id: invite.id,
|
|
8934
|
-
room_name: invite.roomName,
|
|
8935
|
-
mode: invite.mode,
|
|
8936
|
-
caller_name: invite.callerName,
|
|
8937
|
-
expires_at: invite.expiresAt
|
|
8938
|
-
}
|
|
8939
|
-
};
|
|
8940
|
-
}
|
|
8941
|
-
function callInviteText(invite) {
|
|
8942
|
-
return invite.mode === "video" ? `${invite.callerName} started a video call` : `${invite.callerName} started an audio call`;
|
|
8943
|
-
}
|
|
8944
|
-
function createCallInviteHandler(options) {
|
|
8945
|
-
const now = options.now ?? (() => Date.now());
|
|
8946
|
-
return {
|
|
8947
|
-
kind: CALL_INVITE_KIND,
|
|
8948
|
-
versions: [CALL_INVITE_VERSION],
|
|
8949
|
-
choices: (payload) => {
|
|
8950
|
-
if (Date.parse(payload.expires_at) < now()) return [];
|
|
8951
|
-
return [
|
|
8952
|
-
{ id: "join", label: payload.mode === "video" ? "Join video" : "Join call", tone: "primary" },
|
|
8953
|
-
{ id: "decline", label: "Decline", tone: "neutral" }
|
|
8954
|
-
];
|
|
8955
|
-
},
|
|
8956
|
-
summarize: (payload) => payload.mode === "video" ? `${payload.caller_name} started a video call` : `${payload.caller_name} started an audio call`,
|
|
8957
|
-
execute: async (payload, context) => {
|
|
8958
|
-
const settledAt = new Date(now()).toISOString();
|
|
8959
|
-
const inviteId = asCallId(payload.invite_id);
|
|
8960
|
-
const pending = await options.repository.pendingInvites(asUserId(String(context.actorId)));
|
|
8961
|
-
const live = pending.find((invite) => invite.id === inviteId) ?? null;
|
|
8962
|
-
if (live === null) {
|
|
8963
|
-
return {
|
|
8964
|
-
kind: CALL_INVITE_KIND,
|
|
8965
|
-
messageId: context.messageId,
|
|
8966
|
-
actorId: context.actorId,
|
|
8967
|
-
outcome: "unavailable",
|
|
8968
|
-
label: "This call has ended",
|
|
8969
|
-
settledAt,
|
|
8970
|
-
detail: "The invite is no longer ringing \u2014 it was answered elsewhere, cancelled, or it timed out. The chips are replaced by the outcome rather than left clickable."
|
|
8971
|
-
};
|
|
8972
|
-
}
|
|
8973
|
-
if (context.choice === "decline") {
|
|
8974
|
-
await options.calls.decline(inviteId);
|
|
8975
|
-
return {
|
|
8976
|
-
kind: CALL_INVITE_KIND,
|
|
8977
|
-
messageId: context.messageId,
|
|
8978
|
-
actorId: context.actorId,
|
|
8979
|
-
outcome: "declined",
|
|
8980
|
-
label: "Declined",
|
|
8981
|
-
settledAt
|
|
8982
|
-
};
|
|
8983
|
-
}
|
|
8984
|
-
const accepted = await options.calls.accept(inviteId, live.mode);
|
|
8985
|
-
await options.onJoin(accepted);
|
|
8986
|
-
return {
|
|
8987
|
-
kind: CALL_INVITE_KIND,
|
|
8988
|
-
messageId: context.messageId,
|
|
8989
|
-
actorId: context.actorId,
|
|
8990
|
-
outcome: "applied",
|
|
8991
|
-
label: "Joined",
|
|
8992
|
-
settledAt
|
|
8993
|
-
};
|
|
8994
|
-
}
|
|
8995
|
-
};
|
|
8996
|
-
}
|
|
8997
|
-
var MEETING_INVITE_KIND = "meet.meeting-invite";
|
|
8998
|
-
var MEETING_INVITE_VERSION = 1;
|
|
8999
|
-
function meetingInviteAction(payload) {
|
|
9000
|
-
return { kind: MEETING_INVITE_KIND, version: MEETING_INVITE_VERSION, payload };
|
|
9001
|
-
}
|
|
9002
|
-
function createMeetingInviteHandler(options) {
|
|
9003
|
-
const now = options.now ?? (() => Date.now());
|
|
9004
|
-
return {
|
|
9005
|
-
kind: MEETING_INVITE_KIND,
|
|
9006
|
-
versions: [MEETING_INVITE_VERSION],
|
|
9007
|
-
// A DURABLE LINK NEVER EXPIRES (R3) — the chip stays useful after the
|
|
9008
|
-
// meeting too, because that is when people go looking for the notes.
|
|
9009
|
-
choices: () => [{ id: "open", label: "Open meeting", tone: "primary" }],
|
|
9010
|
-
summarize: (payload) => payload.scheduled_for === null ? payload.title : `${payload.title} \u2014 ${payload.scheduled_for}`,
|
|
9011
|
-
execute: async (payload, context) => {
|
|
9012
|
-
if (payload.slug.length === 0) {
|
|
9013
|
-
throw new MeetError(
|
|
9014
|
-
"invalid-response",
|
|
9015
|
-
"meet.meeting-invite: the payload carries no slug",
|
|
9016
|
-
"A meeting invitation without a durable slug cannot be opened. Fix the sender; never synthesize a link on the receiving side."
|
|
9017
|
-
);
|
|
9018
|
-
}
|
|
9019
|
-
await options.onOpen(payload);
|
|
9020
|
-
return {
|
|
9021
|
-
kind: MEETING_INVITE_KIND,
|
|
9022
|
-
messageId: context.messageId,
|
|
9023
|
-
actorId: context.actorId,
|
|
9024
|
-
outcome: "applied",
|
|
9025
|
-
label: "Opened",
|
|
9026
|
-
settledAt: new Date(now()).toISOString()
|
|
9027
|
-
};
|
|
9028
|
-
}
|
|
9029
|
-
};
|
|
9030
|
-
}
|
|
9031
|
-
|
|
9032
|
-
// src/core/after.ts
|
|
9033
|
-
var import_format4 = require("@ai-matrx/kit/format");
|
|
9034
|
-
function kindOf(person) {
|
|
9035
|
-
if (person.isAgent) return "assistant";
|
|
9036
|
-
if (person.role === "host") return "host";
|
|
9037
|
-
if (person.role === "cohost") return "cohost";
|
|
9038
|
-
if (person.role === "guest") return person.userId !== null ? "signed-in guest" : "guest";
|
|
9039
|
-
return "member";
|
|
9040
|
-
}
|
|
9041
|
-
function ms(iso) {
|
|
9042
|
-
if (iso === null) return null;
|
|
9043
|
-
const at = new Date(iso).getTime();
|
|
9044
|
-
return Number.isNaN(at) ? null : at;
|
|
9045
|
-
}
|
|
9046
|
-
function attendanceReport(meeting, attendees, names = {}) {
|
|
9047
|
-
const end = ms(meeting.endedAt);
|
|
9048
|
-
const rows = attendees.map((person) => {
|
|
9049
|
-
const joined = ms(person.joinedAt);
|
|
9050
|
-
const left = ms(person.leftAt);
|
|
9051
|
-
const until = left ?? end;
|
|
9052
|
-
const duration = joined !== null && until !== null && until >= joined ? until - joined : null;
|
|
9053
|
-
return {
|
|
9054
|
-
id: person.id,
|
|
9055
|
-
identity: person.identity,
|
|
9056
|
-
name: displayNameFor(names, person.identity, person.displayName),
|
|
9057
|
-
userId: person.userId,
|
|
9058
|
-
kind: kindOf(person),
|
|
9059
|
-
joinedAt: person.joinedAt,
|
|
9060
|
-
leftAt: person.leftAt,
|
|
9061
|
-
durationMs: duration,
|
|
9062
|
-
durationToEnd: duration !== null && left === null,
|
|
9063
|
-
admission: person.admissionState
|
|
9064
|
-
};
|
|
9065
|
-
});
|
|
9066
|
-
const rank = {
|
|
9067
|
-
host: 0,
|
|
9068
|
-
cohost: 1,
|
|
9069
|
-
member: 2,
|
|
9070
|
-
"signed-in guest": 3,
|
|
9071
|
-
guest: 4,
|
|
9072
|
-
assistant: 5
|
|
9073
|
-
};
|
|
9074
|
-
return rows.sort((a, b) => {
|
|
9075
|
-
if (rank[a.kind] !== rank[b.kind]) return rank[a.kind] - rank[b.kind];
|
|
9076
|
-
return (ms(a.joinedAt) ?? Number.MAX_SAFE_INTEGER) - (ms(b.joinedAt) ?? Number.MAX_SAFE_INTEGER);
|
|
9077
|
-
});
|
|
9078
|
-
}
|
|
9079
|
-
function attendanceTotals(rows) {
|
|
9080
|
-
const humans = rows.filter((row) => row.kind !== "assistant");
|
|
9081
|
-
const attended = humans.filter((row) => row.joinedAt !== null);
|
|
9082
|
-
const timed = attended.filter((row) => row.durationMs !== null);
|
|
9083
|
-
return {
|
|
9084
|
-
people: attended.length,
|
|
9085
|
-
guests: attended.filter((row) => row.kind === "guest" || row.kind === "signed-in guest").length,
|
|
9086
|
-
neverAdmitted: humans.filter((row) => row.joinedAt === null).length,
|
|
9087
|
-
averageMs: timed.length === 0 ? null : Math.round(timed.reduce((sum, row) => sum + (row.durationMs ?? 0), 0) / timed.length)
|
|
9088
|
-
};
|
|
9089
|
-
}
|
|
9090
|
-
function csvCell(value) {
|
|
9091
|
-
if (value === null) return "";
|
|
9092
|
-
const text3 = String(value);
|
|
9093
|
-
return /[",\n\r]/.test(text3) ? `"${text3.replace(/"/g, '""')}"` : text3;
|
|
9094
|
-
}
|
|
9095
|
-
var ATTENDANCE_CSV_HEADER = [
|
|
9096
|
-
"Meeting",
|
|
9097
|
-
"Meeting started",
|
|
9098
|
-
"Name",
|
|
9099
|
-
"Attended as",
|
|
9100
|
-
"Joined",
|
|
9101
|
-
"Left",
|
|
9102
|
-
"Minutes in the meeting",
|
|
9103
|
-
"Admission"
|
|
9104
|
-
];
|
|
9105
|
-
function attendanceCsv(reports) {
|
|
9106
|
-
const lines = [ATTENDANCE_CSV_HEADER.join(",")];
|
|
9107
|
-
for (const { meeting, rows } of reports) {
|
|
9108
|
-
for (const row of rows) {
|
|
9109
|
-
lines.push(
|
|
9110
|
-
[
|
|
9111
|
-
meeting.title,
|
|
9112
|
-
meeting.startedAt ?? meeting.scheduledFor,
|
|
9113
|
-
row.name,
|
|
9114
|
-
row.kind,
|
|
9115
|
-
row.joinedAt,
|
|
9116
|
-
row.leftAt,
|
|
9117
|
-
row.durationMs === null ? null : Math.round(row.durationMs / 6e3) / 10,
|
|
9118
|
-
row.admission
|
|
9119
|
-
].map(csvCell).join(",")
|
|
9120
|
-
);
|
|
9121
|
-
}
|
|
9122
|
-
}
|
|
9123
|
-
return `${lines.join("\r\n")}\r
|
|
9124
|
-
`;
|
|
9125
|
-
}
|
|
9126
|
-
function recordingOffsetMs(at, recording) {
|
|
9127
|
-
if (recording === null || recording.state !== "available") return null;
|
|
9128
|
-
const start = ms(recording.startedAt);
|
|
9129
|
-
const moment = ms(at);
|
|
9130
|
-
if (start === null || moment === null || moment < start) return null;
|
|
9131
|
-
const end = ms(recording.endedAt);
|
|
9132
|
-
if (end !== null && moment > end) return null;
|
|
9133
|
-
return moment - start;
|
|
9134
|
-
}
|
|
9135
|
-
function clockPosition(offsetMs) {
|
|
9136
|
-
const total = Math.max(0, Math.floor(offsetMs / 1e3));
|
|
9137
|
-
const h = Math.floor(total / 3600);
|
|
9138
|
-
const m = Math.floor(total % 3600 / 60);
|
|
9139
|
-
const s = total % 60;
|
|
9140
|
-
const ss = String(s).padStart(2, "0");
|
|
9141
|
-
return h > 0 ? `${h}:${String(m).padStart(2, "0")}:${ss}` : `${m}:${ss}`;
|
|
9142
|
-
}
|
|
9143
|
-
function searchTranscript(segments, query, names = {}) {
|
|
9144
|
-
const needle = fold(query.trim());
|
|
9145
|
-
if (needle.length === 0) return [];
|
|
9146
|
-
const out = [];
|
|
9147
|
-
for (const segment of segments) {
|
|
9148
|
-
const hay = fold(segment.text);
|
|
9149
|
-
const ranges = [];
|
|
9150
|
-
let from = 0;
|
|
9151
|
-
for (; ; ) {
|
|
9152
|
-
const at = hay.indexOf(needle, from);
|
|
9153
|
-
if (at < 0) break;
|
|
9154
|
-
ranges.push([at, at + needle.length]);
|
|
9155
|
-
from = at + needle.length;
|
|
9156
|
-
}
|
|
9157
|
-
const speaker = fold(displayNameFor(names, segment.identity, segment.speaker));
|
|
9158
|
-
if (ranges.length > 0 || speaker.includes(needle)) {
|
|
9159
|
-
out.push({ segmentId: segment.id, ranges });
|
|
9160
|
-
}
|
|
9161
|
-
}
|
|
9162
|
-
return out;
|
|
9163
|
-
}
|
|
9164
|
-
function fold(value) {
|
|
9165
|
-
return value.normalize("NFD").replace(/[̀-ͯ]/g, (mark) => mark.length === 1 ? "" : mark).toLowerCase();
|
|
9166
|
-
}
|
|
9167
|
-
function clockOfDay(iso, options) {
|
|
9168
|
-
const at = new Date(iso);
|
|
9169
|
-
if (Number.isNaN(at.getTime())) return "";
|
|
9170
|
-
return at.toLocaleTimeString(options.locale, {
|
|
9171
|
-
hour: "numeric",
|
|
9172
|
-
minute: "2-digit",
|
|
9173
|
-
second: "2-digit",
|
|
9174
|
-
...options.timeZone !== void 0 ? { timeZone: options.timeZone } : {}
|
|
9175
|
-
});
|
|
9176
|
-
}
|
|
9177
|
-
function vttStamp(offsetMs) {
|
|
9178
|
-
const total = Math.max(0, Math.round(offsetMs));
|
|
9179
|
-
const h = Math.floor(total / 36e5);
|
|
9180
|
-
const m = Math.floor(total % 36e5 / 6e4);
|
|
9181
|
-
const s = Math.floor(total % 6e4 / 1e3);
|
|
9182
|
-
const milli = total % 1e3;
|
|
9183
|
-
return `${String(h).padStart(2, "0")}:${String(m).padStart(2, "0")}:${String(s).padStart(2, "0")}.${String(milli).padStart(3, "0")}`;
|
|
9184
|
-
}
|
|
9185
|
-
function transcriptDocument(bundle, format, options = {}) {
|
|
9186
|
-
const names = options.names ?? bundle.names;
|
|
9187
|
-
const lines = bundle.transcript.filter((line) => line.text.trim().length > 0);
|
|
9188
|
-
const speaker = (line) => displayNameFor(names, line.identity, line.speaker);
|
|
9189
|
-
if (format === "vtt") {
|
|
9190
|
-
const zeroIso = bundle.recording?.state === "available" && bundle.recording.startedAt !== null ? bundle.recording.startedAt : bundle.meeting.startedAt ?? lines[0]?.startedAt ?? null;
|
|
9191
|
-
const zero = ms(zeroIso) ?? 0;
|
|
9192
|
-
const cues = ["WEBVTT", ""];
|
|
9193
|
-
lines.forEach((line, index) => {
|
|
9194
|
-
const start = (ms(line.startedAt) ?? zero) - zero;
|
|
9195
|
-
if (start < 0) return;
|
|
9196
|
-
const next = lines[index + 1];
|
|
9197
|
-
const nextStart = next !== void 0 ? (ms(next.startedAt) ?? zero) - zero : start + 4e3;
|
|
9198
|
-
const end = Math.max(start + 500, Math.min(nextStart, start + 1e4));
|
|
9199
|
-
cues.push(`${vttStamp(start)} --> ${vttStamp(end)}`);
|
|
9200
|
-
cues.push(`<v ${speaker(line).replace(/[<>]/g, "")}>${line.text.replace(/-->/g, "->")}`);
|
|
9201
|
-
cues.push("");
|
|
9202
|
-
});
|
|
9203
|
-
return cues.join("\n");
|
|
9204
|
-
}
|
|
9205
|
-
const out = [];
|
|
9206
|
-
const title = bundle.meeting.title;
|
|
9207
|
-
out.push(format === "md" ? `# ${title} \u2014 transcript` : `${title} \u2014 transcript`);
|
|
9208
|
-
out.push("");
|
|
9209
|
-
let last = null;
|
|
9210
|
-
for (const line of lines) {
|
|
9211
|
-
if (line.identity !== last) {
|
|
9212
|
-
if (last !== null) out.push("");
|
|
9213
|
-
const who = speaker(line);
|
|
9214
|
-
const clock = clockOfDay(line.startedAt, options);
|
|
9215
|
-
out.push(format === "md" ? `**${who}** \xB7 ${clock}` : `${who} (${clock})`);
|
|
9216
|
-
last = line.identity;
|
|
9217
|
-
}
|
|
9218
|
-
out.push(line.text.trim());
|
|
9219
|
-
}
|
|
9220
|
-
if (lines.length === 0) out.push("No transcript was captured for this meeting.");
|
|
9221
|
-
return `${out.join("\n")}
|
|
9222
|
-
`;
|
|
9223
|
-
}
|
|
9224
|
-
function bullet(note) {
|
|
9225
|
-
const owner = note.assigneeDisplayName !== null ? ` \u2014 ${note.assigneeDisplayName}` : "";
|
|
9226
|
-
return `- ${note.text.trim()}${owner}`;
|
|
9227
|
-
}
|
|
9228
|
-
function notesDocument(bundle, options = {}) {
|
|
9229
|
-
const out = [`# ${bundle.meeting.title}`, ""];
|
|
9230
|
-
if (options.when) out.push(`_${options.when}_`, "");
|
|
9231
|
-
const people = bundle.attendees.filter((person) => !person.isAgent && person.joinedAt !== null).map((person) => displayNameFor(bundle.names, person.identity, person.displayName));
|
|
9232
|
-
if (people.length > 0) out.push(`**Attended:** ${people.join(", ")}`, "");
|
|
9233
|
-
out.push("## Summary", "", bundle.summary?.text.trim() || "No summary was written.", "");
|
|
9234
|
-
out.push("## Decisions", "");
|
|
9235
|
-
out.push(...bundle.decisions.length > 0 ? bundle.decisions.map(bullet) : ["None recorded."], "");
|
|
9236
|
-
out.push("## Action items", "");
|
|
9237
|
-
out.push(
|
|
9238
|
-
...bundle.actionItems.length > 0 ? bundle.actionItems.map(bullet) : ["None recorded."],
|
|
9239
|
-
""
|
|
9240
|
-
);
|
|
9241
|
-
if (options.link) out.push(`[Open the meeting record](${options.link})`, "");
|
|
9242
|
-
return out.join("\n");
|
|
9350
|
+
remedy: failure.remedy ?? PLAYBACK_RETRY_REMEDY
|
|
9351
|
+
});
|
|
9352
|
+
} finally {
|
|
9353
|
+
setLoading(false);
|
|
9354
|
+
}
|
|
9355
|
+
})();
|
|
9356
|
+
},
|
|
9357
|
+
children: loading ? "Loading the recording\u2026" : "Play the recording"
|
|
9358
|
+
}
|
|
9359
|
+
)
|
|
9360
|
+
] });
|
|
9243
9361
|
}
|
|
9244
|
-
function
|
|
9245
|
-
const
|
|
9246
|
-
|
|
9247
|
-
|
|
9248
|
-
return (0, import_format4.formatDurationMs)(end - start, { style: "long" });
|
|
9362
|
+
function clockOf(iso) {
|
|
9363
|
+
const at = new Date(iso);
|
|
9364
|
+
if (Number.isNaN(at.getTime())) return "";
|
|
9365
|
+
return at.toLocaleTimeString(void 0, { hour: "numeric", minute: "2-digit" });
|
|
9249
9366
|
}
|
|
9250
|
-
|
|
9251
|
-
const
|
|
9252
|
-
|
|
9253
|
-
|
|
9254
|
-
|
|
9255
|
-
|
|
9256
|
-
|
|
9257
|
-
|
|
9258
|
-
|
|
9259
|
-
|
|
9260
|
-
});
|
|
9261
|
-
} catch (error) {
|
|
9262
|
-
return {
|
|
9263
|
-
messages: [],
|
|
9264
|
-
refusal: {
|
|
9265
|
-
message: `The chat could not be read (${error.message}).`,
|
|
9266
|
-
remedy: error.remedy ?? "Reload the page."
|
|
9267
|
-
}
|
|
9268
|
-
};
|
|
9367
|
+
function MeetingList({ baseUrl }) {
|
|
9368
|
+
const host = useMeetHost();
|
|
9369
|
+
const list = useMeetings();
|
|
9370
|
+
if (host === null) return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(InertNotice, { what: "Your meetings" });
|
|
9371
|
+
if (list.failure !== null) {
|
|
9372
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_design_system.ErrorBox, { as: "div", className: "mx-meet__empty", error: list.failure, operation: "List your meetings", origin: "meet/components", children: [
|
|
9373
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { className: "mx-meet__empty-title", children: "Your meetings could not be listed" }),
|
|
9374
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { className: "mx-meet__empty-body", children: list.failure.message }),
|
|
9375
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { className: "mx-meet__empty-body", children: list.failure.remedy })
|
|
9376
|
+
] });
|
|
9269
9377
|
}
|
|
9270
|
-
if (
|
|
9271
|
-
|
|
9272
|
-
try {
|
|
9273
|
-
body = await response.json();
|
|
9274
|
-
} catch {
|
|
9275
|
-
body = null;
|
|
9276
|
-
}
|
|
9277
|
-
const said = readServerRefusal(body);
|
|
9278
|
-
return {
|
|
9279
|
-
messages: [],
|
|
9280
|
-
refusal: {
|
|
9281
|
-
message: said?.reason ?? `The chat could not be read (the server answered ${response.status}).`,
|
|
9282
|
-
remedy: said?.remedy || "Reload the page."
|
|
9283
|
-
}
|
|
9284
|
-
};
|
|
9378
|
+
if (list.loading) {
|
|
9379
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "mx-meet__empty", role: "status", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { className: "mx-meet__empty-title", children: "Loading your meetings\u2026" }) });
|
|
9285
9380
|
}
|
|
9286
|
-
|
|
9287
|
-
|
|
9288
|
-
|
|
9289
|
-
|
|
9290
|
-
|
|
9291
|
-
).filter((message) => message !== null),
|
|
9292
|
-
refusal: null
|
|
9293
|
-
};
|
|
9294
|
-
}
|
|
9295
|
-
async function refusalOf(response, operation) {
|
|
9296
|
-
let body = null;
|
|
9297
|
-
try {
|
|
9298
|
-
body = await response.json();
|
|
9299
|
-
} catch {
|
|
9300
|
-
body = null;
|
|
9381
|
+
if (list.meetings.length === 0) {
|
|
9382
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "mx-meet__empty", role: "status", children: [
|
|
9383
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { className: "mx-meet__empty-title", children: "No meetings yet" }),
|
|
9384
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { className: "mx-meet__empty-body", children: "Create one above. Its link works before, during and after the meeting \u2014 afterwards it opens the record." })
|
|
9385
|
+
] });
|
|
9301
9386
|
}
|
|
9302
|
-
const
|
|
9303
|
-
return
|
|
9304
|
-
|
|
9305
|
-
|
|
9306
|
-
|
|
9307
|
-
|
|
9387
|
+
const origin = baseUrl ?? (typeof window === "undefined" ? "" : window.location.origin);
|
|
9388
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("ul", { className: "mx-meet-list", children: list.meetings.map((meeting) => {
|
|
9389
|
+
const ended = meeting.endedAt !== null;
|
|
9390
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("li", { className: "mx-meet-list__row", children: [
|
|
9391
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "mx-meet-list__main", children: [
|
|
9392
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("span", { className: "mx-meet-list__title", children: [
|
|
9393
|
+
meeting.title,
|
|
9394
|
+
ended ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "mx-meet__badge", children: "Ended" }) : null
|
|
9395
|
+
] }),
|
|
9396
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "mx-meet-list__when", children: meetingWhen(meeting) })
|
|
9397
|
+
] }),
|
|
9398
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("a", { className: "mx-meet-list__action", href: meetingLink(origin, meeting.slug), children: ended ? "Open the record" : "Join" })
|
|
9399
|
+
] }, meeting.id);
|
|
9400
|
+
}) });
|
|
9308
9401
|
}
|
|
9309
|
-
|
|
9310
|
-
|
|
9402
|
+
|
|
9403
|
+
// src/core/invitations.ts
|
|
9404
|
+
var CALL_INVITE_KIND = "meet.call-invite";
|
|
9405
|
+
var CALL_INVITE_VERSION = 1;
|
|
9406
|
+
function callInviteAction(invite) {
|
|
9311
9407
|
return {
|
|
9312
|
-
|
|
9313
|
-
|
|
9314
|
-
|
|
9315
|
-
|
|
9316
|
-
|
|
9317
|
-
|
|
9408
|
+
kind: CALL_INVITE_KIND,
|
|
9409
|
+
version: CALL_INVITE_VERSION,
|
|
9410
|
+
payload: {
|
|
9411
|
+
invite_id: invite.id,
|
|
9412
|
+
room_name: invite.roomName,
|
|
9413
|
+
mode: invite.mode,
|
|
9414
|
+
caller_name: invite.callerName,
|
|
9415
|
+
expires_at: invite.expiresAt
|
|
9416
|
+
}
|
|
9318
9417
|
};
|
|
9319
9418
|
}
|
|
9320
|
-
|
|
9321
|
-
|
|
9322
|
-
|
|
9323
|
-
|
|
9324
|
-
|
|
9325
|
-
requireAuth: true,
|
|
9326
|
-
operation
|
|
9327
|
-
});
|
|
9328
|
-
if (!response.ok) throw await refusalOf(response, operation);
|
|
9329
|
-
const body = await response.json();
|
|
9330
|
-
const last = body["last_sent"];
|
|
9419
|
+
function callInviteText(invite) {
|
|
9420
|
+
return invite.mode === "video" ? `${invite.callerName} started a video call` : `${invite.callerName} started an audio call`;
|
|
9421
|
+
}
|
|
9422
|
+
function createCallInviteHandler(options) {
|
|
9423
|
+
const now = options.now ?? (() => Date.now());
|
|
9331
9424
|
return {
|
|
9332
|
-
|
|
9333
|
-
|
|
9334
|
-
|
|
9335
|
-
(
|
|
9336
|
-
|
|
9337
|
-
|
|
9338
|
-
|
|
9425
|
+
kind: CALL_INVITE_KIND,
|
|
9426
|
+
versions: [CALL_INVITE_VERSION],
|
|
9427
|
+
choices: (payload) => {
|
|
9428
|
+
if (Date.parse(payload.expires_at) < now()) return [];
|
|
9429
|
+
return [
|
|
9430
|
+
{ id: "join", label: payload.mode === "video" ? "Join video" : "Join call", tone: "primary" },
|
|
9431
|
+
{ id: "decline", label: "Decline", tone: "neutral" }
|
|
9432
|
+
];
|
|
9433
|
+
},
|
|
9434
|
+
summarize: (payload) => payload.mode === "video" ? `${payload.caller_name} started a video call` : `${payload.caller_name} started an audio call`,
|
|
9435
|
+
execute: async (payload, context) => {
|
|
9436
|
+
const settledAt = new Date(now()).toISOString();
|
|
9437
|
+
const inviteId = asCallId(payload.invite_id);
|
|
9438
|
+
const pending = await options.repository.pendingInvites(asUserId(String(context.actorId)));
|
|
9439
|
+
const live = pending.find((invite) => invite.id === inviteId) ?? null;
|
|
9440
|
+
if (live === null) {
|
|
9441
|
+
return {
|
|
9442
|
+
kind: CALL_INVITE_KIND,
|
|
9443
|
+
messageId: context.messageId,
|
|
9444
|
+
actorId: context.actorId,
|
|
9445
|
+
outcome: "unavailable",
|
|
9446
|
+
label: "This call has ended",
|
|
9447
|
+
settledAt,
|
|
9448
|
+
detail: "The invite is no longer ringing \u2014 it was answered elsewhere, cancelled, or it timed out. The chips are replaced by the outcome rather than left clickable."
|
|
9449
|
+
};
|
|
9450
|
+
}
|
|
9451
|
+
if (context.choice === "decline") {
|
|
9452
|
+
await options.calls.decline(inviteId);
|
|
9453
|
+
return {
|
|
9454
|
+
kind: CALL_INVITE_KIND,
|
|
9455
|
+
messageId: context.messageId,
|
|
9456
|
+
actorId: context.actorId,
|
|
9457
|
+
outcome: "declined",
|
|
9458
|
+
label: "Declined",
|
|
9459
|
+
settledAt
|
|
9460
|
+
};
|
|
9461
|
+
}
|
|
9462
|
+
const accepted = await options.calls.accept(inviteId, live.mode);
|
|
9463
|
+
await options.onJoin(accepted);
|
|
9464
|
+
return {
|
|
9465
|
+
kind: CALL_INVITE_KIND,
|
|
9466
|
+
messageId: context.messageId,
|
|
9467
|
+
actorId: context.actorId,
|
|
9468
|
+
outcome: "applied",
|
|
9469
|
+
label: "Joined",
|
|
9470
|
+
settledAt
|
|
9471
|
+
};
|
|
9472
|
+
}
|
|
9339
9473
|
};
|
|
9340
9474
|
}
|
|
9341
|
-
|
|
9342
|
-
|
|
9343
|
-
|
|
9344
|
-
|
|
9345
|
-
|
|
9346
|
-
|
|
9347
|
-
|
|
9348
|
-
body: {
|
|
9349
|
-
subject: input.subject,
|
|
9350
|
-
body: input.body,
|
|
9351
|
-
recipient_keys: [...input.recipientKeys],
|
|
9352
|
-
dry_run: input.dryRun === true
|
|
9353
|
-
}
|
|
9354
|
-
});
|
|
9355
|
-
if (!response.ok) throw await refusalOf(response, operation);
|
|
9356
|
-
const body = await response.json();
|
|
9475
|
+
var MEETING_INVITE_KIND = "meet.meeting-invite";
|
|
9476
|
+
var MEETING_INVITE_VERSION = 1;
|
|
9477
|
+
function meetingInviteAction(payload) {
|
|
9478
|
+
return { kind: MEETING_INVITE_KIND, version: MEETING_INVITE_VERSION, payload };
|
|
9479
|
+
}
|
|
9480
|
+
function createMeetingInviteHandler(options) {
|
|
9481
|
+
const now = options.now ?? (() => Date.now());
|
|
9357
9482
|
return {
|
|
9358
|
-
|
|
9359
|
-
|
|
9360
|
-
|
|
9361
|
-
|
|
9362
|
-
|
|
9363
|
-
|
|
9483
|
+
kind: MEETING_INVITE_KIND,
|
|
9484
|
+
versions: [MEETING_INVITE_VERSION],
|
|
9485
|
+
// A DURABLE LINK NEVER EXPIRES (R3) — the chip stays useful after the
|
|
9486
|
+
// meeting too, because that is when people go looking for the notes.
|
|
9487
|
+
choices: () => [{ id: "open", label: "Open meeting", tone: "primary" }],
|
|
9488
|
+
summarize: (payload) => payload.scheduled_for === null ? payload.title : `${payload.title} \u2014 ${payload.scheduled_for}`,
|
|
9489
|
+
execute: async (payload, context) => {
|
|
9490
|
+
if (payload.slug.length === 0) {
|
|
9491
|
+
throw new MeetError(
|
|
9492
|
+
"invalid-response",
|
|
9493
|
+
"meet.meeting-invite: the payload carries no slug",
|
|
9494
|
+
"A meeting invitation without a durable slug cannot be opened. Fix the sender; never synthesize a link on the receiving side."
|
|
9495
|
+
);
|
|
9496
|
+
}
|
|
9497
|
+
await options.onOpen(payload);
|
|
9498
|
+
return {
|
|
9499
|
+
kind: MEETING_INVITE_KIND,
|
|
9500
|
+
messageId: context.messageId,
|
|
9501
|
+
actorId: context.actorId,
|
|
9502
|
+
outcome: "applied",
|
|
9503
|
+
label: "Opened",
|
|
9504
|
+
settledAt: new Date(now()).toISOString()
|
|
9505
|
+
};
|
|
9506
|
+
}
|
|
9364
9507
|
};
|
|
9365
9508
|
}
|
|
9366
9509
|
|
|
9367
9510
|
// src/core/management.ts
|
|
9368
|
-
|
|
9369
|
-
|
|
9370
|
-
|
|
9371
|
-
|
|
9372
|
-
|
|
9373
|
-
body = null;
|
|
9374
|
-
}
|
|
9375
|
-
const said = readServerRefusal(body);
|
|
9376
|
-
return new MeetError(
|
|
9377
|
-
response.status === 403 || response.status === 401 ? "forbidden" : response.status === 404 ? "not-found" : response.status === 409 ? "conflict" : "transport",
|
|
9378
|
-
`${operation}: ${said?.reason ?? `the server answered ${response.status}`}`,
|
|
9379
|
-
said?.remedy || "The meeting itself is saved \u2014 only the telling failed. Try again; the call is idempotent."
|
|
9511
|
+
function refusal(response, operation) {
|
|
9512
|
+
return refusalFromResponse(
|
|
9513
|
+
response,
|
|
9514
|
+
operation,
|
|
9515
|
+
"The meeting itself is saved \u2014 only the telling failed. Try again; the call is idempotent."
|
|
9380
9516
|
);
|
|
9381
9517
|
}
|
|
9382
9518
|
function count(body, key) {
|