@arbidocs/react 0.5.121 → 0.5.122

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.
@@ -1,5 +1,5 @@
1
1
  import { jsx, jsxs } from "react/jsx-runtime";
2
- import { AppWindow, FileText, Image as ImageIcon } from "lucide-react";
2
+ import { AppWindow, FileText, Image as ImageIcon, Presentation } from "lucide-react";
3
3
  import { Card } from "../ui/card.js";
4
4
  const LABELS = {
5
5
  document: {
@@ -16,6 +16,13 @@ const LABELS = {
16
16
  action: "Open preview",
17
17
  untitled: "Untitled app"
18
18
  },
19
+ presentation: {
20
+ Icon: Presentation,
21
+ busy: "Building slides\u2026",
22
+ ready: "Slides saved",
23
+ action: "Open slides",
24
+ untitled: "Untitled presentation"
25
+ },
19
26
  image: {
20
27
  Icon: ImageIcon,
21
28
  busy: "Generating\u2026",
@@ -26,6 +33,7 @@ const LABELS = {
26
33
  };
27
34
  function labelsFor(mode) {
28
35
  if (mode === "image") return LABELS.image;
36
+ if (mode === "presentation") return LABELS.presentation;
29
37
  if (mode === "html" || mode === "react") return LABELS.live;
30
38
  return LABELS.document;
31
39
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/ai/MessageArtifactCard.tsx"],
4
- "sourcesContent": ["import React from 'react'\nimport { AppWindow, FileText, Image as ImageIcon } from 'lucide-react'\nimport { Card } from '../ui/card'\n\n/**\n * The minimal artifact shape this card renders. A structural subset of the\n * ARBI app's `StreamingDocument`, defined here so the light package doesn't\n * depend on the app store \u2014 any object carrying these fields is accepted.\n */\nexport interface MessageArtifact {\n /** External id of the drafted document; opened in the docked viewer. */\n docExtId: string\n /** Display title; falls back when empty. */\n title: string\n /** Whether generation has finished. */\n isComplete: boolean\n /**\n * Content mode. `markdown` / `text` are document drafts, which the server stores as Word\n * documents (.docx); `html` / `react` are live UI artifacts; `image` is a generated image.\n */\n mode: 'markdown' | 'html' | 'text' | 'react' | 'image'\n}\n\nconst LABELS = {\n document: {\n Icon: FileText,\n busy: 'Drafting\u2026',\n ready: 'Draft ready',\n action: 'Open in Editor',\n untitled: 'Untitled draft',\n },\n live: {\n Icon: AppWindow,\n busy: 'Building\u2026',\n ready: 'Preview ready',\n action: 'Open preview',\n untitled: 'Untitled app',\n },\n image: {\n Icon: ImageIcon,\n busy: 'Generating\u2026',\n ready: 'Image ready',\n action: 'View image',\n untitled: 'Generated image',\n },\n} as const\n\nfunction labelsFor(mode: MessageArtifact['mode']) {\n if (mode === 'image') return LABELS.image\n if (mode === 'html' || mode === 'react') return LABELS.live\n return LABELS.document\n}\n\ninterface MessageArtifactCardProps {\n /** The streaming/drafted document linked to this assistant message. */\n artifact: MessageArtifact\n /** Opens the drafted document (docExtId) in the docked viewer. */\n onOpen: (docExtId: string) => void\n}\n\n/**\n * Inline \"Open in Editor\" (document drafts) / \"Open preview\" / \"View image\" affordance shown inside the assistant message that\n * produced a drafted document \u2014 so the artifact lives in the conversation\n * narrative (dyad-style) instead of only a hidden docked panel.\n *\n * Clean-ARBI contract: flat outline card, one chat type size, brand\n * accent on the action, tap-friendly (a real button \u2014 no hover-only).\n */\nexport function MessageArtifactCard({\n artifact,\n onOpen,\n}: MessageArtifactCardProps): React.JSX.Element {\n const { docExtId, title, isComplete, mode } = artifact\n const labels = labelsFor(mode)\n const Icon = labels.Icon\n return (\n <Card\n variant=\"outline\"\n className=\"mt-3 flex items-center gap-3 px-3 py-2.5\"\n data-testid={`message-artifact-card-${docExtId}`}\n >\n <Icon className=\"h-4 w-4 shrink-0 text-muted-foreground\" aria-hidden />\n <div className=\"min-w-0 flex-1\">\n <div\n className=\"truncate text-chat font-semibold text-foreground\"\n data-testid=\"artifact-title\"\n >\n {title || labels.untitled}\n </div>\n <div className=\"text-sm text-muted-foreground\" data-testid=\"artifact-status\">\n {isComplete ? labels.ready : labels.busy}\n </div>\n </div>\n <button\n type=\"button\"\n onClick={() => onOpen(docExtId)}\n className=\"shrink-0 rounded-md bg-secondary/15 px-3 py-1.5 text-sm font-semibold text-secondary transition-colors hover:bg-secondary/25\"\n data-testid={`view-artifact-button-${docExtId}`}\n >\n {labels.action}\n </button>\n </Card>\n )\n}\n"],
5
- "mappings": "AAiFM,cACA,YADA;AAhFN,SAAS,WAAW,UAAU,SAAS,iBAAiB;AACxD,SAAS,YAAY;AAqBrB,MAAM,SAAS;AAAA,EACb,UAAU;AAAA,IACR,MAAM;AAAA,IACN,MAAM;AAAA,IACN,OAAO;AAAA,IACP,QAAQ;AAAA,IACR,UAAU;AAAA,EACZ;AAAA,EACA,MAAM;AAAA,IACJ,MAAM;AAAA,IACN,MAAM;AAAA,IACN,OAAO;AAAA,IACP,QAAQ;AAAA,IACR,UAAU;AAAA,EACZ;AAAA,EACA,OAAO;AAAA,IACL,MAAM;AAAA,IACN,MAAM;AAAA,IACN,OAAO;AAAA,IACP,QAAQ;AAAA,IACR,UAAU;AAAA,EACZ;AACF;AAEA,SAAS,UAAU,MAA+B;AAChD,MAAI,SAAS,QAAS,QAAO,OAAO;AACpC,MAAI,SAAS,UAAU,SAAS,QAAS,QAAO,OAAO;AACvD,SAAO,OAAO;AAChB;AAiBO,SAAS,oBAAoB;AAAA,EAClC;AAAA,EACA;AACF,GAAgD;AAC9C,QAAM,EAAE,UAAU,OAAO,YAAY,KAAK,IAAI;AAC9C,QAAM,SAAS,UAAU,IAAI;AAC7B,QAAM,OAAO,OAAO;AACpB,SACE;AAAA,IAAC;AAAA;AAAA,MACC,SAAQ;AAAA,MACR,WAAU;AAAA,MACV,eAAa,yBAAyB,QAAQ;AAAA,MAE9C;AAAA,4BAAC,QAAK,WAAU,0CAAyC,eAAW,MAAC;AAAA,QACrE,qBAAC,SAAI,WAAU,kBACb;AAAA;AAAA,YAAC;AAAA;AAAA,cACC,WAAU;AAAA,cACV,eAAY;AAAA,cAEX,mBAAS,OAAO;AAAA;AAAA,UACnB;AAAA,UACA,oBAAC,SAAI,WAAU,iCAAgC,eAAY,mBACxD,uBAAa,OAAO,QAAQ,OAAO,MACtC;AAAA,WACF;AAAA,QACA;AAAA,UAAC;AAAA;AAAA,YACC,MAAK;AAAA,YACL,SAAS,MAAM,OAAO,QAAQ;AAAA,YAC9B,WAAU;AAAA,YACV,eAAa,wBAAwB,QAAQ;AAAA,YAE5C,iBAAO;AAAA;AAAA,QACV;AAAA;AAAA;AAAA,EACF;AAEJ;",
4
+ "sourcesContent": ["import React from 'react'\nimport { AppWindow, FileText, Image as ImageIcon, Presentation } from 'lucide-react'\nimport type { ArtifactEvent } from '@arbidocs/sdk/browser'\nimport { Card } from '../ui/card'\n\n/**\n * The minimal artifact shape this card renders. A structural subset of the\n * ARBI app's `StreamingDocument`, defined here so the light package doesn't\n * depend on the app store \u2014 any object carrying these fields is accepted.\n */\nexport interface MessageArtifact {\n /** External id of the drafted document; opened in the docked viewer. */\n docExtId: string\n /** Display title; falls back when empty. */\n title: string\n /** Whether generation has finished. */\n isComplete: boolean\n /**\n * Content mode. `markdown` / `text` are document drafts, which the server stores as Word\n * documents (.docx); `html` / `react` are live UI artifacts; `image` is a generated image;\n * `presentation` is a slide deck (.pptx), opened in the slides editor.\n */\n mode: ArtifactEvent['mode']\n}\n\nconst LABELS = {\n document: {\n Icon: FileText,\n busy: 'Drafting\u2026',\n ready: 'Draft ready',\n action: 'Open in Editor',\n untitled: 'Untitled draft',\n },\n live: {\n Icon: AppWindow,\n busy: 'Building\u2026',\n ready: 'Preview ready',\n action: 'Open preview',\n untitled: 'Untitled app',\n },\n presentation: {\n Icon: Presentation,\n busy: 'Building slides\u2026',\n ready: 'Slides saved',\n action: 'Open slides',\n untitled: 'Untitled presentation',\n },\n image: {\n Icon: ImageIcon,\n busy: 'Generating\u2026',\n ready: 'Image ready',\n action: 'View image',\n untitled: 'Generated image',\n },\n} as const\n\nfunction labelsFor(mode: MessageArtifact['mode']) {\n if (mode === 'image') return LABELS.image\n if (mode === 'presentation') return LABELS.presentation\n if (mode === 'html' || mode === 'react') return LABELS.live\n return LABELS.document\n}\n\ninterface MessageArtifactCardProps {\n /** The streaming/drafted document linked to this assistant message. */\n artifact: MessageArtifact\n /** Opens the drafted document (docExtId) in the docked viewer. */\n onOpen: (docExtId: string) => void\n}\n\n/**\n * Inline \"Open in Editor\" (document drafts) / \"Open preview\" / \"View image\" affordance shown inside the assistant message that\n * produced a drafted document \u2014 so the artifact lives in the conversation\n * narrative (dyad-style) instead of only a hidden docked panel.\n *\n * Clean-ARBI contract: flat outline card, one chat type size, brand\n * accent on the action, tap-friendly (a real button \u2014 no hover-only).\n */\nexport function MessageArtifactCard({\n artifact,\n onOpen,\n}: MessageArtifactCardProps): React.JSX.Element {\n const { docExtId, title, isComplete, mode } = artifact\n const labels = labelsFor(mode)\n const Icon = labels.Icon\n return (\n <Card\n variant=\"outline\"\n className=\"mt-3 flex items-center gap-3 px-3 py-2.5\"\n data-testid={`message-artifact-card-${docExtId}`}\n >\n <Icon className=\"h-4 w-4 shrink-0 text-muted-foreground\" aria-hidden />\n <div className=\"min-w-0 flex-1\">\n <div\n className=\"truncate text-chat font-semibold text-foreground\"\n data-testid=\"artifact-title\"\n >\n {title || labels.untitled}\n </div>\n <div className=\"text-sm text-muted-foreground\" data-testid=\"artifact-status\">\n {isComplete ? labels.ready : labels.busy}\n </div>\n </div>\n <button\n type=\"button\"\n onClick={() => onOpen(docExtId)}\n className=\"shrink-0 rounded-md bg-secondary/15 px-3 py-1.5 text-sm font-semibold text-secondary transition-colors hover:bg-secondary/25\"\n data-testid={`view-artifact-button-${docExtId}`}\n >\n {labels.action}\n </button>\n </Card>\n )\n}\n"],
5
+ "mappings": "AA2FM,cACA,YADA;AA1FN,SAAS,WAAW,UAAU,SAAS,WAAW,oBAAoB;AAEtE,SAAS,YAAY;AAsBrB,MAAM,SAAS;AAAA,EACb,UAAU;AAAA,IACR,MAAM;AAAA,IACN,MAAM;AAAA,IACN,OAAO;AAAA,IACP,QAAQ;AAAA,IACR,UAAU;AAAA,EACZ;AAAA,EACA,MAAM;AAAA,IACJ,MAAM;AAAA,IACN,MAAM;AAAA,IACN,OAAO;AAAA,IACP,QAAQ;AAAA,IACR,UAAU;AAAA,EACZ;AAAA,EACA,cAAc;AAAA,IACZ,MAAM;AAAA,IACN,MAAM;AAAA,IACN,OAAO;AAAA,IACP,QAAQ;AAAA,IACR,UAAU;AAAA,EACZ;AAAA,EACA,OAAO;AAAA,IACL,MAAM;AAAA,IACN,MAAM;AAAA,IACN,OAAO;AAAA,IACP,QAAQ;AAAA,IACR,UAAU;AAAA,EACZ;AACF;AAEA,SAAS,UAAU,MAA+B;AAChD,MAAI,SAAS,QAAS,QAAO,OAAO;AACpC,MAAI,SAAS,eAAgB,QAAO,OAAO;AAC3C,MAAI,SAAS,UAAU,SAAS,QAAS,QAAO,OAAO;AACvD,SAAO,OAAO;AAChB;AAiBO,SAAS,oBAAoB;AAAA,EAClC;AAAA,EACA;AACF,GAAgD;AAC9C,QAAM,EAAE,UAAU,OAAO,YAAY,KAAK,IAAI;AAC9C,QAAM,SAAS,UAAU,IAAI;AAC7B,QAAM,OAAO,OAAO;AACpB,SACE;AAAA,IAAC;AAAA;AAAA,MACC,SAAQ;AAAA,MACR,WAAU;AAAA,MACV,eAAa,yBAAyB,QAAQ;AAAA,MAE9C;AAAA,4BAAC,QAAK,WAAU,0CAAyC,eAAW,MAAC;AAAA,QACrE,qBAAC,SAAI,WAAU,kBACb;AAAA;AAAA,YAAC;AAAA;AAAA,cACC,WAAU;AAAA,cACV,eAAY;AAAA,cAEX,mBAAS,OAAO;AAAA;AAAA,UACnB;AAAA,UACA,oBAAC,SAAI,WAAU,iCAAgC,eAAY,mBACxD,uBAAa,OAAO,QAAQ,OAAO,MACtC;AAAA,WACF;AAAA,QACA;AAAA,UAAC;AAAA;AAAA,YACC,MAAK;AAAA,YACL,SAAS,MAAM,OAAO,QAAQ;AAAA,YAC9B,WAAU;AAAA,YACV,eAAa,wBAAwB,QAAQ;AAAA,YAE5C,iBAAO;AAAA;AAAA,QACV;AAAA;AAAA;AAAA,EACF;AAEJ;",
6
6
  "names": []
7
7
  }
@@ -1,9 +1,9 @@
1
1
  'use strict';
2
2
 
3
- var chunkX5OZRLFZ_cjs = require('../../chunk-X5OZRLFZ.cjs');
3
+ var chunkFAJLJDWS_cjs = require('../../chunk-FAJLJDWS.cjs');
4
4
  require('../../chunk-7L7QRIQ2.cjs');
5
5
  var chunkO32T7XRD_cjs = require('../../chunk-O32T7XRD.cjs');
6
- var chunkRLSBRZ6Z_cjs = require('../../chunk-RLSBRZ6Z.cjs');
6
+ var chunkIX4HOS5Z_cjs = require('../../chunk-IX4HOS5Z.cjs');
7
7
  require('../../chunk-N73IMVFF.cjs');
8
8
  require('../../chunk-NDIKMOBJ.cjs');
9
9
  require('../../chunk-VOFNX4P6.cjs');
@@ -83,7 +83,7 @@ function useMuteRequests() {
83
83
  const onData = (payload, from, _kind, topic) => {
84
84
  if (topic !== MUTE_REQUEST_TOPIC || !from) return;
85
85
  const who = from.name || from.identity;
86
- chunkRLSBRZ6Z_cjs.notify.info(`${who} asks you to mute your microphone`, {
86
+ chunkIX4HOS5Z_cjs.notify.info(`${who} asks you to mute your microphone`, {
87
87
  action: {
88
88
  label: "Mute",
89
89
  onClick: () => void room.localParticipant.setMicrophoneEnabled(false)
@@ -99,7 +99,7 @@ function useMuteRequests() {
99
99
  reliable: true,
100
100
  topic: MUTE_REQUEST_TOPIC,
101
101
  destinationIdentities: [identity]
102
- }).then(() => chunkRLSBRZ6Z_cjs.notify.info("Asked to mute")).catch((err) => chunkRLSBRZ6Z_cjs.notify.error(`Could not ask: ${err.message}`));
102
+ }).then(() => chunkIX4HOS5Z_cjs.notify.info("Asked to mute")).catch((err) => chunkIX4HOS5Z_cjs.notify.error(`Could not ask: ${err.message}`));
103
103
  }
104
104
  var FINAL_SEGMENT_GRACE_MS = 3e4;
105
105
  function mayStillSend(params) {
@@ -123,8 +123,8 @@ function useCallTranscriber({
123
123
  const arbi = chunkO32T7XRD_cjs.useArbi();
124
124
  const queryClient = reactQuery.useQueryClient();
125
125
  const profile = arbi.isLoggedIn ? arbi.user.profile() : null;
126
- const myProfileName = profile && chunkX5OZRLFZ_cjs.toProfileView(profile).fullName || null;
127
- const deploymentAvailable = chunkX5OZRLFZ_cjs.useCallTranscriptionAvailable();
126
+ const myProfileName = profile && chunkFAJLJDWS_cjs.toProfileView(profile).fullName || null;
127
+ const deploymentAvailable = chunkFAJLJDWS_cjs.useCallTranscriptionAvailable();
128
128
  const available = deploymentAvailable && !!callKey;
129
129
  const [state, setState] = react.useState(null);
130
130
  const [creatorExtId, setCreatorExtId] = react.useState(null);
@@ -167,7 +167,7 @@ function useCallTranscriber({
167
167
  };
168
168
  }, [available, callExtId]);
169
169
  react.useEffect(
170
- () => chunkX5OZRLFZ_cjs.onCallEvent("call.transcription", (message) => {
170
+ () => chunkFAJLJDWS_cjs.onCallEvent("call.transcription", (message) => {
171
171
  if (message.call_ext_id === callExtId) applyState(message);
172
172
  }),
173
173
  [callExtId, applyState]
@@ -183,7 +183,7 @@ function useCallTranscriber({
183
183
  const persist = react.useCallback(
184
184
  (segment, speakerNeedsProxy) => {
185
185
  if (!callKey || persisted.current.has(segment.segmentId)) return;
186
- const role = chunkX5OZRLFZ_cjs.transcriptPersistRole({
186
+ const role = chunkFAJLJDWS_cjs.transcriptPersistRole({
187
187
  myIdentity,
188
188
  canPost: canPost && (segment.speakerIdentity !== myIdentity || selfPosts.current),
189
189
  hasCallKey: true,
@@ -192,7 +192,7 @@ function useCallTranscriber({
192
192
  });
193
193
  if (!role) return;
194
194
  persisted.current.add(segment.segmentId);
195
- chunkX5OZRLFZ_cjs.persistTranscriptSegment(arbi, queryClient, callExtId, callKey, segment).catch(
195
+ chunkFAJLJDWS_cjs.persistTranscriptSegment(arbi, queryClient, callExtId, callKey, segment).catch(
196
196
  (err) => {
197
197
  persisted.current.delete(segment.segmentId);
198
198
  if (role === "own" && err?.status === 403) {
@@ -201,7 +201,7 @@ function useCallTranscriber({
201
201
  console.warn("[calls] could not keep the transcript line", err);
202
202
  if (!saveFailureShown.current) {
203
203
  saveFailureShown.current = true;
204
- chunkRLSBRZ6Z_cjs.notify.warning(
204
+ chunkIX4HOS5Z_cjs.notify.warning(
205
205
  `Transcript lines are not being saved to the meeting chat: ${err?.message ?? "unknown error"}`
206
206
  );
207
207
  }
@@ -212,7 +212,7 @@ function useCallTranscriber({
212
212
  );
213
213
  react.useEffect(() => {
214
214
  const onData = (payload, participant, _kind, topic, _encryption, participantIdentity) => {
215
- if (topic === chunkX5OZRLFZ_cjs.CALL_TRANSCRIPTION_TOPIC) {
215
+ if (topic === chunkFAJLJDWS_cjs.CALL_TRANSCRIPTION_TOPIC) {
216
216
  if (participant || participantIdentity) return;
217
217
  try {
218
218
  const next = browser.calls.transcriptionStateFrom(JSON.parse(new TextDecoder().decode(payload)));
@@ -221,10 +221,10 @@ function useCallTranscriber({
221
221
  }
222
222
  return;
223
223
  }
224
- if (topic !== chunkX5OZRLFZ_cjs.CALL_CAPTION_TOPIC || !callKey) return;
224
+ if (topic !== chunkFAJLJDWS_cjs.CALL_CAPTION_TOPIC || !callKey) return;
225
225
  const identity = participant?.identity ?? participantIdentity;
226
226
  if (!identity || identity === myIdentity) return;
227
- void chunkX5OZRLFZ_cjs.decodeCaptionPacket(payload, callKey).then((packet) => {
227
+ void chunkFAJLJDWS_cjs.decodeCaptionPacket(payload, callKey).then((packet) => {
228
228
  if (!packet || packet.identity !== identity) return;
229
229
  const name = participant?.name || packet.name || identity;
230
230
  const t = Math.min(packet.t, Date.now());
@@ -264,7 +264,7 @@ function useCallTranscriber({
264
264
  })) {
265
265
  return;
266
266
  }
267
- const segmentId = chunkX5OZRLFZ_cjs.newSegmentId();
267
+ const segmentId = chunkFAJLJDWS_cjs.newSegmentId();
268
268
  const startedAt = new Date(segment.startedAt).toISOString();
269
269
  let text;
270
270
  try {
@@ -298,7 +298,7 @@ function useCallTranscriber({
298
298
  });
299
299
  try {
300
300
  await localParticipant.publishData(
301
- await chunkX5OZRLFZ_cjs.encodeCaptionPacket(
301
+ await chunkFAJLJDWS_cjs.encodeCaptionPacket(
302
302
  {
303
303
  v: 1,
304
304
  segment_id: segmentId,
@@ -310,7 +310,7 @@ function useCallTranscriber({
310
310
  },
311
311
  callKey
312
312
  ),
313
- { reliable: true, topic: chunkX5OZRLFZ_cjs.CALL_CAPTION_TOPIC }
313
+ { reliable: true, topic: chunkFAJLJDWS_cjs.CALL_CAPTION_TOPIC }
314
314
  );
315
315
  } catch (err) {
316
316
  console.warn("[calls] could not broadcast a caption", err);
@@ -346,7 +346,7 @@ function useCallTranscriber({
346
346
  answer ?? (next ? { on: true, by_identity: myIdentity, by_name: localParticipant.name || null } : { on: false, by_identity: null, by_name: null })
347
347
  );
348
348
  } catch (err) {
349
- chunkRLSBRZ6Z_cjs.notify.error(`Could not turn transcription ${next ? "on" : "off"}: ${err.message}`);
349
+ chunkIX4HOS5Z_cjs.notify.error(`Could not turn transcription ${next ? "on" : "off"}: ${err.message}`);
350
350
  } finally {
351
351
  setBusy(false);
352
352
  }
@@ -408,7 +408,7 @@ function useCallRecorder({
408
408
  const applyState = react.useCallback((next) => {
409
409
  liveUpdate.current = true;
410
410
  if (stateRef.current?.on && next && !next.on && next.reason === "storage_quota_exceeded") {
411
- chunkRLSBRZ6Z_cjs.notify.warning("Recording stopped: the storage is full.");
411
+ chunkIX4HOS5Z_cjs.notify.warning("Recording stopped: the storage is full.");
412
412
  }
413
413
  stateRef.current = next;
414
414
  setState(next);
@@ -425,14 +425,14 @@ function useCallRecorder({
425
425
  };
426
426
  }, [callExtId]);
427
427
  react.useEffect(
428
- () => chunkX5OZRLFZ_cjs.onCallEvent("call.recording", (message) => {
428
+ () => chunkFAJLJDWS_cjs.onCallEvent("call.recording", (message) => {
429
429
  if (message.call_ext_id === callExtId) applyState(message);
430
430
  }),
431
431
  [callExtId, applyState]
432
432
  );
433
433
  react.useEffect(() => {
434
434
  const onData = (payload, participant, _kind, topic, _encryption, participantIdentity) => {
435
- if (topic !== chunkX5OZRLFZ_cjs.CALL_RECORDING_TOPIC) return;
435
+ if (topic !== chunkFAJLJDWS_cjs.CALL_RECORDING_TOPIC) return;
436
436
  if (participant || participantIdentity) return;
437
437
  try {
438
438
  const next = browser.calls.recordingStateFrom(JSON.parse(new TextDecoder().decode(payload)));
@@ -453,13 +453,13 @@ function useCallRecorder({
453
453
  try {
454
454
  const row = call ?? await arbi.calls.get(callExtId);
455
455
  if (!row.workspace_locked && !row.chat_ext_id && callKey) {
456
- const ref = await chunkX5OZRLFZ_cjs.ensureMeetingChat(arbi, queryClient, callExtId, callKey);
456
+ const ref = await chunkFAJLJDWS_cjs.ensureMeetingChat(arbi, queryClient, callExtId, callKey);
457
457
  if (ref.kind === "chat") setCall({ ...row, chat_ext_id: ref.chatExtId });
458
458
  }
459
459
  const answer = await arbi.calls.recording.start(callExtId, mode);
460
460
  if (answer) applyState(answer);
461
461
  } catch (err) {
462
- chunkRLSBRZ6Z_cjs.notify.error(`Could not start recording: ${err.message}`);
462
+ chunkIX4HOS5Z_cjs.notify.error(`Could not start recording: ${err.message}`);
463
463
  } finally {
464
464
  setBusy(false);
465
465
  }
@@ -498,7 +498,7 @@ function useCallRecorder({
498
498
  const answer = await arbi.calls.recording.stop(callExtId);
499
499
  applyState(answer ?? { ...state, on: false });
500
500
  } catch (err) {
501
- chunkRLSBRZ6Z_cjs.notify.error(`Could not stop recording: ${err.message}`);
501
+ chunkIX4HOS5Z_cjs.notify.error(`Could not stop recording: ${err.message}`);
502
502
  } finally {
503
503
  setBusy(false);
504
504
  }
@@ -555,7 +555,7 @@ function useTrackUploader(recording, callKey, kind, mimeType) {
555
555
  const queryClient = reactQuery.useQueryClient();
556
556
  return react.useMemo(() => {
557
557
  if (!shouldCapture || !recordingExtId || !call) return null;
558
- return chunkX5OZRLFZ_cjs.trackUploaderFor({
558
+ return chunkFAJLJDWS_cjs.trackUploaderFor({
559
559
  arbi,
560
560
  callExtId: call.external_id,
561
561
  recordingExtId,
@@ -563,7 +563,7 @@ function useTrackUploader(recording, callKey, kind, mimeType) {
563
563
  mimeType,
564
564
  recordingStartedAt: startedAt,
565
565
  linkVersion,
566
- conversationKey: () => chunkX5OZRLFZ_cjs.conversationKeyForCall(arbi, queryClient, call, { callKey }),
566
+ conversationKey: () => chunkFAJLJDWS_cjs.conversationKeyForCall(arbi, queryClient, call, { callKey }),
567
567
  // The server stopped the recording at the quota (its state event says so too).
568
568
  onStopped: () => markStopped()
569
569
  });
@@ -612,10 +612,10 @@ function useInCallChat({
612
612
  react.useEffect(() => {
613
613
  if (!callKey) return;
614
614
  const onData = (payload, participant, _kind, topic, _encryption, participantIdentity) => {
615
- if (topic !== chunkX5OZRLFZ_cjs.CALL_CHAT_TOPIC) return;
615
+ if (topic !== chunkFAJLJDWS_cjs.CALL_CHAT_TOPIC) return;
616
616
  const identity = participant?.identity ?? participantIdentity;
617
617
  if (!identity || identity === myIdentity) return;
618
- void chunkX5OZRLFZ_cjs.decodeCallChatPacket(payload, callKey).then((packet) => {
618
+ void chunkFAJLJDWS_cjs.decodeCallChatPacket(payload, callKey).then((packet) => {
619
619
  if (!packet) return;
620
620
  const key = `${identity}:${packet.id}`;
621
621
  if (seen.current.has(key)) return;
@@ -645,7 +645,7 @@ function useInCallChat({
645
645
  if (!open || !canRead || historyRequested.current) return;
646
646
  historyRequested.current = true;
647
647
  setHistoryLoading(true);
648
- chunkX5OZRLFZ_cjs.loadMeetingHistory(arbi, queryClient, callExtId).then((result) => {
648
+ chunkFAJLJDWS_cjs.loadMeetingHistory(arbi, queryClient, callExtId).then((result) => {
649
649
  if (!result) return;
650
650
  setMeetingChat(result.ref);
651
651
  setHistory(result.messages);
@@ -656,13 +656,13 @@ function useInCallChat({
656
656
  }, [open, canRead, callExtId]);
657
657
  const send = react.useCallback(
658
658
  async (raw) => {
659
- const text = raw.trim().slice(0, chunkX5OZRLFZ_cjs.CALL_CHAT_MAX_CHARS);
659
+ const text = raw.trim().slice(0, chunkFAJLJDWS_cjs.CALL_CHAT_MAX_CHARS);
660
660
  if (!text || !callKey || readOnly) return;
661
- const id = chunkX5OZRLFZ_cjs.newPacketId();
661
+ const id = chunkFAJLJDWS_cjs.newPacketId();
662
662
  const at = Date.now();
663
663
  await localParticipant.publishData(
664
- await chunkX5OZRLFZ_cjs.encodeCallChatPacket({ v: 1, id, text, at }, callKey),
665
- { reliable: true, topic: chunkX5OZRLFZ_cjs.CALL_CHAT_TOPIC }
664
+ await chunkFAJLJDWS_cjs.encodeCallChatPacket({ v: 1, id, text, at }, callKey),
665
+ { reliable: true, topic: chunkFAJLJDWS_cjs.CALL_CHAT_TOPIC }
666
666
  );
667
667
  setLive((prev) => [
668
668
  ...prev,
@@ -681,7 +681,7 @@ function useInCallChat({
681
681
  (prev) => prev.map((l) => l.id === id && l.senderIdentity === myIdentity ? { ...l, status } : l)
682
682
  );
683
683
  try {
684
- setMeetingChat(await chunkX5OZRLFZ_cjs.persistMeetingMessage(arbi, queryClient, callExtId, callKey, text));
684
+ setMeetingChat(await chunkFAJLJDWS_cjs.persistMeetingMessage(arbi, queryClient, callExtId, callKey, text));
685
685
  mark("saved");
686
686
  } catch (err) {
687
687
  console.warn("[calls] could not keep the message in the meeting chat", err);
@@ -693,15 +693,15 @@ function useInCallChat({
693
693
  const openMeeting = react.useCallback(async () => {
694
694
  if (!callKey) return;
695
695
  try {
696
- const ref = meetingChat ?? await chunkX5OZRLFZ_cjs.ensureMeetingChat(arbi, queryClient, callExtId, callKey);
696
+ const ref = meetingChat ?? await chunkFAJLJDWS_cjs.ensureMeetingChat(arbi, queryClient, callExtId, callKey);
697
697
  setMeetingChat(ref);
698
698
  openMeetingChat?.(ref);
699
699
  } catch (err) {
700
- chunkRLSBRZ6Z_cjs.notify.error(`Could not open the meeting chat: ${err.message}`);
700
+ chunkIX4HOS5Z_cjs.notify.error(`Could not open the meeting chat: ${err.message}`);
701
701
  }
702
702
  }, [callExtId, callKey, meetingChat]);
703
703
  const lines = react.useMemo(
704
- () => chunkX5OZRLFZ_cjs.mergeChatLines(history, live, {
704
+ () => chunkFAJLJDWS_cjs.mergeChatLines(history, live, {
705
705
  myIdentity,
706
706
  nameOf: (identity) => identity === myIdentity ? localParticipant.name || "You" : room.remoteParticipants.get(identity)?.name || null
707
707
  }),
@@ -723,7 +723,7 @@ function useInCallChat({
723
723
  var DEMOTE_REJOIN_FALLBACK_MS = 5e3;
724
724
  function failed(what) {
725
725
  return (err) => {
726
- chunkRLSBRZ6Z_cjs.notify.error(`Could not ${what}: ${err?.message ?? "unknown error"}`);
726
+ chunkIX4HOS5Z_cjs.notify.error(`Could not ${what}: ${err?.message ?? "unknown error"}`);
727
727
  throw err;
728
728
  };
729
729
  }
@@ -760,7 +760,7 @@ function useWebinar({
760
760
  const n = ++questionsRead.current;
761
761
  try {
762
762
  const rows = await webinarApi().questions.list(callExtId, linkVersion);
763
- const opened = await Promise.all(rows.map((q) => chunkX5OZRLFZ_cjs.openQuestion(q, callKey)));
763
+ const opened = await Promise.all(rows.map((q) => chunkFAJLJDWS_cjs.openQuestion(q, callKey)));
764
764
  if (n === questionsRead.current) setQuestions(opened);
765
765
  } catch (err) {
766
766
  console.warn("[calls] could not load the questions", err);
@@ -772,7 +772,7 @@ function useWebinar({
772
772
  }, [refresh, refreshQuestions]);
773
773
  react.useEffect(() => {
774
774
  if (!enabled) return;
775
- return chunkX5OZRLFZ_cjs.onCallEvent("call.webinar", (m) => {
775
+ return chunkFAJLJDWS_cjs.onCallEvent("call.webinar", (m) => {
776
776
  if (m.call_ext_id !== callExtId) return;
777
777
  setState((s) => s ? { ...s, live: m.live, chat: m.chat } : s);
778
778
  });
@@ -781,8 +781,8 @@ function useWebinar({
781
781
  react.useEffect(() => {
782
782
  if (!enabled) return;
783
783
  const onData = (payload, participant, _kind, topic, _encryption, participantIdentity) => {
784
- if (topic !== chunkX5OZRLFZ_cjs.CALL_WEBINAR_TOPIC) return;
785
- const packet = chunkX5OZRLFZ_cjs.parseWebinarPacket(payload, !!(participant || participantIdentity));
784
+ if (topic !== chunkFAJLJDWS_cjs.CALL_WEBINAR_TOPIC) return;
785
+ const packet = chunkFAJLJDWS_cjs.parseWebinarPacket(payload, !!(participant || participantIdentity));
786
786
  if (!packet) return;
787
787
  switch (packet.type) {
788
788
  case "state":
@@ -803,7 +803,7 @@ function useWebinar({
803
803
  return;
804
804
  case "demoted":
805
805
  setStageCheck(false);
806
- chunkX5OZRLFZ_cjs.markRejoining(callExtId);
806
+ chunkFAJLJDWS_cjs.markRejoining(callExtId);
807
807
  if (fallback.current) clearTimeout(fallback.current);
808
808
  fallback.current = setTimeout(() => void room.disconnect(), DEMOTE_REJOIN_FALLBACK_MS);
809
809
  return;
@@ -830,12 +830,12 @@ function useWebinar({
830
830
  [callExtId, linkVersion]
831
831
  );
832
832
  const stepDown = react.useCallback(async () => {
833
- chunkX5OZRLFZ_cjs.markRejoining(callExtId);
833
+ chunkFAJLJDWS_cjs.markRejoining(callExtId);
834
834
  setStageCheck(false);
835
835
  try {
836
836
  await webinarApi().demote(callExtId, myIdentity, linkVersion);
837
837
  } catch (err) {
838
- chunkX5OZRLFZ_cjs.markRejoining(callExtId, false);
838
+ chunkFAJLJDWS_cjs.markRejoining(callExtId, false);
839
839
  failed("leave the stage")(err);
840
840
  }
841
841
  }, [callExtId, myIdentity, linkVersion]);
@@ -895,7 +895,7 @@ function useWebinar({
895
895
  if (!callKey) throw new Error("This call has no key.");
896
896
  await webinarApi().questions.ask(
897
897
  callExtId,
898
- await chunkX5OZRLFZ_cjs.sealQuestionText(text, callKey),
898
+ await chunkFAJLJDWS_cjs.sealQuestionText(text, callKey),
899
899
  linkVersion
900
900
  );
901
901
  }, "ask the question"),
@@ -909,7 +909,7 @@ function useWebinar({
909
909
  await webinarApi().questions.update(
910
910
  callExtId,
911
911
  id,
912
- { answer: await chunkX5OZRLFZ_cjs.sealQuestionText(text, callKey) },
912
+ { answer: await chunkFAJLJDWS_cjs.sealQuestionText(text, callKey) },
913
913
  linkVersion
914
914
  );
915
915
  }, "answer the question"),
@@ -939,15 +939,15 @@ function useWebinar({
939
939
  function useHostMuteNotice(callExtId) {
940
940
  const room = componentsReact.useRoomContext();
941
941
  react.useEffect(() => {
942
- const notifyMuted = chunkX5OZRLFZ_cjs.createMutedNotifier((text) => chunkRLSBRZ6Z_cjs.notify.info(text));
942
+ const notifyMuted = chunkFAJLJDWS_cjs.createMutedNotifier((text) => chunkIX4HOS5Z_cjs.notify.info(text));
943
943
  const onData = (payload, participant, _kind, topic, _encryption, participantIdentity) => {
944
- if (topic !== chunkX5OZRLFZ_cjs.CALL_MODERATION_TOPIC) return;
944
+ if (topic !== chunkFAJLJDWS_cjs.CALL_MODERATION_TOPIC) return;
945
945
  if (participant || participantIdentity) return;
946
- const notice = chunkX5OZRLFZ_cjs.mutedNoticeFromPacket(payload);
946
+ const notice = chunkFAJLJDWS_cjs.mutedNoticeFromPacket(payload);
947
947
  if (notice) notifyMuted(notice);
948
948
  };
949
949
  room.on(livekitClient.RoomEvent.DataReceived, onData);
950
- const offWs = chunkX5OZRLFZ_cjs.onCallEvent("call.muted", (message) => {
950
+ const offWs = chunkFAJLJDWS_cjs.onCallEvent("call.muted", (message) => {
951
951
  if (message.callExtId === callExtId) notifyMuted(message);
952
952
  });
953
953
  return () => {
@@ -15,7 +15,6 @@ function recordChatArtifact(event, conversationId) {
15
15
  docExtId: event.doc_ext_id,
16
16
  title: event.title,
17
17
  content: event.content,
18
- // The backend schema lags the 'react' mode it also sends; the value passes through unchanged.
19
18
  mode: event.mode,
20
19
  isComplete: event.is_complete,
21
20
  version: event.version,
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/chat/chat-artifacts.ts"],
4
- "sourcesContent": ["import { useSyncExternalStore } from 'react'\nimport type { ArtifactEvent } from '@arbidocs/sdk/browser'\n\n/**\n * The documents the assistant is writing (or wrote) in this tab's chats, as their artifact events\n * stream in: the chat engine records every one (`recordChatArtifact`), before the host's\n * `chat.onArtifact` shows it. One store for every surface that shows a draft as it is written: the\n * answer's artifact card, the document viewer, and a drafting chat's document pane.\n *\n * A module-level store, like the open-document registry: a chat and a viewer agree wherever they\n * sit in the tree. It holds what streamed in this tab only; a saved draft is read from the\n * documents list. Cleared on sign-out (`clearChatArtifacts`).\n */\n\n/** An artifact as it streams: what the events said so far. */\nexport interface ChatArtifact {\n /** The document it is written into (`doc-\u2026`). */\n docExtId: string\n title: string\n /** The content so far (Markdown, or the html/react source of a live artifact). */\n content: string\n /**\n * `markdown` / `text` are document drafts (stored by the server as Word documents); `html` /\n * `react` are live artifacts; `image` is a generated image.\n */\n mode: 'markdown' | 'html' | 'text' | 'react' | 'image'\n isComplete: boolean\n version?: number | null\n /** The answer that writes it. */\n messageExtId?: string\n /** The conversation it is written in (`new` until the server named it). */\n conversationId: string\n createdAt: string\n}\n\nlet artifacts: Record<string, ChatArtifact> = {}\n/** Document ids, most recently started last. */\nlet order: string[] = []\nconst listeners = new Set<() => void>()\n\nfunction subscribe(listener: () => void): () => void {\n listeners.add(listener)\n return () => listeners.delete(listener)\n}\n\nfunction emit() {\n listeners.forEach((listener) => listener())\n}\n\n/** Record an artifact event of a chat turn in `conversationId`. */\nexport function recordChatArtifact(event: ArtifactEvent, conversationId: string): void {\n const previous = artifacts[event.doc_ext_id]\n const artifact: ChatArtifact = {\n docExtId: event.doc_ext_id,\n title: event.title,\n content: event.content,\n // The backend schema lags the 'react' mode it also sends; the value passes through unchanged.\n mode: event.mode as ChatArtifact['mode'],\n isComplete: event.is_complete,\n version: event.version,\n createdAt: new Date().toISOString(),\n messageExtId: event.message_ext_id ?? undefined,\n conversationId,\n }\n artifacts = { ...artifacts, [artifact.docExtId]: artifact }\n if (!previous) order = [...order, artifact.docExtId]\n emit()\n}\n\n/** Forget every streamed artifact (sign-out). */\nexport function clearChatArtifacts(): void {\n if (order.length === 0) return\n artifacts = {}\n order = []\n emit()\n}\n\n/** Every artifact streamed in this tab, by document id. */\nexport function getChatArtifacts(): Readonly<Record<string, ChatArtifact>> {\n return artifacts\n}\n\n/** The artifact streaming (or streamed) into this document, if one did in this tab. */\nexport function getChatArtifact(documentId: string | null | undefined) {\n return documentId ? artifacts[documentId] : undefined\n}\n\n/** The artifact streaming into this document, if one is. Re-renders as it streams. */\nexport function useChatArtifact(documentId: string | null | undefined) {\n return useSyncExternalStore(\n subscribe,\n () => getChatArtifact(documentId),\n () => undefined\n )\n}\n\n/** The artifact this answer writes (or wrote), if it did in this tab. */\nexport function useMessageChatArtifact(messageId: string | null | undefined) {\n return useSyncExternalStore(\n subscribe,\n () =>\n messageId\n ? Object.values(artifacts).find((artifact) => artifact.messageExtId === messageId)\n : undefined,\n () => undefined\n )\n}\n"],
5
- "mappings": "AAAA,SAAS,4BAA4B;AAmCrC,IAAI,YAA0C,CAAC;AAE/C,IAAI,QAAkB,CAAC;AACvB,MAAM,YAAY,oBAAI,IAAgB;AAEtC,SAAS,UAAU,UAAkC;AACnD,YAAU,IAAI,QAAQ;AACtB,SAAO,MAAM,UAAU,OAAO,QAAQ;AACxC;AAEA,SAAS,OAAO;AACd,YAAU,QAAQ,CAAC,aAAa,SAAS,CAAC;AAC5C;AAGO,SAAS,mBAAmB,OAAsB,gBAA8B;AACrF,QAAM,WAAW,UAAU,MAAM,UAAU;AAC3C,QAAM,WAAyB;AAAA,IAC7B,UAAU,MAAM;AAAA,IAChB,OAAO,MAAM;AAAA,IACb,SAAS,MAAM;AAAA;AAAA,IAEf,MAAM,MAAM;AAAA,IACZ,YAAY,MAAM;AAAA,IAClB,SAAS,MAAM;AAAA,IACf,YAAW,oBAAI,KAAK,GAAE,YAAY;AAAA,IAClC,cAAc,MAAM,kBAAkB;AAAA,IACtC;AAAA,EACF;AACA,cAAY,EAAE,GAAG,WAAW,CAAC,SAAS,QAAQ,GAAG,SAAS;AAC1D,MAAI,CAAC,SAAU,SAAQ,CAAC,GAAG,OAAO,SAAS,QAAQ;AACnD,OAAK;AACP;AAGO,SAAS,qBAA2B;AACzC,MAAI,MAAM,WAAW,EAAG;AACxB,cAAY,CAAC;AACb,UAAQ,CAAC;AACT,OAAK;AACP;AAGO,SAAS,mBAA2D;AACzE,SAAO;AACT;AAGO,SAAS,gBAAgB,YAAuC;AACrE,SAAO,aAAa,UAAU,UAAU,IAAI;AAC9C;AAGO,SAAS,gBAAgB,YAAuC;AACrE,SAAO;AAAA,IACL;AAAA,IACA,MAAM,gBAAgB,UAAU;AAAA,IAChC,MAAM;AAAA,EACR;AACF;AAGO,SAAS,uBAAuB,WAAsC;AAC3E,SAAO;AAAA,IACL;AAAA,IACA,MACE,YACI,OAAO,OAAO,SAAS,EAAE,KAAK,CAAC,aAAa,SAAS,iBAAiB,SAAS,IAC/E;AAAA,IACN,MAAM;AAAA,EACR;AACF;",
4
+ "sourcesContent": ["import { useSyncExternalStore } from 'react'\nimport type { ArtifactEvent } from '@arbidocs/sdk/browser'\n\n/**\n * The documents the assistant is writing (or wrote) in this tab's chats, as their artifact events\n * stream in: the chat engine records every one (`recordChatArtifact`), before the host's\n * `chat.onArtifact` shows it. One store for every surface that shows a draft as it is written: the\n * answer's artifact card, the document viewer, and a drafting chat's document pane.\n *\n * A module-level store, like the open-document registry: a chat and a viewer agree wherever they\n * sit in the tree. It holds what streamed in this tab only; a saved draft is read from the\n * documents list. Cleared on sign-out (`clearChatArtifacts`).\n */\n\n/** An artifact as it streams: what the events said so far. */\nexport interface ChatArtifact {\n /** The document it is written into (`doc-\u2026`). */\n docExtId: string\n title: string\n /** The content so far (Markdown, or the html/react source of a live artifact). */\n content: string\n /**\n * `markdown` / `text` are document drafts (stored by the server as Word documents); `html` /\n * `react` are live artifacts; `image` is a generated image; `presentation` a slide deck (.pptx).\n */\n mode: ArtifactEvent['mode']\n isComplete: boolean\n version?: number | null\n /** The answer that writes it. */\n messageExtId?: string\n /** The conversation it is written in (`new` until the server named it). */\n conversationId: string\n createdAt: string\n}\n\nlet artifacts: Record<string, ChatArtifact> = {}\n/** Document ids, most recently started last. */\nlet order: string[] = []\nconst listeners = new Set<() => void>()\n\nfunction subscribe(listener: () => void): () => void {\n listeners.add(listener)\n return () => listeners.delete(listener)\n}\n\nfunction emit() {\n listeners.forEach((listener) => listener())\n}\n\n/** Record an artifact event of a chat turn in `conversationId`. */\nexport function recordChatArtifact(event: ArtifactEvent, conversationId: string): void {\n const previous = artifacts[event.doc_ext_id]\n const artifact: ChatArtifact = {\n docExtId: event.doc_ext_id,\n title: event.title,\n content: event.content,\n mode: event.mode,\n isComplete: event.is_complete,\n version: event.version,\n createdAt: new Date().toISOString(),\n messageExtId: event.message_ext_id ?? undefined,\n conversationId,\n }\n artifacts = { ...artifacts, [artifact.docExtId]: artifact }\n if (!previous) order = [...order, artifact.docExtId]\n emit()\n}\n\n/** Forget every streamed artifact (sign-out). */\nexport function clearChatArtifacts(): void {\n if (order.length === 0) return\n artifacts = {}\n order = []\n emit()\n}\n\n/** Every artifact streamed in this tab, by document id. */\nexport function getChatArtifacts(): Readonly<Record<string, ChatArtifact>> {\n return artifacts\n}\n\n/** The artifact streaming (or streamed) into this document, if one did in this tab. */\nexport function getChatArtifact(documentId: string | null | undefined) {\n return documentId ? artifacts[documentId] : undefined\n}\n\n/** The artifact streaming into this document, if one is. Re-renders as it streams. */\nexport function useChatArtifact(documentId: string | null | undefined) {\n return useSyncExternalStore(\n subscribe,\n () => getChatArtifact(documentId),\n () => undefined\n )\n}\n\n/** The artifact this answer writes (or wrote), if it did in this tab. */\nexport function useMessageChatArtifact(messageId: string | null | undefined) {\n return useSyncExternalStore(\n subscribe,\n () =>\n messageId\n ? Object.values(artifacts).find((artifact) => artifact.messageExtId === messageId)\n : undefined,\n () => undefined\n )\n}\n"],
5
+ "mappings": "AAAA,SAAS,4BAA4B;AAmCrC,IAAI,YAA0C,CAAC;AAE/C,IAAI,QAAkB,CAAC;AACvB,MAAM,YAAY,oBAAI,IAAgB;AAEtC,SAAS,UAAU,UAAkC;AACnD,YAAU,IAAI,QAAQ;AACtB,SAAO,MAAM,UAAU,OAAO,QAAQ;AACxC;AAEA,SAAS,OAAO;AACd,YAAU,QAAQ,CAAC,aAAa,SAAS,CAAC;AAC5C;AAGO,SAAS,mBAAmB,OAAsB,gBAA8B;AACrF,QAAM,WAAW,UAAU,MAAM,UAAU;AAC3C,QAAM,WAAyB;AAAA,IAC7B,UAAU,MAAM;AAAA,IAChB,OAAO,MAAM;AAAA,IACb,SAAS,MAAM;AAAA,IACf,MAAM,MAAM;AAAA,IACZ,YAAY,MAAM;AAAA,IAClB,SAAS,MAAM;AAAA,IACf,YAAW,oBAAI,KAAK,GAAE,YAAY;AAAA,IAClC,cAAc,MAAM,kBAAkB;AAAA,IACtC;AAAA,EACF;AACA,cAAY,EAAE,GAAG,WAAW,CAAC,SAAS,QAAQ,GAAG,SAAS;AAC1D,MAAI,CAAC,SAAU,SAAQ,CAAC,GAAG,OAAO,SAAS,QAAQ;AACnD,OAAK;AACP;AAGO,SAAS,qBAA2B;AACzC,MAAI,MAAM,WAAW,EAAG;AACxB,cAAY,CAAC;AACb,UAAQ,CAAC;AACT,OAAK;AACP;AAGO,SAAS,mBAA2D;AACzE,SAAO;AACT;AAGO,SAAS,gBAAgB,YAAuC;AACrE,SAAO,aAAa,UAAU,UAAU,IAAI;AAC9C;AAGO,SAAS,gBAAgB,YAAuC;AACrE,SAAO;AAAA,IACL;AAAA,IACA,MAAM,gBAAgB,UAAU;AAAA,IAChC,MAAM;AAAA,EACR;AACF;AAGO,SAAS,uBAAuB,WAAsC;AAC3E,SAAO;AAAA,IACL;AAAA,IACA,MACE,YACI,OAAO,OAAO,SAAS,EAAE,KAAK,CAAC,aAAa,SAAS,iBAAiB,SAAS,IAC/E;AAAA,IACN,MAAM;AAAA,EACR;AACF;",
6
6
  "names": []
7
7
  }
@@ -3,10 +3,11 @@ import { parseVersionRef } from "../artifacts/versions.js";
3
3
  function opensInWord(doc) {
4
4
  return !!doc && (doc.wp_type === "artifact" || !!doc.file_name?.toLowerCase().endsWith(".docx"));
5
5
  }
6
+ const isSlideDeck = (doc) => !!doc?.file_name?.toLowerCase().endsWith(".pptx");
6
7
  function savedDocumentIdFor(input) {
7
8
  const { documentId, isDraft, isStreaming, isBlobArtifact, documentData } = input;
8
9
  if (!documentId || isDraft || isStreaming || isBlobArtifact) return null;
9
- if (!documentData || !opensInWord(documentData)) return null;
10
+ if (!documentData || !(opensInWord(documentData) || isSlideDeck(documentData))) return null;
10
11
  if (documentData.external_id !== documentId) return null;
11
12
  return documentId;
12
13
  }
@@ -95,6 +96,7 @@ function takeDocumentForTurn(input) {
95
96
  return documentId;
96
97
  }
97
98
  export {
99
+ isSlideDeck,
98
100
  openDocumentId,
99
101
  opensInWord,
100
102
  savedDocumentIdFor,
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/chat/open-document.ts"],
4
- "sourcesContent": ["import { useEffect, useRef, useSyncExternalStore } from 'react'\nimport { parseVersionRef } from '../artifacts/versions'\n\n/**\n * Drafting mode: the documents open beside the chat, and which one a chat turn drafts in.\n *\n * A viewer registers the document it shows (`useRegisterOpenDocument`). Being open binds nothing:\n * a turn names a document (`active_artifact_ext_id`, read with `takeDocumentForTurn` as the\n * question is sent) only when its thread is bound to it (`./thread-document`: started from the\n * document, or attached in that thread), when the document was made in that thread, or when the\n * open document is ambient (an email draft open in the composer). The server then binds the live\n * document to the turn: a Word document is read on demand and edited as tracked changes in the\n * open editor, and what the assistant saves becomes a child version of it. The request carries a\n * reference, never the content.\n *\n * The named document is always the subject, also while one of its earlier versions is on screen\n * read-only: a version still names its document. Sending a turn then returns the viewer to the live\n * document (`showLive`), where the assistant's edits appear as they are made. (Pinning a specific\n * version as a source is a separate feature: it does not go through here.)\n *\n * A module-level registry (not React context), like `useChatSurface`, so a viewer and a chat agree\n * wherever they sit in the tree; the most recently opened document is on top and closing it hands\n * back to the one before. It is not the session state's viewing document (`useArbiSessionState`):\n * it holds what mounted viewers can draft in (saved documents only, with the callback that shows\n * the live one again), and a turn reads it outside React. A viewer on the session's viewing\n * document passes that as `shownId` (it may be a version reference, resolved here the same way as\n * `selectViewingDocumentId` does).\n */\n\n/** What a viewer knows about the document it shows. */\nexport interface SavedDocumentInput {\n /** The document (`doc-\u2026`), never a version reference. */\n documentId: string | null | undefined\n /** An unsaved draft (client-minted id, no backend record). */\n isDraft: boolean\n /** Still streaming: nothing is persisted yet. */\n isStreaming: boolean\n /** An image, audio or video artifact: there is no text to draft in. */\n isBlobArtifact: boolean\n /** The loaded document record. */\n documentData:\n | { external_id?: string | null; wp_type?: string | null; file_name?: string | null }\n | null\n | undefined\n}\n\n/** The fields that decide where a document opens. */\nexport interface WordDocumentKind {\n file_name?: string | null\n wp_type?: string | null\n}\n\n/**\n * A document edited in the Word editor: a `.docx`, or a document the assistant wrote (an\n * artifact). The one rule for where a document opens (`openWordEditor` or `openDocument`) and\n * which documents a chat drafts in.\n */\nexport function opensInWord(doc: WordDocumentKind | null | undefined): boolean {\n return !!doc && (doc.wp_type === 'artifact' || !!doc.file_name?.toLowerCase().endsWith('.docx'))\n}\n\n/**\n * The saved, content-backed document a chat can draft in, or null. Only a saved artifact or Word\n * document counts: drafts, an artifact still streaming and blob artifacts do not.\n */\nexport function savedDocumentIdFor(input: SavedDocumentInput): string | null {\n const { documentId, isDraft, isStreaming, isBlobArtifact, documentData } = input\n if (!documentId || isDraft || isStreaming || isBlobArtifact) return null\n if (!documentData || !opensInWord(documentData)) return null\n // A stale record of the previously viewed document is not this one.\n if (documentData.external_id !== documentId) return null\n return documentId\n}\n\nexport interface OpenDocumentRegistration {\n /** The saved document the viewer shows (`doc-\u2026`); null or empty registers nothing. */\n documentId: string | null | undefined\n /** What is on screen: the document itself, or one of its earlier versions (`doc-\u2026@v2`). */\n shownId?: string | null\n /** Show the live document again. Called when a turn starts while an earlier version is shown. */\n showLive?: () => void\n /** The workspace the document belongs to; a chat in another workspace does not draft in it. */\n workspaceId?: string | null\n /** The thread the document was made in: that thread drafts in it without being bound to it. */\n conversationId?: string | null\n /**\n * Drafted in by whichever chat is beside it, bound or not (an email draft open in the composer:\n * the composer is a drafting surface of its own).\n */\n ambient?: boolean\n}\n\ninterface Entry {\n documentId: string\n current: () => OpenDocumentRegistration\n}\n\nlet entries: Entry[] = []\nconst listeners = new Set<() => void>()\n\nfunction subscribe(listener: () => void): () => void {\n listeners.add(listener)\n return () => listeners.delete(listener)\n}\n\nfunction emit() {\n listeners.forEach((l) => l())\n}\n\nconst top = (): Entry | undefined => entries[entries.length - 1]\n\n/**\n * Registers the document a viewer shows as the open document while the caller is mounted and\n * `documentId` is set. Pass `savedDocumentIdFor(...)`, or the id of a document known to be saved.\n */\nexport function useRegisterOpenDocument(registration: OpenDocumentRegistration): void {\n const latest = useRef(registration)\n const { documentId, workspaceId, conversationId, ambient } = registration\n useEffect(() => {\n latest.current = registration\n })\n // What decides which chat drafts in it changed (its record loaded): readers re-render.\n useEffect(() => {\n if (documentId) emit()\n }, [documentId, workspaceId, conversationId, ambient])\n useEffect(() => {\n if (!documentId) return\n const entry: Entry = { documentId, current: () => latest.current }\n entries = [...entries, entry]\n emit()\n return () => {\n entries = entries.filter((e) => e !== entry)\n emit()\n }\n }, [documentId])\n}\n\n/** The open document's id (the most recently opened), or null. Re-renders when it changes. */\nexport function useOpenDocumentId(options?: { workspaceId?: string | null }): string | null {\n const workspaceId = options?.workspaceId ?? null\n return useSyncExternalStore(\n subscribe,\n () => openDocumentId({ workspaceId }),\n () => null\n )\n}\n\n/** The open document's id (the most recently opened); with `workspaceId`, only one of it. */\nexport function openDocumentId(options?: { workspaceId?: string | null }): string | null {\n const entry = top()\n if (!entry || !inWorkspace(entry, options?.workspaceId)) return null\n return entry.documentId\n}\n\nfunction inWorkspace(entry: Entry, workspaceId: string | null | undefined): boolean {\n const own = entry.current().workspaceId\n return !workspaceId || !own || own === workspaceId\n}\n\n/** What a turn is about to be sent in: its conversation and the document its thread is bound to. */\nexport interface TurnDocumentInput {\n /** The thread's bound document (`threadDocumentId` / the session's `newChatDocumentId`). */\n boundDocumentId: string | null | undefined\n /** The conversation (null: a new chat). */\n conversationId: string | null | undefined\n /** The chat's workspace: an open document of another workspace is never picked up. */\n workspaceId?: string | null\n}\n\n/** The document a turn drafts in, and the open viewer showing it (if one is). */\nfunction pickTurnDocument({\n boundDocumentId,\n conversationId,\n workspaceId,\n}: TurnDocumentInput): { documentId: string; entry: Entry | undefined } | null {\n // A bound thread names its document on every turn, open or not.\n if (boundDocumentId) {\n const entry = [...entries].reverse().find((e) => e.documentId === boundDocumentId)\n return { documentId: boundDocumentId, entry }\n }\n // Unbound: only a document made in this thread, or an ambient one (an email draft).\n for (let i = entries.length - 1; i >= 0; i--) {\n const entry = entries[i]\n if (!inWorkspace(entry, workspaceId)) continue\n const { ambient, conversationId: madeIn } = entry.current()\n if (ambient || (conversationId && madeIn === conversationId))\n return { documentId: entry.documentId, entry }\n }\n return null\n}\n\n/** The document a turn sent now would draft in, or null. Re-renders as documents open and close. */\nexport function useTurnDocumentId(input: TurnDocumentInput): string | null {\n const { boundDocumentId = null, conversationId = null, workspaceId = null } = input\n return useSyncExternalStore(\n subscribe,\n () => pickTurnDocument({ boundDocumentId, conversationId, workspaceId })?.documentId ?? null,\n () => null\n )\n}\n\n/**\n * The document an assistant turn about to be sent drafts in, or null. Call it once per turn as the\n * question is sent: when an earlier version of the document is on screen, the viewer returns to the\n * live document, where the assistant's edits appear.\n */\nexport function takeDocumentForTurn(input: TurnDocumentInput): string | null {\n const picked = pickTurnDocument(input)\n if (!picked) return null\n const { documentId, entry } = picked\n if (entry) {\n const { shownId, showLive } = entry.current()\n if (shownId && shownId !== documentId && parseVersionRef(shownId).docId === documentId)\n showLive?.()\n }\n return documentId\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import { useEffect, useRef, useSyncExternalStore } from 'react'\nimport { parseVersionRef } from '../artifacts/versions'\n\n/**\n * Drafting mode: the documents open beside the chat, and which one a chat turn drafts in.\n *\n * A viewer registers the document it shows (`useRegisterOpenDocument`). Being open binds nothing:\n * a turn names a document (`active_artifact_ext_id`, read with `takeDocumentForTurn` as the\n * question is sent) only when its thread is bound to it (`./thread-document`: started from the\n * document, or attached in that thread), when the document was made in that thread, or when the\n * open document is ambient (an email draft open in the composer). The server then binds the live\n * document to the turn: a Word document is read on demand and edited as tracked changes in the\n * open editor, and what the assistant saves becomes a child version of it. The request carries a\n * reference, never the content.\n *\n * The named document is always the subject, also while one of its earlier versions is on screen\n * read-only: a version still names its document. Sending a turn then returns the viewer to the live\n * document (`showLive`), where the assistant's edits appear as they are made. (Pinning a specific\n * version as a source is a separate feature: it does not go through here.)\n *\n * A module-level registry (not React context), like `useChatSurface`, so a viewer and a chat agree\n * wherever they sit in the tree; the most recently opened document is on top and closing it hands\n * back to the one before. It is not the session state's viewing document (`useArbiSessionState`):\n * it holds what mounted viewers can draft in (saved documents only, with the callback that shows\n * the live one again), and a turn reads it outside React. A viewer on the session's viewing\n * document passes that as `shownId` (it may be a version reference, resolved here the same way as\n * `selectViewingDocumentId` does).\n */\n\n/** What a viewer knows about the document it shows. */\nexport interface SavedDocumentInput {\n /** The document (`doc-\u2026`), never a version reference. */\n documentId: string | null | undefined\n /** An unsaved draft (client-minted id, no backend record). */\n isDraft: boolean\n /** Still streaming: nothing is persisted yet. */\n isStreaming: boolean\n /** An image, audio or video artifact: there is no text to draft in. */\n isBlobArtifact: boolean\n /** The loaded document record. */\n documentData:\n | { external_id?: string | null; wp_type?: string | null; file_name?: string | null }\n | null\n | undefined\n}\n\n/** The fields that decide where a document opens. */\nexport interface WordDocumentKind {\n file_name?: string | null\n wp_type?: string | null\n}\n\n/**\n * A document edited in the Word editor: a `.docx`, or a document the assistant wrote (an\n * artifact). The one rule for where a document opens (`openWordEditor` or `openDocument`) and\n * which documents a chat drafts in.\n */\nexport function opensInWord(doc: WordDocumentKind | null | undefined): boolean {\n return !!doc && (doc.wp_type === 'artifact' || !!doc.file_name?.toLowerCase().endsWith('.docx'))\n}\n\n/** A slide deck (a PowerPoint file): the assistant edits it with its presentation tool. */\nexport const isSlideDeck = (doc: WordDocumentKind | null | undefined): boolean =>\n !!doc?.file_name?.toLowerCase().endsWith('.pptx')\n\n/**\n * The saved, content-backed document a chat can draft in, or null. Only a saved artifact, Word\n * document or slide deck counts: drafts, an artifact still streaming and blob artifacts do not.\n */\nexport function savedDocumentIdFor(input: SavedDocumentInput): string | null {\n const { documentId, isDraft, isStreaming, isBlobArtifact, documentData } = input\n if (!documentId || isDraft || isStreaming || isBlobArtifact) return null\n if (!documentData || !(opensInWord(documentData) || isSlideDeck(documentData))) return null\n // A stale record of the previously viewed document is not this one.\n if (documentData.external_id !== documentId) return null\n return documentId\n}\n\nexport interface OpenDocumentRegistration {\n /** The saved document the viewer shows (`doc-\u2026`); null or empty registers nothing. */\n documentId: string | null | undefined\n /** What is on screen: the document itself, or one of its earlier versions (`doc-\u2026@v2`). */\n shownId?: string | null\n /** Show the live document again. Called when a turn starts while an earlier version is shown. */\n showLive?: () => void\n /** The workspace the document belongs to; a chat in another workspace does not draft in it. */\n workspaceId?: string | null\n /** The thread the document was made in: that thread drafts in it without being bound to it. */\n conversationId?: string | null\n /**\n * Drafted in by whichever chat is beside it, bound or not (an email draft open in the composer:\n * the composer is a drafting surface of its own).\n */\n ambient?: boolean\n}\n\ninterface Entry {\n documentId: string\n current: () => OpenDocumentRegistration\n}\n\nlet entries: Entry[] = []\nconst listeners = new Set<() => void>()\n\nfunction subscribe(listener: () => void): () => void {\n listeners.add(listener)\n return () => listeners.delete(listener)\n}\n\nfunction emit() {\n listeners.forEach((l) => l())\n}\n\nconst top = (): Entry | undefined => entries[entries.length - 1]\n\n/**\n * Registers the document a viewer shows as the open document while the caller is mounted and\n * `documentId` is set. Pass `savedDocumentIdFor(...)`, or the id of a document known to be saved.\n */\nexport function useRegisterOpenDocument(registration: OpenDocumentRegistration): void {\n const latest = useRef(registration)\n const { documentId, workspaceId, conversationId, ambient } = registration\n useEffect(() => {\n latest.current = registration\n })\n // What decides which chat drafts in it changed (its record loaded): readers re-render.\n useEffect(() => {\n if (documentId) emit()\n }, [documentId, workspaceId, conversationId, ambient])\n useEffect(() => {\n if (!documentId) return\n const entry: Entry = { documentId, current: () => latest.current }\n entries = [...entries, entry]\n emit()\n return () => {\n entries = entries.filter((e) => e !== entry)\n emit()\n }\n }, [documentId])\n}\n\n/** The open document's id (the most recently opened), or null. Re-renders when it changes. */\nexport function useOpenDocumentId(options?: { workspaceId?: string | null }): string | null {\n const workspaceId = options?.workspaceId ?? null\n return useSyncExternalStore(\n subscribe,\n () => openDocumentId({ workspaceId }),\n () => null\n )\n}\n\n/** The open document's id (the most recently opened); with `workspaceId`, only one of it. */\nexport function openDocumentId(options?: { workspaceId?: string | null }): string | null {\n const entry = top()\n if (!entry || !inWorkspace(entry, options?.workspaceId)) return null\n return entry.documentId\n}\n\nfunction inWorkspace(entry: Entry, workspaceId: string | null | undefined): boolean {\n const own = entry.current().workspaceId\n return !workspaceId || !own || own === workspaceId\n}\n\n/** What a turn is about to be sent in: its conversation and the document its thread is bound to. */\nexport interface TurnDocumentInput {\n /** The thread's bound document (`threadDocumentId` / the session's `newChatDocumentId`). */\n boundDocumentId: string | null | undefined\n /** The conversation (null: a new chat). */\n conversationId: string | null | undefined\n /** The chat's workspace: an open document of another workspace is never picked up. */\n workspaceId?: string | null\n}\n\n/** The document a turn drafts in, and the open viewer showing it (if one is). */\nfunction pickTurnDocument({\n boundDocumentId,\n conversationId,\n workspaceId,\n}: TurnDocumentInput): { documentId: string; entry: Entry | undefined } | null {\n // A bound thread names its document on every turn, open or not.\n if (boundDocumentId) {\n const entry = [...entries].reverse().find((e) => e.documentId === boundDocumentId)\n return { documentId: boundDocumentId, entry }\n }\n // Unbound: only a document made in this thread, or an ambient one (an email draft).\n for (let i = entries.length - 1; i >= 0; i--) {\n const entry = entries[i]\n if (!inWorkspace(entry, workspaceId)) continue\n const { ambient, conversationId: madeIn } = entry.current()\n if (ambient || (conversationId && madeIn === conversationId))\n return { documentId: entry.documentId, entry }\n }\n return null\n}\n\n/** The document a turn sent now would draft in, or null. Re-renders as documents open and close. */\nexport function useTurnDocumentId(input: TurnDocumentInput): string | null {\n const { boundDocumentId = null, conversationId = null, workspaceId = null } = input\n return useSyncExternalStore(\n subscribe,\n () => pickTurnDocument({ boundDocumentId, conversationId, workspaceId })?.documentId ?? null,\n () => null\n )\n}\n\n/**\n * The document an assistant turn about to be sent drafts in, or null. Call it once per turn as the\n * question is sent: when an earlier version of the document is on screen, the viewer returns to the\n * live document, where the assistant's edits appear.\n */\nexport function takeDocumentForTurn(input: TurnDocumentInput): string | null {\n const picked = pickTurnDocument(input)\n if (!picked) return null\n const { documentId, entry } = picked\n if (entry) {\n const { shownId, showLive } = entry.current()\n if (shownId && shownId !== documentId && parseVersionRef(shownId).docId === documentId)\n showLive?.()\n }\n return documentId\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -2,7 +2,7 @@
2
2
 
3
3
  var chunk7L7QRIQ2_cjs = require('./chunk-7L7QRIQ2.cjs');
4
4
  var chunkO32T7XRD_cjs = require('./chunk-O32T7XRD.cjs');
5
- var chunkRLSBRZ6Z_cjs = require('./chunk-RLSBRZ6Z.cjs');
5
+ var chunkIX4HOS5Z_cjs = require('./chunk-IX4HOS5Z.cjs');
6
6
  var chunkEEIYXVM4_cjs = require('./chunk-EEIYXVM4.cjs');
7
7
  var reactQuery = require('@tanstack/react-query');
8
8
  var react = require('react');
@@ -618,7 +618,7 @@ function applyCallEvent(queryClient, event, options = {}) {
618
618
  }
619
619
  case "call.declined":
620
620
  if (callExtId && callSession.getState().active?.callExtId === callExtId) {
621
- chunkRLSBRZ6Z_cjs.notify.info("Call declined", { record: false, ambient: false });
621
+ chunkIX4HOS5Z_cjs.notify.info("Call declined", { record: false, ambient: false });
622
622
  }
623
623
  return true;
624
624
  case "call.started":
@@ -1088,7 +1088,7 @@ function arbiAccountClient(arbi, overrides = {}) {
1088
1088
  else await arbi.logout();
1089
1089
  } finally {
1090
1090
  chunk7L7QRIQ2_cjs.clearSealedDrafts();
1091
- chunkRLSBRZ6Z_cjs.clearChatArtifacts();
1091
+ chunkIX4HOS5Z_cjs.clearChatArtifacts();
1092
1092
  await wipeOfflineMail().catch(() => {
1093
1093
  });
1094
1094
  }
@@ -2452,13 +2452,13 @@ function callLink(callExtId, key, linkVersion) {
2452
2452
  function alreadyInCall(callExtId) {
2453
2453
  const active = callSession.getState().active;
2454
2454
  if (!active || active.callExtId === callExtId) return false;
2455
- chunkRLSBRZ6Z_cjs.notify.warning("You are already in a call. Leave it first.");
2455
+ chunkIX4HOS5Z_cjs.notify.warning("You are already in a call. Leave it first.");
2456
2456
  return true;
2457
2457
  }
2458
2458
  function fail(err, what = "start the call") {
2459
2459
  console.error(`[calls] failed to ${what}`, err);
2460
2460
  const message = err instanceof Error ? err.message : "Unknown error";
2461
- chunkRLSBRZ6Z_cjs.notify.error(`Could not ${what}: ${message}`);
2461
+ chunkIX4HOS5Z_cjs.notify.error(`Could not ${what}: ${message}`);
2462
2462
  }
2463
2463
  function callContext(call) {
2464
2464
  return {
@@ -2481,7 +2481,7 @@ async function sendInviteLinks(arbi, callExtId, key, linkVersion, invites) {
2481
2481
  fail(err, `invite ${invite.name || "a contact"}`);
2482
2482
  }
2483
2483
  }
2484
- if (sent) chunkRLSBRZ6Z_cjs.notify.success(`Sent ${sent} ${sent === 1 ? "invitation" : "invitations"}`);
2484
+ if (sent) chunkIX4HOS5Z_cjs.notify.success(`Sent ${sent} ${sent === 1 ? "invitation" : "invitations"}`);
2485
2485
  return sent;
2486
2486
  }
2487
2487
  async function startCall(arbi, opts) {
@@ -2754,7 +2754,7 @@ function useOpenCallMeetingChat() {
2754
2754
  open?.(ref);
2755
2755
  } catch (err) {
2756
2756
  console.warn("[calls] could not open the meeting chat", err);
2757
- chunkRLSBRZ6Z_cjs.notify.error(
2757
+ chunkIX4HOS5Z_cjs.notify.error(
2758
2758
  `Could not open the meeting chat: ${err instanceof Error ? err.message : "unknown error"}`
2759
2759
  );
2760
2760
  }
@@ -2983,5 +2983,5 @@ exports.useUserSettings = useUserSettings;
2983
2983
  exports.wipeOfflineMail = wipeOfflineMail;
2984
2984
  exports.wrapTrackKey = wrapTrackKey;
2985
2985
  exports.writeSamples = writeSamples;
2986
- //# sourceMappingURL=chunk-X5OZRLFZ.cjs.map
2987
- //# sourceMappingURL=chunk-X5OZRLFZ.cjs.map
2986
+ //# sourceMappingURL=chunk-FAJLJDWS.cjs.map
2987
+ //# sourceMappingURL=chunk-FAJLJDWS.cjs.map