@mediansh/widget 0.7.0 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,18 +1,20 @@
1
1
  "use client";
2
2
  import {
3
3
  cn
4
- } from "./chunk-3KEM4W7J.js";
4
+ } from "./chunk-3ZXE5RBN.js";
5
5
  import {
6
- __export,
7
6
  clsx
8
- } from "./chunk-W6NPKKCE.js";
7
+ } from "./chunk-HLALDSPM.js";
8
+ import {
9
+ __export
10
+ } from "./chunk-77KIJLPP.js";
9
11
 
10
12
  // ../../apps/web/src/components/support-widget/sdk/median-support.tsx
11
- import { useEffect as useEffect22 } from "react";
13
+ import { useEffect as useEffect26 } from "react";
12
14
 
13
15
  // ../../apps/web/src/components/support-widget/markdown-chunk.tsx
14
16
  import { lazy, useSyncExternalStore } from "react";
15
- var load = () => import("./message-markdown-LOE74LBE.js");
17
+ var load = () => import("./message-markdown-KQVNAD42.js");
16
18
  var MessageMarkdown = lazy(async () => ({
17
19
  default: (await load()).MessageMarkdown
18
20
  }));
@@ -61,18 +63,18 @@ function useMarkdownReady() {
61
63
  }
62
64
 
63
65
  // ../../apps/web/src/components/support-widget/widget-launcher.tsx
64
- import { useCallback as useCallback7, useEffect as useEffect11, useRef as useRef13, useState as useState16 } from "react";
66
+ import { useCallback as useCallback7, useEffect as useEffect10, useRef as useRef14, useState as useState15 } from "react";
65
67
 
66
68
  // ../../node_modules/.bun/@phosphor-icons+react@2.1.10+005eabf3d8b6ef06/node_modules/@phosphor-icons/react/dist/lib/SSRBase.es.js
67
69
  import * as e from "react";
68
70
  var w = e.forwardRef((l5, s4) => {
69
71
  const {
70
- alt: r32,
71
- color: a28 = "currentColor",
72
- size: t10 = "1em",
73
- weight: o43 = "regular",
72
+ alt: r42,
73
+ color: a30 = "currentColor",
74
+ size: t14 = "1em",
75
+ weight: o54 = "regular",
74
76
  mirrored: i3 = false,
75
- children: n,
77
+ children: n2,
76
78
  weights: c8,
77
79
  ...m5
78
80
  } = l5;
@@ -81,16 +83,16 @@ var w = e.forwardRef((l5, s4) => {
81
83
  {
82
84
  ref: s4,
83
85
  xmlns: "http://www.w3.org/2000/svg",
84
- width: t10,
85
- height: t10,
86
- fill: a28,
86
+ width: t14,
87
+ height: t14,
88
+ fill: a30,
87
89
  viewBox: "0 0 256 256",
88
90
  transform: i3 ? "scale(-1, 1)" : void 0,
89
91
  ...m5
90
92
  },
91
- !!r32 && /* @__PURE__ */ e.createElement("title", null, r32),
92
- n,
93
- c8.get(o43)
93
+ !!r42 && /* @__PURE__ */ e.createElement("title", null, r42),
94
+ n2,
95
+ c8.get(o54)
94
96
  );
95
97
  });
96
98
  w.displayName = "SSRBase";
@@ -128,7 +130,7 @@ var a = /* @__PURE__ */ new Map([
128
130
  ]);
129
131
 
130
132
  // ../../node_modules/.bun/@phosphor-icons+react@2.1.10+005eabf3d8b6ef06/node_modules/@phosphor-icons/react/dist/ssr/ArrowDown.es.js
131
- var r = o.forwardRef((e55, t10) => /* @__PURE__ */ o.createElement(w, { ref: t10, ...e55, weights: a }));
133
+ var r = o.forwardRef((e62, t14) => /* @__PURE__ */ o.createElement(w, { ref: t14, ...e62, weights: a }));
132
134
  r.displayName = "ArrowDownIcon";
133
135
 
134
136
  // ../../node_modules/.bun/@phosphor-icons+react@2.1.10+005eabf3d8b6ef06/node_modules/@phosphor-icons/react/dist/ssr/ArrowRight.es.js
@@ -164,7 +166,7 @@ var a2 = /* @__PURE__ */ new Map([
164
166
  ]);
165
167
 
166
168
  // ../../node_modules/.bun/@phosphor-icons+react@2.1.10+005eabf3d8b6ef06/node_modules/@phosphor-icons/react/dist/ssr/ArrowRight.es.js
167
- var o2 = r2.forwardRef((t10, e55) => /* @__PURE__ */ r2.createElement(w, { ref: e55, ...t10, weights: a2 }));
169
+ var o2 = r2.forwardRef((t14, e62) => /* @__PURE__ */ r2.createElement(w, { ref: e62, ...t14, weights: a2 }));
168
170
  o2.displayName = "ArrowRightIcon";
169
171
 
170
172
  // ../../node_modules/.bun/@phosphor-icons+react@2.1.10+005eabf3d8b6ef06/node_modules/@phosphor-icons/react/dist/ssr/ArrowUp.es.js
@@ -200,7 +202,7 @@ var a3 = /* @__PURE__ */ new Map([
200
202
  ]);
201
203
 
202
204
  // ../../node_modules/.bun/@phosphor-icons+react@2.1.10+005eabf3d8b6ef06/node_modules/@phosphor-icons/react/dist/ssr/ArrowUp.es.js
203
- var o3 = r3.forwardRef((e55, t10) => /* @__PURE__ */ r3.createElement(w, { ref: t10, ...e55, weights: a3 }));
205
+ var o3 = r3.forwardRef((e62, t14) => /* @__PURE__ */ r3.createElement(w, { ref: t14, ...e62, weights: a3 }));
204
206
  o3.displayName = "ArrowUpIcon";
205
207
 
206
208
  // ../../node_modules/.bun/@phosphor-icons+react@2.1.10+005eabf3d8b6ef06/node_modules/@phosphor-icons/react/dist/ssr/ArrowsClockwise.es.js
@@ -236,7 +238,7 @@ var e5 = /* @__PURE__ */ new Map([
236
238
  ]);
237
239
 
238
240
  // ../../node_modules/.bun/@phosphor-icons+react@2.1.10+005eabf3d8b6ef06/node_modules/@phosphor-icons/react/dist/ssr/ArrowsClockwise.es.js
239
- var r4 = o4.forwardRef((e55, s4) => /* @__PURE__ */ o4.createElement(w, { ref: s4, ...e55, weights: e5 }));
241
+ var r4 = o4.forwardRef((e62, s4) => /* @__PURE__ */ o4.createElement(w, { ref: s4, ...e62, weights: e5 }));
240
242
  r4.displayName = "ArrowsClockwiseIcon";
241
243
 
242
244
  // ../../node_modules/.bun/@phosphor-icons+react@2.1.10+005eabf3d8b6ef06/node_modules/@phosphor-icons/react/dist/ssr/ArrowsIn.es.js
@@ -278,7 +280,7 @@ var e6 = /* @__PURE__ */ new Map([
278
280
  ]);
279
281
 
280
282
  // ../../node_modules/.bun/@phosphor-icons+react@2.1.10+005eabf3d8b6ef06/node_modules/@phosphor-icons/react/dist/ssr/ArrowsIn.es.js
281
- var o5 = r5.forwardRef((e55, s4) => /* @__PURE__ */ r5.createElement(w, { ref: s4, ...e55, weights: e6 }));
283
+ var o5 = r5.forwardRef((e62, s4) => /* @__PURE__ */ r5.createElement(w, { ref: s4, ...e62, weights: e6 }));
282
284
  o5.displayName = "ArrowsInIcon";
283
285
 
284
286
  // ../../node_modules/.bun/@phosphor-icons+react@2.1.10+005eabf3d8b6ef06/node_modules/@phosphor-icons/react/dist/ssr/ArrowsOut.es.js
@@ -314,7 +316,7 @@ var e7 = /* @__PURE__ */ new Map([
314
316
  ]);
315
317
 
316
318
  // ../../node_modules/.bun/@phosphor-icons+react@2.1.10+005eabf3d8b6ef06/node_modules/@phosphor-icons/react/dist/ssr/ArrowsOut.es.js
317
- var o6 = r6.forwardRef((t10, e55) => /* @__PURE__ */ r6.createElement(w, { ref: e55, ...t10, weights: e7 }));
319
+ var o6 = r6.forwardRef((t14, e62) => /* @__PURE__ */ r6.createElement(w, { ref: e62, ...t14, weights: e7 }));
318
320
  o6.displayName = "ArrowsOutIcon";
319
321
 
320
322
  // ../../node_modules/.bun/@phosphor-icons+react@2.1.10+005eabf3d8b6ef06/node_modules/@phosphor-icons/react/dist/ssr/Bug.es.js
@@ -350,7 +352,7 @@ var e8 = /* @__PURE__ */ new Map([
350
352
  ]);
351
353
 
352
354
  // ../../node_modules/.bun/@phosphor-icons+react@2.1.10+005eabf3d8b6ef06/node_modules/@phosphor-icons/react/dist/ssr/Bug.es.js
353
- var e9 = o7.forwardRef((r32, t10) => /* @__PURE__ */ o7.createElement(w, { ref: t10, ...r32, weights: e8 }));
355
+ var e9 = o7.forwardRef((r42, t14) => /* @__PURE__ */ o7.createElement(w, { ref: t14, ...r42, weights: e8 }));
354
356
  e9.displayName = "BugIcon";
355
357
 
356
358
  // ../../node_modules/.bun/@phosphor-icons+react@2.1.10+005eabf3d8b6ef06/node_modules/@phosphor-icons/react/dist/ssr/CaretDown.es.js
@@ -386,7 +388,7 @@ var t = /* @__PURE__ */ new Map([
386
388
  ]);
387
389
 
388
390
  // ../../node_modules/.bun/@phosphor-icons+react@2.1.10+005eabf3d8b6ef06/node_modules/@phosphor-icons/react/dist/ssr/CaretDown.es.js
389
- var e11 = o8.forwardRef((r32, t10) => /* @__PURE__ */ o8.createElement(w, { ref: t10, ...r32, weights: t }));
391
+ var e11 = o8.forwardRef((r42, t14) => /* @__PURE__ */ o8.createElement(w, { ref: t14, ...r42, weights: t }));
390
392
  e11.displayName = "CaretDownIcon";
391
393
 
392
394
  // ../../node_modules/.bun/@phosphor-icons+react@2.1.10+005eabf3d8b6ef06/node_modules/@phosphor-icons/react/dist/ssr/CaretRight.es.js
@@ -422,7 +424,7 @@ var t2 = /* @__PURE__ */ new Map([
422
424
  ]);
423
425
 
424
426
  // ../../node_modules/.bun/@phosphor-icons+react@2.1.10+005eabf3d8b6ef06/node_modules/@phosphor-icons/react/dist/ssr/CaretRight.es.js
425
- var e13 = t3.forwardRef((r32, o43) => /* @__PURE__ */ t3.createElement(w, { ref: o43, ...r32, weights: t2 }));
427
+ var e13 = t3.forwardRef((r42, o54) => /* @__PURE__ */ t3.createElement(w, { ref: o54, ...r42, weights: t2 }));
426
428
  e13.displayName = "CaretRightIcon";
427
429
 
428
430
  // ../../node_modules/.bun/@phosphor-icons+react@2.1.10+005eabf3d8b6ef06/node_modules/@phosphor-icons/react/dist/ssr/ChatCircleDots.es.js
@@ -464,7 +466,7 @@ var e14 = /* @__PURE__ */ new Map([
464
466
  ]);
465
467
 
466
468
  // ../../node_modules/.bun/@phosphor-icons+react@2.1.10+005eabf3d8b6ef06/node_modules/@phosphor-icons/react/dist/ssr/ChatCircleDots.es.js
467
- var o9 = t4.forwardRef((e55, r32) => /* @__PURE__ */ t4.createElement(w, { ref: r32, ...e55, weights: e14 }));
469
+ var o9 = t4.forwardRef((e62, r42) => /* @__PURE__ */ t4.createElement(w, { ref: r42, ...e62, weights: e14 }));
468
470
  o9.displayName = "ChatCircleDotsIcon";
469
471
 
470
472
  // ../../node_modules/.bun/@phosphor-icons+react@2.1.10+005eabf3d8b6ef06/node_modules/@phosphor-icons/react/dist/ssr/Check.es.js
@@ -506,7 +508,7 @@ var a9 = /* @__PURE__ */ new Map([
506
508
  ]);
507
509
 
508
510
  // ../../node_modules/.bun/@phosphor-icons+react@2.1.10+005eabf3d8b6ef06/node_modules/@phosphor-icons/react/dist/ssr/Check.es.js
509
- var o10 = e16.forwardRef((r32, t10) => /* @__PURE__ */ e16.createElement(w, { ref: t10, ...r32, weights: a9 }));
511
+ var o10 = e16.forwardRef((r42, t14) => /* @__PURE__ */ e16.createElement(w, { ref: t14, ...r42, weights: a9 }));
510
512
  o10.displayName = "CheckIcon";
511
513
 
512
514
  // ../../node_modules/.bun/@phosphor-icons+react@2.1.10+005eabf3d8b6ef06/node_modules/@phosphor-icons/react/dist/ssr/EnvelopeSimple.es.js
@@ -578,7 +580,7 @@ var a11 = /* @__PURE__ */ new Map([
578
580
  ]);
579
581
 
580
582
  // ../../node_modules/.bun/@phosphor-icons+react@2.1.10+005eabf3d8b6ef06/node_modules/@phosphor-icons/react/dist/ssr/File.es.js
581
- var o12 = e20.forwardRef((r32, t10) => /* @__PURE__ */ e20.createElement(w, { ref: t10, ...r32, weights: a11 }));
583
+ var o12 = e20.forwardRef((r42, t14) => /* @__PURE__ */ e20.createElement(w, { ref: t14, ...r42, weights: a11 }));
582
584
  o12.displayName = "FileIcon";
583
585
 
584
586
  // ../../node_modules/.bun/@phosphor-icons+react@2.1.10+005eabf3d8b6ef06/node_modules/@phosphor-icons/react/dist/ssr/ImageSquare.es.js
@@ -620,7 +622,7 @@ var e21 = /* @__PURE__ */ new Map([
620
622
  ]);
621
623
 
622
624
  // ../../node_modules/.bun/@phosphor-icons+react@2.1.10+005eabf3d8b6ef06/node_modules/@phosphor-icons/react/dist/ssr/ImageSquare.es.js
623
- var a13 = e22.forwardRef((r32, o43) => /* @__PURE__ */ e22.createElement(w, { ref: o43, ...r32, weights: e21 }));
625
+ var a13 = e22.forwardRef((r42, o54) => /* @__PURE__ */ e22.createElement(w, { ref: o54, ...r42, weights: e21 }));
624
626
  a13.displayName = "ImageSquareIcon";
625
627
 
626
628
  // ../../node_modules/.bun/@phosphor-icons+react@2.1.10+005eabf3d8b6ef06/node_modules/@phosphor-icons/react/dist/ssr/Lightbulb.es.js
@@ -662,7 +664,7 @@ var e23 = /* @__PURE__ */ new Map([
662
664
  ]);
663
665
 
664
666
  // ../../node_modules/.bun/@phosphor-icons+react@2.1.10+005eabf3d8b6ef06/node_modules/@phosphor-icons/react/dist/ssr/Lightbulb.es.js
665
- var o13 = t5.forwardRef((e55, r32) => /* @__PURE__ */ t5.createElement(w, { ref: r32, ...e55, weights: e23 }));
667
+ var o13 = t5.forwardRef((e62, r42) => /* @__PURE__ */ t5.createElement(w, { ref: r42, ...e62, weights: e23 }));
666
668
  o13.displayName = "LightbulbIcon";
667
669
 
668
670
  // ../../node_modules/.bun/@phosphor-icons+react@2.1.10+005eabf3d8b6ef06/node_modules/@phosphor-icons/react/dist/ssr/LockSimple.es.js
@@ -704,7 +706,7 @@ var e24 = /* @__PURE__ */ new Map([
704
706
  ]);
705
707
 
706
708
  // ../../node_modules/.bun/@phosphor-icons+react@2.1.10+005eabf3d8b6ef06/node_modules/@phosphor-icons/react/dist/ssr/LockSimple.es.js
707
- var e25 = o14.forwardRef((m5, r32) => /* @__PURE__ */ o14.createElement(w, { ref: r32, ...m5, weights: e24 }));
709
+ var e25 = o14.forwardRef((m5, r42) => /* @__PURE__ */ o14.createElement(w, { ref: r42, ...m5, weights: e24 }));
708
710
  e25.displayName = "LockSimpleIcon";
709
711
 
710
712
  // ../../node_modules/.bun/@phosphor-icons+react@2.1.10+005eabf3d8b6ef06/node_modules/@phosphor-icons/react/dist/ssr/Play.es.js
@@ -746,7 +748,7 @@ var a16 = /* @__PURE__ */ new Map([
746
748
  ]);
747
749
 
748
750
  // ../../node_modules/.bun/@phosphor-icons+react@2.1.10+005eabf3d8b6ef06/node_modules/@phosphor-icons/react/dist/ssr/Play.es.js
749
- var a17 = o15.forwardRef((e55, r32) => /* @__PURE__ */ o15.createElement(w, { ref: r32, ...e55, weights: a16 }));
751
+ var a17 = o15.forwardRef((e62, r42) => /* @__PURE__ */ o15.createElement(w, { ref: r42, ...e62, weights: a16 }));
750
752
  a17.displayName = "PlayIcon";
751
753
 
752
754
  // ../../node_modules/.bun/@phosphor-icons+react@2.1.10+005eabf3d8b6ef06/node_modules/@phosphor-icons/react/dist/ssr/Plus.es.js
@@ -788,7 +790,7 @@ var a18 = /* @__PURE__ */ new Map([
788
790
  ]);
789
791
 
790
792
  // ../../node_modules/.bun/@phosphor-icons+react@2.1.10+005eabf3d8b6ef06/node_modules/@phosphor-icons/react/dist/ssr/Plus.es.js
791
- var e28 = o16.forwardRef((r32, s4) => /* @__PURE__ */ o16.createElement(w, { ref: s4, ...r32, weights: a18 }));
793
+ var e28 = o16.forwardRef((r42, s4) => /* @__PURE__ */ o16.createElement(w, { ref: s4, ...r42, weights: a18 }));
792
794
  e28.displayName = "PlusIcon";
793
795
 
794
796
  // ../../node_modules/.bun/@phosphor-icons+react@2.1.10+005eabf3d8b6ef06/node_modules/@phosphor-icons/react/dist/ssr/Sparkle.es.js
@@ -830,7 +832,7 @@ var l = /* @__PURE__ */ new Map([
830
832
  ]);
831
833
 
832
834
  // ../../node_modules/.bun/@phosphor-icons+react@2.1.10+005eabf3d8b6ef06/node_modules/@phosphor-icons/react/dist/ssr/Sparkle.es.js
833
- var r7 = e29.forwardRef((o43, a28) => /* @__PURE__ */ e29.createElement(w, { ref: a28, ...o43, weights: l }));
835
+ var r7 = e29.forwardRef((o54, a30) => /* @__PURE__ */ e29.createElement(w, { ref: a30, ...o54, weights: l }));
834
836
  r7.displayName = "SparkleIcon";
835
837
 
836
838
  // ../../node_modules/.bun/@phosphor-icons+react@2.1.10+005eabf3d8b6ef06/node_modules/@phosphor-icons/react/dist/ssr/Star.es.js
@@ -872,7 +874,7 @@ var l2 = /* @__PURE__ */ new Map([
872
874
  ]);
873
875
 
874
876
  // ../../node_modules/.bun/@phosphor-icons+react@2.1.10+005eabf3d8b6ef06/node_modules/@phosphor-icons/react/dist/ssr/Star.es.js
875
- var t6 = r8.forwardRef((o43, a28) => /* @__PURE__ */ r8.createElement(w, { ref: a28, ...o43, weights: l2 }));
877
+ var t6 = r8.forwardRef((o54, a30) => /* @__PURE__ */ r8.createElement(w, { ref: a30, ...o54, weights: l2 }));
876
878
  t6.displayName = "StarIcon";
877
879
 
878
880
  // ../../node_modules/.bun/@phosphor-icons+react@2.1.10+005eabf3d8b6ef06/node_modules/@phosphor-icons/react/dist/ssr/UserCircle.es.js
@@ -914,7 +916,7 @@ var a21 = /* @__PURE__ */ new Map([
914
916
  ]);
915
917
 
916
918
  // ../../node_modules/.bun/@phosphor-icons+react@2.1.10+005eabf3d8b6ef06/node_modules/@phosphor-icons/react/dist/ssr/UserCircle.es.js
917
- var r9 = e31.forwardRef((o43, s4) => /* @__PURE__ */ e31.createElement(w, { ref: s4, ...o43, weights: a21 }));
919
+ var r9 = e31.forwardRef((o54, s4) => /* @__PURE__ */ e31.createElement(w, { ref: s4, ...o54, weights: a21 }));
918
920
  r9.displayName = "UserCircleIcon";
919
921
 
920
922
  // ../../node_modules/.bun/@phosphor-icons+react@2.1.10+005eabf3d8b6ef06/node_modules/@phosphor-icons/react/dist/ssr/WarningCircle.es.js
@@ -950,7 +952,7 @@ var a22 = /* @__PURE__ */ new Map([
950
952
  ]);
951
953
 
952
954
  // ../../node_modules/.bun/@phosphor-icons+react@2.1.10+005eabf3d8b6ef06/node_modules/@phosphor-icons/react/dist/ssr/WarningCircle.es.js
953
- var e33 = r10.forwardRef((o43, a28) => /* @__PURE__ */ r10.createElement(w, { ref: a28, ...o43, weights: a22 }));
955
+ var e33 = r10.forwardRef((o54, a30) => /* @__PURE__ */ r10.createElement(w, { ref: a30, ...o54, weights: a22 }));
954
956
  e33.displayName = "WarningCircleIcon";
955
957
 
956
958
  // ../../node_modules/.bun/@phosphor-icons+react@2.1.10+005eabf3d8b6ef06/node_modules/@phosphor-icons/react/dist/ssr/X.es.js
@@ -992,7 +994,7 @@ var a23 = /* @__PURE__ */ new Map([
992
994
  ]);
993
995
 
994
996
  // ../../node_modules/.bun/@phosphor-icons+react@2.1.10+005eabf3d8b6ef06/node_modules/@phosphor-icons/react/dist/ssr/X.es.js
995
- var e35 = o17.forwardRef((r32, t10) => /* @__PURE__ */ o17.createElement(w, { ref: t10, ...r32, weights: a23 }));
997
+ var e35 = o17.forwardRef((r42, t14) => /* @__PURE__ */ o17.createElement(w, { ref: t14, ...r42, weights: a23 }));
996
998
  e35.displayName = "XIcon";
997
999
 
998
1000
  // ../../apps/web/src/components/icons.tsx
@@ -1007,8 +1009,15 @@ function icon(Base, name, weight = "fill") {
1007
1009
  var Bug = /* @__PURE__ */ icon(e9, "Bug");
1008
1010
  var ChatCircleDots = /* @__PURE__ */ icon(o9, "ChatCircleDots");
1009
1011
  var EnvelopeSimple = /* @__PURE__ */ icon(o11, "EnvelopeSimple");
1010
- var File = /* @__PURE__ */ icon(o12, "File");
1012
+ var File2 = /* @__PURE__ */ icon(o12, "File");
1011
1013
  var ImageSquare = /* @__PURE__ */ icon(a13, "ImageSquare");
1014
+ var LIFEBUOY = [
1015
+ "M128,24A104,104,0,1,0,232,128,104,104,0,0,0,128,24Z",
1016
+ "M166.6,69.6L149.08,87.12A46,46,0,0,0,106.92,87.12L89.4,69.6A70,70,0,0,1,166.6,69.6Z",
1017
+ "M186.4,166.6L168.88,149.08A46,46,0,0,0,168.88,106.92L186.4,89.4A70,70,0,0,1,186.4,166.6Z",
1018
+ "M89.4,186.4L106.92,168.88A46,46,0,0,0,149.08,168.88L166.6,186.4A70,70,0,0,1,89.4,186.4Z",
1019
+ "M69.6,89.4L87.12,106.92A46,46,0,0,0,87.12,149.08L69.6,166.6A70,70,0,0,1,69.6,89.4Z"
1020
+ ].join("");
1012
1021
  var Lightbulb = /* @__PURE__ */ icon(o13, "Lightbulb");
1013
1022
  var LockSimple = /* @__PURE__ */ icon(e25, "LockSimple");
1014
1023
  var Play = /* @__PURE__ */ icon(a17, "Play");
@@ -1113,7 +1122,7 @@ function setSpot(spot) {
1113
1122
  }
1114
1123
 
1115
1124
  // ../../apps/web/src/components/support-widget/support-widget.tsx
1116
- import { useState as useState11 } from "react";
1125
+ import { useState as useState10 } from "react";
1117
1126
 
1118
1127
  // ../../apps/web/src/components/ui/collapse.tsx
1119
1128
  import { jsx as jsx2 } from "react/jsx-runtime";
@@ -1121,17 +1130,21 @@ var COLLAPSE_MS = 200;
1121
1130
  function Collapse({
1122
1131
  isOpen,
1123
1132
  id,
1133
+ direction = "vertical",
1124
1134
  children,
1125
1135
  className
1126
1136
  }) {
1137
+ const isSideways = direction === "horizontal";
1127
1138
  return /* @__PURE__ */ jsx2(
1128
1139
  "div",
1129
1140
  {
1130
1141
  id,
1131
1142
  inert: isOpen ? void 0 : true,
1132
1143
  className: cn(
1133
- "grid transition-[grid-template-rows,opacity] duration-200 ease-out motion-reduce:transition-none",
1134
- isOpen ? "grid-rows-[1fr] opacity-100" : "grid-rows-[0fr] opacity-0",
1144
+ "grid duration-200 ease-out motion-reduce:transition-none",
1145
+ isSideways ? "transition-[grid-template-columns,opacity]" : "transition-[grid-template-rows,opacity]",
1146
+ isSideways ? isOpen ? "grid-cols-[1fr]" : "grid-cols-[0fr]" : isOpen ? "grid-rows-[1fr]" : "grid-rows-[0fr]",
1147
+ isOpen ? "opacity-100" : "opacity-0",
1135
1148
  className
1136
1149
  ),
1137
1150
  children: /* @__PURE__ */ jsx2("div", { className: "overflow-hidden", children })
@@ -1144,57 +1157,57 @@ import * as React3 from "react";
1144
1157
 
1145
1158
  // ../../node_modules/.bun/@shadcn+react@0.3.0+03aa47d54245a271/node_modules/@shadcn/react/dist/chunk-HBS6WEDP.js
1146
1159
  import * as i2 from "react";
1147
- function S({ defaultTagName: a28, props: r32, render: t10, state: n = {}, stateAttributesMapping: e55 }) {
1148
- let o43 = d(R(n, e55), r32);
1149
- if (!t10) return i2.createElement(a28, o43);
1150
- if (typeof t10 == "function") return t10(o43, n);
1151
- if (!i2.isValidElement(t10)) return null;
1152
- let s4 = t10.props, u = { ...d(o43, s4), ref: f8(o43.ref, s4.ref) };
1153
- return i2.cloneElement(t10, u);
1154
- }
1155
- function d(...a28) {
1156
- let r32 = {};
1157
- for (let t10 of a28) {
1158
- if (!t10) continue;
1159
- let n = t10;
1160
- for (let e55 of Object.keys(n)) {
1161
- let o43 = n[e55];
1162
- if (o43 === void 0) continue;
1163
- let s4 = r32[e55];
1164
- e55 === "className" ? r32[e55] = [s4, o43].filter(Boolean).join(" ") : e55 === "style" ? r32[e55] = { ...s4, ...o43 } : e55 === "ref" ? r32[e55] = f8(s4, o43) : l4(e55) && typeof s4 == "function" && typeof o43 == "function" ? r32[e55] = p2(o43, s4) : r32[e55] = o43;
1160
+ function S({ defaultTagName: a30, props: r42, render: t14, state: n2 = {}, stateAttributesMapping: e62 }) {
1161
+ let o54 = d(R(n2, e62), r42);
1162
+ if (!t14) return i2.createElement(a30, o54);
1163
+ if (typeof t14 == "function") return t14(o54, n2);
1164
+ if (!i2.isValidElement(t14)) return null;
1165
+ let s4 = t14.props, u = { ...d(o54, s4), ref: f8(o54.ref, s4.ref) };
1166
+ return i2.cloneElement(t14, u);
1167
+ }
1168
+ function d(...a30) {
1169
+ let r42 = {};
1170
+ for (let t14 of a30) {
1171
+ if (!t14) continue;
1172
+ let n2 = t14;
1173
+ for (let e62 of Object.keys(n2)) {
1174
+ let o54 = n2[e62];
1175
+ if (o54 === void 0) continue;
1176
+ let s4 = r42[e62];
1177
+ e62 === "className" ? r42[e62] = [s4, o54].filter(Boolean).join(" ") : e62 === "style" ? r42[e62] = { ...s4, ...o54 } : e62 === "ref" ? r42[e62] = f8(s4, o54) : l4(e62) && typeof s4 == "function" && typeof o54 == "function" ? r42[e62] = p2(o54, s4) : r42[e62] = o54;
1165
1178
  }
1166
1179
  }
1167
- return r32;
1168
- }
1169
- function R(a28, r32) {
1170
- let t10 = {};
1171
- for (let n of Object.keys(a28)) {
1172
- let e55 = a28[n], o43 = r32?.[n]?.(e55);
1173
- if (o43) {
1174
- Object.assign(t10, o43);
1180
+ return r42;
1181
+ }
1182
+ function R(a30, r42) {
1183
+ let t14 = {};
1184
+ for (let n2 of Object.keys(a30)) {
1185
+ let e62 = a30[n2], o54 = r42?.[n2]?.(e62);
1186
+ if (o54) {
1187
+ Object.assign(t14, o54);
1175
1188
  continue;
1176
1189
  }
1177
- if (n === "slot") {
1178
- t10["data-slot"] = e55;
1190
+ if (n2 === "slot") {
1191
+ t14["data-slot"] = e62;
1179
1192
  continue;
1180
1193
  }
1181
- let s4 = `data-${String(n).replace(/[A-Z]/g, (c8) => `-${c8.toLowerCase()}`)}`;
1182
- typeof e55 == "boolean" ? t10[s4] = e55 ? "" : void 0 : e55 != null && (t10[s4] = String(e55));
1194
+ let s4 = `data-${String(n2).replace(/[A-Z]/g, (c8) => `-${c8.toLowerCase()}`)}`;
1195
+ typeof e62 == "boolean" ? t14[s4] = e62 ? "" : void 0 : e62 != null && (t14[s4] = String(e62));
1183
1196
  }
1184
- return t10;
1197
+ return t14;
1185
1198
  }
1186
- function p2(a28, r32) {
1187
- return function(n) {
1188
- a28(n), n.defaultPrevented || r32(n);
1199
+ function p2(a30, r42) {
1200
+ return function(n2) {
1201
+ a30(n2), n2.defaultPrevented || r42(n2);
1189
1202
  };
1190
1203
  }
1191
- function l4(a28) {
1192
- return /^on[A-Z]/.test(a28);
1204
+ function l4(a30) {
1205
+ return /^on[A-Z]/.test(a30);
1193
1206
  }
1194
- function f8(...a28) {
1195
- let r32 = a28.filter(Boolean);
1196
- if (r32.length !== 0) return (t10) => {
1197
- for (let n of r32) typeof n == "function" ? n(t10) : n && (n.current = t10);
1207
+ function f8(...a30) {
1208
+ let r42 = a30.filter(Boolean);
1209
+ if (r42.length !== 0) return (t14) => {
1210
+ for (let n2 of r42) typeof n2 == "function" ? n2(t14) : n2 && (n2.current = t14);
1198
1211
  };
1199
1212
  }
1200
1213
 
@@ -1210,15 +1223,15 @@ var Be = /* @__PURE__ */ new Set(["ArrowDown", "ArrowUp", "End", "Home", "PageDo
1210
1223
  var ge = { start: false, end: false };
1211
1224
  var dt = [];
1212
1225
  var Q = { currentAnchorId: null, visibleMessageIds: dt };
1213
- function De({ content: e55, scrollEdgeThreshold: t10, spacer: r32, viewport: n }) {
1214
- if (!n || !e55) return ge;
1215
- let o43 = Se({ content: e55, spacer: r32, viewport: n });
1216
- return { start: n.scrollTop > t10, end: o43 - n.scrollTop - n.clientHeight > t10 };
1217
- }
1218
- function Fe({ content: e55, scrollMargin: t10, scrollPreviousItemPeek: r32, spacer: n, viewport: o43, visibleMessageIds: i3 }) {
1219
- if (!e55 || !o43) return Q;
1220
- let S2 = o43.getBoundingClientRect(), l5 = S2.top + t10 + r32, d2 = typeof IntersectionObserver > "u", E = [], b2 = null;
1221
- for (let C of ee(e55, n)) {
1226
+ function De({ content: e62, scrollEdgeThreshold: t14, spacer: r42, viewport: n2 }) {
1227
+ if (!n2 || !e62) return ge;
1228
+ let o54 = Se({ content: e62, spacer: r42, viewport: n2 });
1229
+ return { start: n2.scrollTop > t14, end: o54 - n2.scrollTop - n2.clientHeight > t14 };
1230
+ }
1231
+ function Fe({ content: e62, scrollMargin: t14, scrollPreviousItemPeek: r42, spacer: n2, viewport: o54, visibleMessageIds: i3 }) {
1232
+ if (!e62 || !o54) return Q;
1233
+ let S2 = o54.getBoundingClientRect(), l5 = S2.top + t14 + r42, d2 = typeof IntersectionObserver > "u", E = [], b2 = null;
1234
+ for (let C of ee(e62, n2)) {
1222
1235
  let p3 = C.dataset.messageId;
1223
1236
  if (!p3) continue;
1224
1237
  let m5 = C.dataset.scrollAnchor === "true", L = m5 || d2 ? C.getBoundingClientRect() : null;
@@ -1226,100 +1239,100 @@ function Fe({ content: e55, scrollMargin: t10, scrollPreviousItemPeek: r32, spac
1226
1239
  }
1227
1240
  return E.length === 0 && b2 === null ? Q : { currentAnchorId: b2, visibleMessageIds: E };
1228
1241
  }
1229
- function ee(e55, t10) {
1230
- return Array.from(e55.children).filter((r32) => r32 instanceof HTMLElement && r32 !== t10);
1242
+ function ee(e62, t14) {
1243
+ return Array.from(e62.children).filter((r42) => r42 instanceof HTMLElement && r42 !== t14);
1231
1244
  }
1232
- function je(e55, t10) {
1233
- for (let r32 = t10; r32 < e55.length; r32++) {
1234
- let n = e55[r32];
1235
- if (n?.dataset.scrollAnchor === "true") return n;
1245
+ function je(e62, t14) {
1246
+ for (let r42 = t14; r42 < e62.length; r42++) {
1247
+ let n2 = e62[r42];
1248
+ if (n2?.dataset.scrollAnchor === "true") return n2;
1236
1249
  }
1237
1250
  return null;
1238
1251
  }
1239
- function Ge(e55, t10) {
1240
- for (let r32 of e55) if (r32.dataset.scrollAnchor === "true" && !t10.has(r32)) return r32;
1252
+ function Ge(e62, t14) {
1253
+ for (let r42 of e62) if (r42.dataset.scrollAnchor === "true" && !t14.has(r42)) return r42;
1241
1254
  return null;
1242
1255
  }
1243
- function Ne(e55, t10) {
1244
- let r32 = 0;
1245
- for (let n = t10; n < e55.length; n++) if (e55[n]?.dataset.scrollAnchor === "true" && (r32 += 1, r32 > 1)) return true;
1256
+ function Ne(e62, t14) {
1257
+ let r42 = 0;
1258
+ for (let n2 = t14; n2 < e62.length; n2++) if (e62[n2]?.dataset.scrollAnchor === "true" && (r42 += 1, r42 > 1)) return true;
1246
1259
  return false;
1247
1260
  }
1248
- function Ue(e55) {
1249
- for (let t10 = e55.length - 1; t10 >= 0; t10--) {
1250
- let r32 = e55[t10];
1251
- if (r32?.dataset.scrollAnchor === "true") return r32;
1261
+ function Ue(e62) {
1262
+ for (let t14 = e62.length - 1; t14 >= 0; t14--) {
1263
+ let r42 = e62[t14];
1264
+ if (r42?.dataset.scrollAnchor === "true") return r42;
1252
1265
  }
1253
1266
  return null;
1254
1267
  }
1255
- function Ye({ content: e55, spacer: t10, viewport: r32 }) {
1256
- let n = r32.getBoundingClientRect();
1257
- for (let o43 of ee(e55, t10)) {
1258
- if (!o43.dataset.messageId) continue;
1259
- let i3 = o43.getBoundingClientRect();
1260
- if (i3.bottom > n.top && i3.top < n.bottom) return o43;
1268
+ function Ye({ content: e62, spacer: t14, viewport: r42 }) {
1269
+ let n2 = r42.getBoundingClientRect();
1270
+ for (let o54 of ee(e62, t14)) {
1271
+ if (!o54.dataset.messageId) continue;
1272
+ let i3 = o54.getBoundingClientRect();
1273
+ if (i3.bottom > n2.top && i3.top < n2.bottom) return o54;
1261
1274
  }
1262
1275
  return null;
1263
1276
  }
1264
- function ze({ align: e55, element: t10, scrollMargin: r32, spacer: n, viewport: o43 }) {
1265
- let i3 = pe(t10, o43), S2 = t10.getBoundingClientRect().height, l5 = bt(n);
1266
- if (e55 === "center") {
1267
- let d2 = Math.max(0, o43.clientHeight - l5.start - l5.end);
1268
- return i3 - l5.start - (d2 - S2) / 2 - r32;
1277
+ function ze({ align: e62, element: t14, scrollMargin: r42, spacer: n2, viewport: o54 }) {
1278
+ let i3 = pe(t14, o54), S2 = t14.getBoundingClientRect().height, l5 = bt(n2);
1279
+ if (e62 === "center") {
1280
+ let d2 = Math.max(0, o54.clientHeight - l5.start - l5.end);
1281
+ return i3 - l5.start - (d2 - S2) / 2 - r42;
1269
1282
  }
1270
- if (e55 === "end") return i3 - o43.clientHeight + S2 + l5.end + r32;
1271
- if (e55 === "nearest") {
1272
- let d2 = i3 + S2, E = o43.scrollTop + l5.start, b2 = o43.scrollTop + o43.clientHeight - l5.end;
1273
- return i3 >= E && d2 <= b2 ? o43.scrollTop : i3 < E ? i3 - l5.start - r32 : d2 - o43.clientHeight + l5.end + r32;
1283
+ if (e62 === "end") return i3 - o54.clientHeight + S2 + l5.end + r42;
1284
+ if (e62 === "nearest") {
1285
+ let d2 = i3 + S2, E = o54.scrollTop + l5.start, b2 = o54.scrollTop + o54.clientHeight - l5.end;
1286
+ return i3 >= E && d2 <= b2 ? o54.scrollTop : i3 < E ? i3 - l5.start - r42 : d2 - o54.clientHeight + l5.end + r42;
1274
1287
  }
1275
- return i3 - l5.start - r32;
1288
+ return i3 - l5.start - r42;
1276
1289
  }
1277
- function pe(e55, t10) {
1278
- let r32 = e55.getBoundingClientRect(), n = t10.getBoundingClientRect();
1279
- return r32.top - n.top + t10.scrollTop;
1290
+ function pe(e62, t14) {
1291
+ let r42 = e62.getBoundingClientRect(), n2 = t14.getBoundingClientRect();
1292
+ return r42.top - n2.top + t14.scrollTop;
1280
1293
  }
1281
- function te(e55, t10) {
1282
- return e55.getBoundingClientRect().top - t10.getBoundingClientRect().top;
1294
+ function te(e62, t14) {
1295
+ return e62.getBoundingClientRect().top - t14.getBoundingClientRect().top;
1283
1296
  }
1284
- function qe({ content: e55, scrollTop: t10, spacer: r32, viewport: n }) {
1285
- let o43 = Se({ content: e55, spacer: r32, viewport: n });
1286
- return t10 + n.clientHeight - o43;
1297
+ function qe({ content: e62, scrollTop: t14, spacer: r42, viewport: n2 }) {
1298
+ let o54 = Se({ content: e62, spacer: r42, viewport: n2 });
1299
+ return t14 + n2.clientHeight - o54;
1287
1300
  }
1288
- function Se({ content: e55, spacer: t10, viewport: r32 }) {
1289
- let n = ee(e55, t10), o43 = We(e55), i3 = r32.getBoundingClientRect(), S2 = r32.scrollTop, l5 = o43.start + o43.end;
1290
- for (let d2 of n) {
1301
+ function Se({ content: e62, spacer: t14, viewport: r42 }) {
1302
+ let n2 = ee(e62, t14), o54 = We(e62), i3 = r42.getBoundingClientRect(), S2 = r42.scrollTop, l5 = o54.start + o54.end;
1303
+ for (let d2 of n2) {
1291
1304
  let E = d2.getBoundingClientRect();
1292
- l5 = Math.max(l5, E.bottom - i3.top + S2 + o43.end);
1305
+ l5 = Math.max(l5, E.bottom - i3.top + S2 + o54.end);
1293
1306
  }
1294
1307
  return l5;
1295
1308
  }
1296
- function Ke(e55) {
1297
- return Math.max(0, e55.scrollHeight - e55.clientHeight);
1309
+ function Ke(e62) {
1310
+ return Math.max(0, e62.scrollHeight - e62.clientHeight);
1298
1311
  }
1299
- function We(e55) {
1300
- let t10 = window.getComputedStyle(e55);
1301
- return { end: Re(t10.paddingBlockEnd || t10.paddingBottom), start: Re(t10.paddingBlockStart || t10.paddingTop) };
1312
+ function We(e62) {
1313
+ let t14 = window.getComputedStyle(e62);
1314
+ return { end: Re(t14.paddingBlockEnd || t14.paddingBottom), start: Re(t14.paddingBlockStart || t14.paddingTop) };
1302
1315
  }
1303
- function bt(e55) {
1304
- let t10 = e55?.parentElement;
1305
- return t10 ? We(t10) : { end: 0, start: 0 };
1316
+ function bt(e62) {
1317
+ let t14 = e62?.parentElement;
1318
+ return t14 ? We(t14) : { end: 0, start: 0 };
1306
1319
  }
1307
- function $e(e55) {
1308
- if (!e55) return 0;
1309
- let t10 = window.getComputedStyle(e55), r32 = t10.rowGap === "normal" ? t10.gap : t10.rowGap;
1310
- return Re(r32);
1320
+ function $e(e62) {
1321
+ if (!e62) return 0;
1322
+ let t14 = window.getComputedStyle(e62), r42 = t14.rowGap === "normal" ? t14.gap : t14.rowGap;
1323
+ return Re(r42);
1311
1324
  }
1312
- function Re(e55) {
1313
- if (!e55) return 0;
1314
- let t10 = Number.parseFloat(e55);
1315
- return Number.isFinite(t10) ? t10 : 0;
1325
+ function Re(e62) {
1326
+ if (!e62) return 0;
1327
+ let t14 = Number.parseFloat(e62);
1328
+ return Number.isFinite(t14) ? t14 : 0;
1316
1329
  }
1317
- function Je({ refs: e55, commitScrollState: t10, scheduleStateCommit: r32, scheduleVisibilitySync: n }) {
1318
- let { streamingTurnRef: o43, autoScrollRef: i3, autoscrollingRef: S2, autoscrollingTimeoutRef: l5, contentRef: d2, defaultScrollPositionAppliedRef: E, itemCountRef: b2, messageElementsRef: C, modeRef: p3, pendingScrollToMessageRef: m5, prependRestoreRef: L, scrollMarginRef: y, scrollPreviousItemPeekRef: u, spacerGapRef: x, spacerHeightRef: Y, spacerRef: k, viewportRef: _ } = e55, G = g.useCallback((c8) => {
1319
- l5.current !== null && (window.clearTimeout(l5.current), l5.current = null), S2.current !== c8 && (S2.current = c8, t10()), c8 && (l5.current = window.setTimeout(() => {
1320
- l5.current = null, S2.current = false, t10();
1330
+ function Je({ refs: e62, commitScrollState: t14, scheduleStateCommit: r42, scheduleVisibilitySync: n2 }) {
1331
+ let { streamingTurnRef: o54, autoScrollRef: i3, autoscrollingRef: S2, autoscrollingTimeoutRef: l5, contentRef: d2, defaultScrollPositionAppliedRef: E, itemCountRef: b2, messageElementsRef: C, modeRef: p3, pendingScrollToMessageRef: m5, prependRestoreRef: L, scrollMarginRef: y, scrollPreviousItemPeekRef: u, spacerGapRef: x, spacerHeightRef: Y, spacerRef: k, viewportRef: _ } = e62, G = g.useCallback((c8) => {
1332
+ l5.current !== null && (window.clearTimeout(l5.current), l5.current = null), S2.current !== c8 && (S2.current = c8, t14()), c8 && (l5.current = window.setTimeout(() => {
1333
+ l5.current = null, S2.current = false, t14();
1321
1334
  }, ke));
1322
- }, [t10]), v = g.useCallback((c8) => {
1335
+ }, [t14]), v = g.useCallback((c8) => {
1323
1336
  let f9 = k.current;
1324
1337
  if (!f9) return;
1325
1338
  let h = Math.max(0, Math.ceil(c8));
@@ -1329,20 +1342,20 @@ function Je({ refs: e55, commitScrollState: t10, scheduleStateCommit: r32, sched
1329
1342
  if (!P) return;
1330
1343
  let V = Math.max(0, c8);
1331
1344
  if (Math.abs(P.scrollTop - V) <= J) {
1332
- P.scrollTop = V, t10();
1345
+ P.scrollTop = V, t14();
1333
1346
  return;
1334
1347
  }
1335
- h && G(true), P.scrollTo({ top: V, behavior: f9 }), r32();
1336
- }, [t10, r32, G]), N = g.useCallback(({ behavior: c8 = "auto" } = {}) => _.current ? (v(0), o43.current = null, p3.current = "free-scrolling", H(0, { behavior: c8 }), n(), true) : false, [n, H, v]), ne = g.useCallback(({ behavior: c8 = "auto" } = {}) => {
1348
+ h && G(true), P.scrollTo({ top: V, behavior: f9 }), r42();
1349
+ }, [t14, r42, G]), N = g.useCallback(({ behavior: c8 = "auto" } = {}) => _.current ? (v(0), o54.current = null, p3.current = "free-scrolling", H(0, { behavior: c8 }), n2(), true) : false, [n2, H, v]), ne = g.useCallback(({ behavior: c8 = "auto" } = {}) => {
1337
1350
  let f9 = _.current;
1338
- return f9 ? (v(0), o43.current = null, p3.current = i3.current ? "following-bottom" : "free-scrolling", H(Ke(f9), { autoscrolling: true, behavior: c8 }), n(), true) : false;
1339
- }, [n, H, v]), A = g.useCallback((c8, { align: f9 = "start", behavior: h = "auto", scrollMargin: P = y.current } = {}, { keepPreviousPeek: V = false } = {}) => {
1351
+ return f9 ? (v(0), o54.current = null, p3.current = i3.current ? "following-bottom" : "free-scrolling", H(Ke(f9), { autoscrolling: true, behavior: c8 }), n2(), true) : false;
1352
+ }, [n2, H, v]), A = g.useCallback((c8, { align: f9 = "start", behavior: h = "auto", scrollMargin: P = y.current } = {}, { keepPreviousPeek: V = false } = {}) => {
1340
1353
  let z = d2.current, q = _.current;
1341
1354
  if (!z || !q || !z.contains(c8)) return false;
1342
1355
  let K = ze({ align: f9, element: c8, scrollMargin: V ? P + u.current : P, spacer: k.current, viewport: q }), se = qe({ content: z, scrollTop: K, spacer: k.current, viewport: q });
1343
- return v(se), L.current = { element: c8, viewportTop: te(c8, q) }, p3.current = V ? "anchored-to-message" : "settling-jump", o43.current = V ? c8 : null, H(K, { behavior: h }), n(), true;
1344
- }, [n, H, v]), B = g.useCallback(() => {
1345
- let c8 = o43.current;
1356
+ return v(se), L.current = { element: c8, viewportTop: te(c8, q) }, p3.current = V ? "anchored-to-message" : "settling-jump", o54.current = V ? c8 : null, H(K, { behavior: h }), n2(), true;
1357
+ }, [n2, H, v]), B = g.useCallback(() => {
1358
+ let c8 = o54.current;
1346
1359
  return !c8 || !c8.isConnected || p3.current !== "anchored-to-message" ? false : A(c8, { align: "start" }, { keepPreviousPeek: true });
1347
1360
  }, [A]), D = g.useCallback((c8, f9) => {
1348
1361
  let h = C.current.get(c8);
@@ -1355,52 +1368,52 @@ function Je({ refs: e55, commitScrollState: t10, scheduleStateCommit: r32, sched
1355
1368
  return !f9 || !A(f9, c8.options) ? false : (m5.current = null, E.current = true, true);
1356
1369
  }, [A]), reanchorToAnchoredMessage: B, scrollToElement: A, scrollToEnd: ne, scrollToMessage: D, scrollToStart: N };
1357
1370
  }
1358
- function Qe(e55, t10) {
1359
- let r32 = e55, n = /* @__PURE__ */ new Set();
1360
- return { getSnapshot: () => r32, hasListeners: () => n.size > 0, setSnapshot: (o43) => {
1361
- t10(r32, o43) || (r32 = o43, n.forEach((i3) => i3()));
1362
- }, subscribe: (o43, i3, S2) => {
1363
- let l5 = n.size === 0;
1364
- return n.add(o43), l5 && i3?.(), () => {
1365
- n.delete(o43), n.size === 0 && S2?.();
1371
+ function Qe(e62, t14) {
1372
+ let r42 = e62, n2 = /* @__PURE__ */ new Set();
1373
+ return { getSnapshot: () => r42, hasListeners: () => n2.size > 0, setSnapshot: (o54) => {
1374
+ t14(r42, o54) || (r42 = o54, n2.forEach((i3) => i3()));
1375
+ }, subscribe: (o54, i3, S2) => {
1376
+ let l5 = n2.size === 0;
1377
+ return n2.add(o54), l5 && i3?.(), () => {
1378
+ n2.delete(o54), n2.size === 0 && S2?.();
1366
1379
  };
1367
1380
  } };
1368
1381
  }
1369
- function Xe(e55, t10) {
1370
- return Qe(e55, t10);
1382
+ function Xe(e62, t14) {
1383
+ return Qe(e62, t14);
1371
1384
  }
1372
1385
  function Ze() {
1373
1386
  return Qe(Q, Tt);
1374
1387
  }
1375
- function et(e55, t10) {
1376
- return e55.start === t10.start && e55.end === t10.end;
1388
+ function et(e62, t14) {
1389
+ return e62.start === t14.start && e62.end === t14.end;
1377
1390
  }
1378
- function Tt(e55, t10) {
1379
- return e55.currentAnchorId !== t10.currentAnchorId || e55.visibleMessageIds.length !== t10.visibleMessageIds.length ? false : e55.visibleMessageIds.every((r32, n) => r32 === t10.visibleMessageIds[n]);
1391
+ function Tt(e62, t14) {
1392
+ return e62.currentAnchorId !== t14.currentAnchorId || e62.visibleMessageIds.length !== t14.visibleMessageIds.length ? false : e62.visibleMessageIds.every((r42, n2) => r42 === t14.visibleMessageIds[n2]);
1380
1393
  }
1381
- function tt({ autoScroll: e55, scrollEdgeThreshold: t10, scrollMargin: r32, scrollPreviousItemPeek: n }) {
1382
- let o43 = g.useRef(e55), i3 = g.useRef(false), S2 = g.useRef(null), l5 = g.useRef(false), d2 = g.useRef(t10), E = g.useRef(0), b2 = g.useRef(null), C = g.useRef(0), p3 = g.useRef(e55 ? "following-bottom" : "free-scrolling"), m5 = g.useRef(/* @__PURE__ */ new Map()), L = g.useRef(null), y = g.useRef(null), u = g.useRef(null), x = g.useRef(n), Y = g.useRef(true), k = g.useRef(null), _ = g.useRef(r32), G = g.useRef(null), v = g.useRef(0), H = g.useRef(0), N = g.useRef(null), ne = g.useRef(null), A = g.useRef(null), B = g.useRef(null), D = g.useRef(null), U = g.useRef(null), c8 = g.useRef(null), f9 = g.useRef(null), h = g.useRef(/* @__PURE__ */ new Set()), P = g.useRef(/* @__PURE__ */ new WeakSet());
1383
- return A.current === null && (A.current = Xe(ge, et)), f9.current === null && (f9.current = Ze()), o43.current = e55, d2.current = t10, _.current = r32, x.current = n, { autoScrollRef: o43, autoscrollingRef: i3, autoscrollingTimeoutRef: B, streamingTurnRef: u, contentRef: S2, defaultScrollPositionAppliedRef: l5, firstItemRef: b2, itemCountRef: E, lastScrollTopRef: C, messageElementsRef: m5, modeRef: p3, pendingScrollFrameRef: G, pendingScrollToMessageRef: L, prependRestoreRef: y, preserveScrollOnPrependRef: Y, rootRef: k, scrollEdgeThresholdRef: d2, scrollMarginRef: _, scrollPreviousItemPeekRef: x, spacerGapRef: v, spacerHeightRef: H, spacerRef: N, stateFrameRef: ne, stateStore: A.current, viewportRef: D, visibilityFrameRef: U, visibilityObserverRef: c8, visibilityStore: f9.current, visibleMessageIdsRef: h, handledScrollAnchorsRef: P };
1394
+ function tt({ autoScroll: e62, scrollEdgeThreshold: t14, scrollMargin: r42, scrollPreviousItemPeek: n2 }) {
1395
+ let o54 = g.useRef(e62), i3 = g.useRef(false), S2 = g.useRef(null), l5 = g.useRef(false), d2 = g.useRef(t14), E = g.useRef(0), b2 = g.useRef(null), C = g.useRef(0), p3 = g.useRef(e62 ? "following-bottom" : "free-scrolling"), m5 = g.useRef(/* @__PURE__ */ new Map()), L = g.useRef(null), y = g.useRef(null), u = g.useRef(null), x = g.useRef(n2), Y = g.useRef(true), k = g.useRef(null), _ = g.useRef(r42), G = g.useRef(null), v = g.useRef(0), H = g.useRef(0), N = g.useRef(null), ne = g.useRef(null), A = g.useRef(null), B = g.useRef(null), D = g.useRef(null), U = g.useRef(null), c8 = g.useRef(null), f9 = g.useRef(null), h = g.useRef(/* @__PURE__ */ new Set()), P = g.useRef(/* @__PURE__ */ new WeakSet());
1396
+ return A.current === null && (A.current = Xe(ge, et)), f9.current === null && (f9.current = Ze()), o54.current = e62, d2.current = t14, _.current = r42, x.current = n2, { autoScrollRef: o54, autoscrollingRef: i3, autoscrollingTimeoutRef: B, streamingTurnRef: u, contentRef: S2, defaultScrollPositionAppliedRef: l5, firstItemRef: b2, itemCountRef: E, lastScrollTopRef: C, messageElementsRef: m5, modeRef: p3, pendingScrollFrameRef: G, pendingScrollToMessageRef: L, prependRestoreRef: y, preserveScrollOnPrependRef: Y, rootRef: k, scrollEdgeThresholdRef: d2, scrollMarginRef: _, scrollPreviousItemPeekRef: x, spacerGapRef: v, spacerHeightRef: H, spacerRef: N, stateFrameRef: ne, stateStore: A.current, viewportRef: D, visibilityFrameRef: U, visibilityObserverRef: c8, visibilityStore: f9.current, visibleMessageIdsRef: h, handledScrollAnchorsRef: P };
1384
1397
  }
1385
- function rt(e55, t10) {
1386
- return g.useCallback((r32) => {
1387
- e55.current = r32, r32 && t10();
1388
- }, [e55, t10]);
1398
+ function rt(e62, t14) {
1399
+ return g.useCallback((r42) => {
1400
+ e62.current = r42, r42 && t14();
1401
+ }, [e62, t14]);
1389
1402
  }
1390
- function nt({ autoScroll: e55 = false, defaultScrollPosition: t10 = "end", scrollEdgeThreshold: r32 = xe, scrollPreviousItemPeek: n = Ve, scrollMargin: o43 = _e }) {
1391
- let i3 = tt({ autoScroll: e55, scrollEdgeThreshold: r32, scrollMargin: o43, scrollPreviousItemPeek: n }), { streamingTurnRef: S2, autoScrollRef: l5, autoscrollingRef: d2, autoscrollingTimeoutRef: E, contentRef: b2, defaultScrollPositionAppliedRef: C, firstItemRef: p3, itemCountRef: m5, lastScrollTopRef: L, messageElementsRef: y, modeRef: u, pendingScrollFrameRef: x, pendingScrollToMessageRef: Y, prependRestoreRef: k, preserveScrollOnPrependRef: _, rootRef: G, scrollEdgeThresholdRef: v, scrollMarginRef: H, scrollPreviousItemPeekRef: N, spacerGapRef: ne, spacerHeightRef: A, spacerRef: B, stateFrameRef: D, stateStore: U, viewportRef: c8, visibilityFrameRef: f9, visibilityObserverRef: h, visibilityStore: P, visibleMessageIdsRef: V, handledScrollAnchorsRef: z } = i3, q = g.useRef(t10);
1392
- q.current !== t10 && (q.current = t10, C.current = false);
1403
+ function nt({ autoScroll: e62 = false, defaultScrollPosition: t14 = "end", scrollEdgeThreshold: r42 = xe, scrollPreviousItemPeek: n2 = Ve, scrollMargin: o54 = _e }) {
1404
+ let i3 = tt({ autoScroll: e62, scrollEdgeThreshold: r42, scrollMargin: o54, scrollPreviousItemPeek: n2 }), { streamingTurnRef: S2, autoScrollRef: l5, autoscrollingRef: d2, autoscrollingTimeoutRef: E, contentRef: b2, defaultScrollPositionAppliedRef: C, firstItemRef: p3, itemCountRef: m5, lastScrollTopRef: L, messageElementsRef: y, modeRef: u, pendingScrollFrameRef: x, pendingScrollToMessageRef: Y, prependRestoreRef: k, preserveScrollOnPrependRef: _, rootRef: G, scrollEdgeThresholdRef: v, scrollMarginRef: H, scrollPreviousItemPeekRef: N, spacerGapRef: ne, spacerHeightRef: A, spacerRef: B, stateFrameRef: D, stateStore: U, viewportRef: c8, visibilityFrameRef: f9, visibilityObserverRef: h, visibilityStore: P, visibleMessageIdsRef: V, handledScrollAnchorsRef: z } = i3, q = g.useRef(t14);
1405
+ q.current !== t14 && (q.current = t14, C.current = false);
1393
1406
  let K = g.useCallback((s4) => {
1394
- let a28 = G.current, T = c8.current, O = [s4.start && "start", s4.end && "end"].filter(Boolean).join(" "), ue = d2.current;
1395
- for (let W of [a28, T]) W && (O ? W.setAttribute("data-scrollable", O) : W.removeAttribute("data-scrollable"), W.toggleAttribute("data-autoscrolling", ue));
1407
+ let a30 = G.current, T = c8.current, O = [s4.start && "start", s4.end && "end"].filter(Boolean).join(" "), ue = d2.current;
1408
+ for (let W of [a30, T]) W && (O ? W.setAttribute("data-scrollable", O) : W.removeAttribute("data-scrollable"), W.toggleAttribute("data-autoscrolling", ue));
1396
1409
  }, []), se = g.useCallback((s4) => {
1397
- let a28 = c8.current?.scrollTop ?? 0, T = a28 < L.current - J;
1398
- L.current = a28, l5.current && !s4.end && u.current !== "settling-jump" && u.current !== "anchored-to-message" ? u.current = "following-bottom" : u.current === "following-bottom" && s4.end && T && !d2.current && (u.current = "free-scrolling");
1410
+ let a30 = c8.current?.scrollTop ?? 0, T = a30 < L.current - J;
1411
+ L.current = a30, l5.current && !s4.end && u.current !== "settling-jump" && u.current !== "anchored-to-message" ? u.current = "following-bottom" : u.current === "following-bottom" && s4.end && T && !d2.current && (u.current = "free-scrolling");
1399
1412
  }, []), F = g.useCallback(() => {
1400
1413
  let s4 = De({ content: b2.current, scrollEdgeThreshold: v.current, spacer: B.current, viewport: c8.current });
1401
1414
  se(s4);
1402
- let a28 = u.current === "following-bottom" ? { ...s4, end: false } : s4;
1403
- K(a28), U.setSnapshot(a28);
1415
+ let a30 = u.current === "following-bottom" ? { ...s4, end: false } : s4;
1416
+ K(a30), U.setSnapshot(a30);
1404
1417
  }, [se, U, K]), oe = g.useCallback(() => {
1405
1418
  D.current === null && (D.current = window.requestAnimationFrame(() => {
1406
1419
  D.current = null, F();
@@ -1410,54 +1423,54 @@ function nt({ autoScroll: e55 = false, defaultScrollPosition: t10 = "end", scrol
1410
1423
  f9.current = null, P.hasListeners() && P.setSnapshot(Fe({ content: b2.current, scrollMargin: H.current, scrollPreviousItemPeek: N.current, spacer: B.current, viewport: c8.current, visibleMessageIds: V.current }));
1411
1424
  }));
1412
1425
  }, [P]), { flushPendingScrollToMessage: ce, reanchorToAnchoredMessage: me, scrollToElement: le, scrollToEnd: I2, scrollToMessage: Me, scrollToStart: ae } = Je({ refs: i3, commitScrollState: F, scheduleStateCommit: oe, scheduleVisibilitySync: w2 }), de = g.useCallback(() => {
1413
- let s4 = k.current, a28 = c8.current;
1414
- if (!s4 || !a28 || !s4.element.isConnected) return false;
1415
- let O = te(s4.element, a28) - s4.viewportTop;
1416
- return Math.abs(O) <= J ? false : (a28.scrollTop += O, s4.viewportTop = te(s4.element, a28), oe(), w2(), true);
1426
+ let s4 = k.current, a30 = c8.current;
1427
+ if (!s4 || !a30 || !s4.element.isConnected) return false;
1428
+ let O = te(s4.element, a30) - s4.viewportTop;
1429
+ return Math.abs(O) <= J ? false : (a30.scrollTop += O, s4.viewportTop = te(s4.element, a30), oe(), w2(), true);
1417
1430
  }, [oe, w2]), Z = g.useCallback(() => {
1418
- let s4 = b2.current, a28 = c8.current;
1419
- if (!s4 || !a28) {
1431
+ let s4 = b2.current, a30 = c8.current;
1432
+ if (!s4 || !a30) {
1420
1433
  k.current = null;
1421
1434
  return;
1422
1435
  }
1423
- let T = Ye({ content: s4, spacer: B.current, viewport: a28 });
1424
- k.current = T ? { element: T, viewportTop: te(T, a28) } : null;
1436
+ let T = Ye({ content: s4, spacer: B.current, viewport: a30 });
1437
+ k.current = T ? { element: T, viewportTop: te(T, a30) } : null;
1425
1438
  }, []), be = g.useCallback(() => {
1426
1439
  x.current === null && (x.current = window.requestAnimationFrame(() => {
1427
1440
  x.current = null, ce() && Z();
1428
1441
  }));
1429
1442
  }, [Z, ce]), ie = g.useCallback(() => {
1430
- if (!t10 || C.current || m5.current === 0) return false;
1443
+ if (!t14 || C.current || m5.current === 0) return false;
1431
1444
  let s4 = false;
1432
- if (t10 === "last-anchor") {
1433
- let a28 = b2.current, T = c8.current, O = a28 && T ? Ue(ee(a28, B.current)) : null;
1434
- if (!a28 || !T || !O) s4 = I2({ behavior: "auto" });
1445
+ if (t14 === "last-anchor") {
1446
+ let a30 = b2.current, T = c8.current, O = a30 && T ? Ue(ee(a30, B.current)) : null;
1447
+ if (!a30 || !T || !O) s4 = I2({ behavior: "auto" });
1435
1448
  else {
1436
1449
  let ue = pe(O, T);
1437
- s4 = Se({ content: a28, spacer: B.current, viewport: T }) - ue <= T.clientHeight ? I2({ behavior: "auto" }) : le(O, { align: "start" }, { keepPreviousPeek: true });
1450
+ s4 = Se({ content: a30, spacer: B.current, viewport: T }) - ue <= T.clientHeight ? I2({ behavior: "auto" }) : le(O, { align: "start" }, { keepPreviousPeek: true });
1438
1451
  }
1439
- } else s4 = t10 === "end" ? I2({ behavior: "auto" }) : ae({ behavior: "auto" });
1452
+ } else s4 = t14 === "end" ? I2({ behavior: "auto" }) : ae({ behavior: "auto" });
1440
1453
  return s4 ? (C.current = true, true) : false;
1441
- }, [t10, le, I2, ae]), Te = g.useCallback(() => {
1454
+ }, [t14, le, I2, ae]), Te = g.useCallback(() => {
1442
1455
  let s4 = b2.current;
1443
1456
  if (!s4) return;
1444
- let a28 = ee(s4, B.current), T = m5.current, O = p3.current;
1445
- m5.current = a28.length, p3.current = a28[0] ?? null, (() => {
1457
+ let a30 = ee(s4, B.current), T = m5.current, O = p3.current;
1458
+ m5.current = a30.length, p3.current = a30[0] ?? null, (() => {
1446
1459
  if (ce()) return;
1447
1460
  if (T === 0) {
1448
- if (ie() || a28.length > 0 && l5.current && I2({ behavior: "auto" })) return;
1461
+ if (ie() || a30.length > 0 && l5.current && I2({ behavior: "auto" })) return;
1449
1462
  F(), w2();
1450
1463
  return;
1451
1464
  }
1452
- let W = O ? a28.indexOf(O) : -1;
1465
+ let W = O ? a30.indexOf(O) : -1;
1453
1466
  if (_.current && W > 0) {
1454
1467
  de();
1455
1468
  return;
1456
1469
  }
1457
- if (a28.length > T) {
1458
- let $ = je(a28, T);
1470
+ if (a30.length > T) {
1471
+ let $ = je(a30, T);
1459
1472
  if ($) {
1460
- if (l5.current && u.current === "following-bottom" && Ne(a28, T)) {
1473
+ if (l5.current && u.current === "following-bottom" && Ne(a30, T)) {
1461
1474
  I2({ behavior: "auto" });
1462
1475
  return;
1463
1476
  }
@@ -1465,8 +1478,8 @@ function nt({ autoScroll: e55 = false, defaultScrollPosition: t10 = "end", scrol
1465
1478
  return;
1466
1479
  }
1467
1480
  }
1468
- if (a28.length === T) {
1469
- let $ = Ge(a28, z.current);
1481
+ if (a30.length === T) {
1482
+ let $ = Ge(a30, z.current);
1470
1483
  if ($) {
1471
1484
  le($, { align: "start" }, { keepPreviousPeek: true }), z.current.add($);
1472
1485
  return;
@@ -1492,21 +1505,21 @@ function nt({ autoScroll: e55 = false, defaultScrollPosition: t10 = "end", scrol
1492
1505
  w2();
1493
1506
  return;
1494
1507
  }
1495
- h.current || (h.current = new IntersectionObserver((a28) => {
1496
- for (let T of a28) {
1508
+ h.current || (h.current = new IntersectionObserver((a30) => {
1509
+ for (let T of a30) {
1497
1510
  let O = T.target.dataset.messageId;
1498
1511
  O && (T.isIntersecting ? V.current.add(O) : V.current.delete(O));
1499
1512
  }
1500
1513
  w2();
1501
- }, { root: s4, rootMargin: `${-(H.current + N.current)}px 0px 0px 0px`, threshold: [0, 0.01, 0.5, 1] })), y.current.forEach((a28) => {
1502
- h.current?.observe(a28);
1514
+ }, { root: s4, rootMargin: `${-(H.current + N.current)}px 0px 0px 0px`, threshold: [0, 0.01, 0.5, 1] })), y.current.forEach((a30) => {
1515
+ h.current?.observe(a30);
1503
1516
  }), w2();
1504
1517
  }
1505
1518
  }, [w2, P]), he = g.useCallback(() => {
1506
1519
  f9.current !== null && (window.cancelAnimationFrame(f9.current), f9.current = null), h.current?.disconnect(), h.current = null, V.current.clear(), P.setSnapshot(Q);
1507
- }, [P]), pt = g.useCallback((s4, a28, T) => {
1508
- if (a28) {
1509
- y.current.set(s4, a28), h.current?.observe(a28), w2(), Y.current?.messageId === s4 && be();
1520
+ }, [P]), pt = g.useCallback((s4, a30, T) => {
1521
+ if (a30) {
1522
+ y.current.set(s4, a30), h.current?.observe(a30), w2(), Y.current?.messageId === s4 && be();
1510
1523
  return;
1511
1524
  }
1512
1525
  T && y.current.get(s4) === T && (y.current.delete(s4), V.current.delete(s4), h.current?.unobserve(T), w2());
@@ -1524,54 +1537,54 @@ function nt({ autoScroll: e55 = false, defaultScrollPosition: t10 = "end", scrol
1524
1537
  }, [ie]), g.useEffect(() => () => {
1525
1538
  D.current !== null && (window.cancelAnimationFrame(D.current), D.current = null), f9.current !== null && (window.cancelAnimationFrame(f9.current), f9.current = null), E.current !== null && (window.clearTimeout(E.current), E.current = null), x.current !== null && (window.cancelAnimationFrame(x.current), x.current = null), h.current?.disconnect(), h.current = null;
1526
1539
  }, []), g.useLayoutEffect(() => {
1527
- if (e55 && u.current === "following-bottom" && m5.current > 0) {
1540
+ if (e62 && u.current === "following-bottom" && m5.current > 0) {
1528
1541
  I2({ behavior: "auto" });
1529
1542
  return;
1530
1543
  }
1531
1544
  F();
1532
- }, [e55, F, I2]), { context: mt, registerMessage: pt };
1545
+ }, [e62, F, I2]), { context: mt, registerMessage: pt };
1533
1546
  }
1534
- function lt(e55) {
1535
- let t10 = g.useRef(e55);
1536
- return t10.current = e55, t10;
1547
+ function lt(e62) {
1548
+ let t14 = g.useRef(e62);
1549
+ return t14.current = e62, t14;
1537
1550
  }
1538
1551
  var st = g.createContext(null);
1539
1552
  var ct = g.createContext(null);
1540
1553
  function X2() {
1541
- let e55 = g.useContext(st);
1542
- if (!e55) throw new Error("useMessageScroller must be used within a MessageScroller.");
1543
- return e55;
1554
+ let e62 = g.useContext(st);
1555
+ if (!e62) throw new Error("useMessageScroller must be used within a MessageScroller.");
1556
+ return e62;
1544
1557
  }
1545
1558
  function Et() {
1546
- let e55 = g.useContext(ct);
1547
- if (!e55) throw new Error("MessageScrollerItem must be used within a MessageScroller.");
1548
- return e55;
1559
+ let e62 = g.useContext(ct);
1560
+ if (!e62) throw new Error("MessageScrollerItem must be used within a MessageScroller.");
1561
+ return e62;
1549
1562
  }
1550
- function at({ autoScroll: e55 = false, children: t10, defaultScrollPosition: r32 = "end", scrollEdgeThreshold: n, scrollPreviousItemPeek: o43, scrollMargin: i3 }) {
1551
- let { context: S2, registerMessage: l5 } = nt({ autoScroll: e55, defaultScrollPosition: r32, scrollEdgeThreshold: n, scrollPreviousItemPeek: o43, scrollMargin: i3 });
1552
- return jsx3(st.Provider, { value: S2, children: jsx3(ct.Provider, { value: l5, children: t10 }) });
1563
+ function at({ autoScroll: e62 = false, children: t14, defaultScrollPosition: r42 = "end", scrollEdgeThreshold: n2, scrollPreviousItemPeek: o54, scrollMargin: i3 }) {
1564
+ let { context: S2, registerMessage: l5 } = nt({ autoScroll: e62, defaultScrollPosition: r42, scrollEdgeThreshold: n2, scrollPreviousItemPeek: o54, scrollMargin: i3 });
1565
+ return jsx3(st.Provider, { value: S2, children: jsx3(ct.Provider, { value: l5, children: t14 }) });
1553
1566
  }
1554
- function it({ children: e55, ...t10 }) {
1555
- let { setRootElement: r32 } = X2();
1556
- return jsx3("div", { ref: r32, ...t10, children: e55 });
1567
+ function it({ children: e62, ...t14 }) {
1568
+ let { setRootElement: r42 } = X2();
1569
+ return jsx3("div", { ref: r42, ...t14, children: e62 });
1557
1570
  }
1558
- function ut({ "aria-label": e55, children: t10, onKeyDown: r32, onScroll: n, onTouchMove: o43, onWheel: i3, preserveScrollOnPrepend: S2 = true, ref: l5, role: d2, tabIndex: E, ...b2 }) {
1571
+ function ut({ "aria-label": e62, children: t14, onKeyDown: r42, onScroll: n2, onTouchMove: o54, onWheel: i3, preserveScrollOnPrepend: S2 = true, ref: l5, role: d2, tabIndex: E, ...b2 }) {
1559
1572
  let { handleResize: C, preserveScrollOnPrependRef: p3, setViewportElement: m5, syncAfterScroll: L, userScrollIntent: y, viewportRef: u } = X2();
1560
1573
  p3.current = S2;
1561
1574
  let x = g.useCallback((v) => {
1562
1575
  m5(v), f8(l5)?.(v);
1563
1576
  }, [l5, m5]);
1564
1577
  function Y(v) {
1565
- L(), n?.(v);
1578
+ L(), n2?.(v);
1566
1579
  }
1567
1580
  function k(v) {
1568
1581
  y(), i3?.(v);
1569
1582
  }
1570
1583
  function _(v) {
1571
- y(), o43?.(v);
1584
+ y(), o54?.(v);
1572
1585
  }
1573
1586
  function G(v) {
1574
- Be.has(v.key) && y(), r32?.(v);
1587
+ Be.has(v.key) && y(), r42?.(v);
1575
1588
  }
1576
1589
  return g.useEffect(() => {
1577
1590
  let v = u.current;
@@ -1582,12 +1595,12 @@ function ut({ "aria-label": e55, children: t10, onKeyDown: r32, onScroll: n, onT
1582
1595
  return N.observe(v), () => {
1583
1596
  window.cancelAnimationFrame(H), N.disconnect();
1584
1597
  };
1585
- }, [C, u]), jsx3("div", { ref: x, role: d2 ?? "region", "aria-label": e55 ?? "Messages", tabIndex: E ?? 0, onKeyDown: G, onScroll: Y, onTouchMove: _, onWheel: k, ...b2, children: t10 });
1598
+ }, [C, u]), jsx3("div", { ref: x, role: d2 ?? "region", "aria-label": e62 ?? "Messages", tabIndex: E ?? 0, onKeyDown: G, onScroll: Y, onTouchMove: _, onWheel: k, ...b2, children: t14 });
1586
1599
  }
1587
- function ft({ "aria-relevant": e55, children: t10, ref: r32, role: n, spacerClassName: o43, ...i3 }) {
1600
+ function ft({ "aria-relevant": e62, children: t14, ref: r42, role: n2, spacerClassName: o54, ...i3 }) {
1588
1601
  let { handleContentChange: S2, handleResize: l5, setContentElement: d2, setSpacerElement: E } = X2(), b2 = g.useRef(null), C = g.useCallback((p3) => {
1589
- b2.current = p3, d2(p3), f8(r32)?.(p3);
1590
- }, [r32, d2]);
1602
+ b2.current = p3, d2(p3), f8(r42)?.(p3);
1603
+ }, [r42, d2]);
1591
1604
  return g.useLayoutEffect(() => {
1592
1605
  let p3 = b2.current;
1593
1606
  if (!p3 || (S2(), typeof MutationObserver > "u")) return;
@@ -1604,23 +1617,23 @@ function ft({ "aria-relevant": e55, children: t10, ref: r32, role: n, spacerClas
1604
1617
  return L.observe(p3), () => {
1605
1618
  window.cancelAnimationFrame(m5), L.disconnect();
1606
1619
  };
1607
- }, [l5]), jsxs("div", { ref: C, role: n ?? "log", "aria-relevant": e55 ?? "additions", ...i3, children: [t10, jsx3("div", { ref: E, "aria-hidden": "true", "data-message-scroller-spacer": "", hidden: true, className: o43 })] });
1620
+ }, [l5]), jsxs("div", { ref: C, role: n2 ?? "log", "aria-relevant": e62 ?? "additions", ...i3, children: [t14, jsx3("div", { ref: E, "aria-hidden": "true", "data-message-scroller-spacer": "", hidden: true, className: o54 })] });
1608
1621
  }
1609
- function gt({ messageId: e55, ref: t10, scrollAnchor: r32 = false, ...n }) {
1610
- let o43 = Et(), i3 = g.useRef(null), S2 = g.useCallback((l5) => {
1622
+ function gt({ messageId: e62, ref: t14, scrollAnchor: r42 = false, ...n2 }) {
1623
+ let o54 = Et(), i3 = g.useRef(null), S2 = g.useCallback((l5) => {
1611
1624
  let d2 = i3.current;
1612
- i3.current = l5, e55 && o43(e55, l5, d2), f8(t10)?.(l5);
1613
- }, [e55, t10, o43]);
1614
- return jsx3("div", { ref: S2, "data-message-id": e55, "data-scroll-anchor": r32 ? "true" : "false", ...n });
1625
+ i3.current = l5, e62 && o54(e62, l5, d2), f8(t14)?.(l5);
1626
+ }, [e62, t14, o54]);
1627
+ return jsx3("div", { ref: S2, "data-message-id": e62, "data-scroll-anchor": r42 ? "true" : "false", ...n2 });
1615
1628
  }
1616
- function St({ behavior: e55 = "smooth", children: t10, direction: r32 = "end", onClick: n, render: o43, tabIndex: i3, type: S2 = "button", ...l5 }) {
1617
- let { scrollToEnd: d2, scrollToStart: E, stateStore: b$1 } = X2(), C = lt(n), p3 = g.useCallback((u) => b$1.subscribe(u), [b$1]), m5 = g.useCallback(() => {
1629
+ function St({ behavior: e62 = "smooth", children: t14, direction: r42 = "end", onClick: n2, render: o54, tabIndex: i3, type: S2 = "button", ...l5 }) {
1630
+ let { scrollToEnd: d2, scrollToStart: E, stateStore: b$1 } = X2(), C = lt(n2), p3 = g.useCallback((u) => b$1.subscribe(u), [b$1]), m5 = g.useCallback(() => {
1618
1631
  let u = b$1.getSnapshot();
1619
- return r32 === "start" ? u.start : u.end;
1620
- }, [r32, b$1]), L = g.useSyncExternalStore(p3, m5, m5), y = g.useCallback((u) => {
1621
- L && (C.current?.(u), u.defaultPrevented || (u.currentTarget.blur(), r32 === "start" ? E({ behavior: e55 }) : d2({ behavior: e55 })));
1622
- }, [e55, r32, L, C, d2, E]);
1623
- return S({ defaultTagName: "button", props: d({ type: S2, inert: !L, tabIndex: L ? i3 : -1, children: t10 ?? jsxs("span", { children: ["Scroll to ", r32] }), onClick: y }, l5), render: o43, state: { active: L, direction: r32 }, stateAttributesMapping: { active: (u) => ({ "data-active": u ? "true" : "false" }) } });
1632
+ return r42 === "start" ? u.start : u.end;
1633
+ }, [r42, b$1]), L = g.useSyncExternalStore(p3, m5, m5), y = g.useCallback((u) => {
1634
+ L && (C.current?.(u), u.defaultPrevented || (u.currentTarget.blur(), r42 === "start" ? E({ behavior: e62 }) : d2({ behavior: e62 })));
1635
+ }, [e62, r42, L, C, d2, E]);
1636
+ return S({ defaultTagName: "button", props: d({ type: S2, inert: !L, tabIndex: L ? i3 : -1, children: t14 ?? jsxs("span", { children: ["Scroll to ", r42] }), onClick: y }, l5), render: o54, state: { active: L, direction: r42 }, stateAttributesMapping: { active: (u) => ({ "data-active": u ? "true" : "false" }) } });
1624
1637
  }
1625
1638
  var $t = { Provider: at, Root: it, Viewport: ut, Content: ft, Item: gt, Button: St };
1626
1639
 
@@ -1859,14 +1872,14 @@ var use = React2[" use ".trim().toString()];
1859
1872
  // ../../apps/web/src/components/ui/button.tsx
1860
1873
  import { jsx as jsx4 } from "react/jsx-runtime";
1861
1874
  var buttonVariants = cva(
1862
- "group/button inline-flex shrink-0 items-center justify-center rounded-full border border-transparent bg-clip-padding text-sm font-medium whitespace-nowrap transition-all outline-none select-none touch-manipulation focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 active:not-aria-[haspopup]:translate-y-px disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
1875
+ "group/button inline-flex shrink-0 items-center justify-center rounded-lg border border-transparent bg-clip-padding text-sm font-medium whitespace-nowrap transition-[background-color,border-color,color,box-shadow,translate] duration-(--duration-quick) ease-(--ease-smooth-out) motion-reduce:transition-none outline-none select-none touch-manipulation focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/25 motion-safe:active:not-aria-[haspopup]:translate-y-px disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
1863
1876
  {
1864
1877
  variants: {
1865
1878
  variant: {
1866
- default: "bg-primary text-primary-foreground hover:bg-primary/80",
1867
- outline: "border-border bg-background hover:bg-muted hover:text-foreground aria-expanded:bg-muted aria-expanded:text-foreground dark:border-input dark:bg-input/30 dark:hover:bg-input/50",
1868
- secondary: "bg-secondary text-secondary-foreground hover:bg-[color-mix(in_oklch,var(--secondary),var(--foreground)_5%)] aria-expanded:bg-secondary aria-expanded:text-secondary-foreground",
1869
- ghost: "hover:bg-muted hover:text-foreground aria-expanded:bg-muted aria-expanded:text-foreground dark:hover:bg-muted/50",
1879
+ default: "surface-primary bg-primary bg-clip-border text-primary-foreground hover:bg-primary/90 active:shadow-field",
1880
+ outline: "surface-control border-input/80 bg-card text-foreground hover:border-input hover:bg-muted/60 aria-expanded:bg-muted aria-expanded:text-foreground active:shadow-field",
1881
+ secondary: "surface-control border-border bg-secondary text-secondary-foreground hover:bg-muted hover:text-foreground aria-expanded:bg-muted aria-expanded:text-foreground active:shadow-field",
1882
+ ghost: "text-muted-foreground hover:bg-muted hover:text-foreground aria-expanded:bg-muted aria-expanded:text-foreground",
1870
1883
  destructive: "bg-destructive/10 text-destructive hover:bg-destructive/20 focus-visible:border-destructive/40 focus-visible:ring-destructive/20 dark:bg-destructive/20 dark:hover:bg-destructive/30 dark:focus-visible:ring-destructive/40",
1871
1884
  link: "text-primary underline-offset-4 hover:underline"
1872
1885
  },
@@ -1907,34 +1920,6 @@ function Button({
1907
1920
  );
1908
1921
  }
1909
1922
 
1910
- // ../../apps/web/src/hooks/use-is-resizing.ts
1911
- import { useEffect as useEffect2, useState } from "react";
1912
- var SETTLE_MS = 120;
1913
- function useIsResizing(target) {
1914
- const [isResizing, setIsResizing] = useState(false);
1915
- useEffect2(() => {
1916
- const node = target.current;
1917
- if (!node || typeof ResizeObserver === "undefined") return;
1918
- let settle = 0;
1919
- let isOpeningReport = true;
1920
- const observer = new ResizeObserver(() => {
1921
- if (isOpeningReport) {
1922
- isOpeningReport = false;
1923
- return;
1924
- }
1925
- setIsResizing(true);
1926
- window.clearTimeout(settle);
1927
- settle = window.setTimeout(() => setIsResizing(false), SETTLE_MS);
1928
- });
1929
- observer.observe(node);
1930
- return () => {
1931
- window.clearTimeout(settle);
1932
- observer.disconnect();
1933
- };
1934
- }, [target]);
1935
- return isResizing;
1936
- }
1937
-
1938
1923
  // ../../apps/web/src/components/ui/message-scroller.tsx
1939
1924
  import { Fragment as Fragment25, jsx as jsx5, jsxs as jsxs2 } from "react/jsx-runtime";
1940
1925
  function MessageScrollerProvider({
@@ -1969,9 +1954,12 @@ function usePinnedToEnd(viewport) {
1969
1954
  const remember = () => {
1970
1955
  wasAtEnd = node.scrollHeight - node.scrollTop - node.clientHeight <= AT_END_PX;
1971
1956
  };
1957
+ let height = node.clientHeight;
1972
1958
  const observer = new ResizeObserver(() => {
1973
1959
  const drift = node.scrollHeight - node.scrollTop - node.clientHeight;
1974
- if (wasAtEnd && drift > DRIFT_PX) {
1960
+ const shrank = node.clientHeight < height;
1961
+ height = node.clientHeight;
1962
+ if (wasAtEnd && shrank && drift > DRIFT_PX) {
1975
1963
  node.scrollTo({
1976
1964
  top: node.scrollHeight - node.clientHeight,
1977
1965
  behavior: "instant"
@@ -1992,11 +1980,11 @@ var MAX_STEP_MS = 64;
1992
1980
  function prefersReducedMotion() {
1993
1981
  return window.matchMedia("(prefers-reduced-motion: reduce)").matches;
1994
1982
  }
1995
- function useGlidingScroll(viewport, isSettled) {
1996
- const settled = React3.useRef(isSettled);
1983
+ function useGlidingScroll(viewport, hasLanded) {
1984
+ const landed = React3.useRef(hasLanded);
1997
1985
  React3.useLayoutEffect(() => {
1998
- settled.current = isSettled;
1999
- }, [isSettled]);
1986
+ landed.current = hasLanded;
1987
+ }, [hasLanded]);
2000
1988
  React3.useEffect(() => {
2001
1989
  const node = viewport.current;
2002
1990
  if (!node) return;
@@ -2004,6 +1992,7 @@ function useGlidingScroll(viewport, isSettled) {
2004
1992
  let target = null;
2005
1993
  let frame = 0;
2006
1994
  let last = 0;
1995
+ let box = { width: node.clientWidth, height: node.clientHeight };
2007
1996
  const stop = () => {
2008
1997
  if (frame !== 0) cancelAnimationFrame(frame);
2009
1998
  frame = 0;
@@ -2012,11 +2001,12 @@ function useGlidingScroll(viewport, isSettled) {
2012
2001
  const step = (now) => {
2013
2002
  frame = 0;
2014
2003
  if (target === null) return;
2015
- const elapsed = Math.min(now - last, MAX_STEP_MS);
2004
+ const elapsed = Math.max(0, Math.min(now - last, MAX_STEP_MS));
2016
2005
  last = now;
2017
- const left = target - node.scrollTop;
2006
+ const goal = Math.min(target, node.scrollHeight - node.clientHeight);
2007
+ const left = goal - node.scrollTop;
2018
2008
  if (Math.abs(left) < 0.5) {
2019
- node.scrollTop = target;
2009
+ node.scrollTop = goal;
2020
2010
  target = null;
2021
2011
  return;
2022
2012
  }
@@ -2025,7 +2015,10 @@ function useGlidingScroll(viewport, isSettled) {
2025
2015
  };
2026
2016
  const glide = (options, y) => {
2027
2017
  const asked = typeof options === "number" ? { left: options, top: y } : options;
2028
- const glides = asked !== void 0 && typeof asked.top === "number" && asked.behavior !== "instant" && settled.current && !prefersReducedMotion();
2018
+ const size = { width: node.clientWidth, height: node.clientHeight };
2019
+ const shrank = size.width !== box.width || size.height < box.height;
2020
+ box = size;
2021
+ const glides = asked !== void 0 && typeof asked.top === "number" && asked.behavior !== "instant" && landed.current && !shrank && !prefersReducedMotion();
2029
2022
  if (!glides) {
2030
2023
  stop();
2031
2024
  scrollNatively.call(node, asked);
@@ -2070,10 +2063,8 @@ function MessageScrollerViewport({
2070
2063
  }) {
2071
2064
  const viewport = React3.useRef(null);
2072
2065
  const hasLanded = useHasLanded();
2073
- const isResizing = useIsResizing(viewport);
2074
- const isSettled = hasLanded && !isResizing;
2075
2066
  usePinnedToEnd(viewport);
2076
- useGlidingScroll(viewport, isSettled);
2067
+ useGlidingScroll(viewport, hasLanded);
2077
2068
  const holdViewport = React3.useCallback(
2078
2069
  (node) => {
2079
2070
  viewport.current = node;
@@ -2087,7 +2078,6 @@ function MessageScrollerViewport({
2087
2078
  {
2088
2079
  ref: holdViewport,
2089
2080
  "data-slot": "message-scroller-viewport",
2090
- "data-settled": isSettled ? "" : void 0,
2091
2081
  className: cn(
2092
2082
  "size-full min-h-0 min-w-0 overflow-y-auto overscroll-contain contain-content [scrollbar-gutter:stable] [scrollbar-width:thin]",
2093
2083
  // The fade says "there is more below", so it only belongs to a
@@ -2266,9 +2256,9 @@ var SETTLE_BODY = cn(
2266
2256
 
2267
2257
  // ../../apps/web/src/components/support-widget/widget-composer.tsx
2268
2258
  import {
2269
- useEffect as useEffect6,
2270
- useRef as useRef7,
2271
- useState as useState5
2259
+ useEffect as useEffect5,
2260
+ useRef as useRef8,
2261
+ useState as useState4
2272
2262
  } from "react";
2273
2263
 
2274
2264
  // src/next-image-shim.tsx
@@ -2296,7 +2286,7 @@ import { jsx as jsx8, jsxs as jsxs3 } from "react/jsx-runtime";
2296
2286
  var KIND_ICONS = {
2297
2287
  image: ImageSquare,
2298
2288
  video: Play,
2299
- file: File
2289
+ file: File2
2300
2290
  };
2301
2291
  function AttachmentDraftChip({
2302
2292
  draft,
@@ -2369,7 +2359,7 @@ function AttachmentDraftChip({
2369
2359
  }
2370
2360
 
2371
2361
  // ../../apps/web/src/components/attachments/use-attachment-drafts.ts
2372
- import { useEffect as useEffect4, useRef as useRef4, useState as useState3 } from "react";
2362
+ import { useEffect as useEffect3, useRef as useRef4, useState as useState2 } from "react";
2373
2363
  var MAX_BYTES = 25 * 1024 * 1024;
2374
2364
  var UNSAFE_TYPES = [
2375
2365
  "text/html",
@@ -2410,12 +2400,12 @@ function useAttachmentDrafts({
2410
2400
  accept,
2411
2401
  max
2412
2402
  } = {}) {
2413
- const [drafts, setDrafts] = useState3([]);
2403
+ const [drafts, setDrafts] = useState2([]);
2414
2404
  const draftsRef = useRef4(drafts);
2415
- useEffect4(() => {
2405
+ useEffect3(() => {
2416
2406
  draftsRef.current = drafts;
2417
2407
  }, [drafts]);
2418
- useEffect4(() => () => draftsRef.current.forEach(release), []);
2408
+ useEffect3(() => () => draftsRef.current.forEach(release), []);
2419
2409
  function add(files) {
2420
2410
  const picked = Array.from(files).map((file, index) => {
2421
2411
  const isTooBig = file.size > MAX_BYTES;
@@ -2460,12 +2450,12 @@ function useAttachmentDrafts({
2460
2450
  }
2461
2451
 
2462
2452
  // ../../apps/web/src/components/attachments/use-attachment-target.ts
2463
- import { useRef as useRef5, useState as useState4 } from "react";
2453
+ import { useRef as useRef5, useState as useState3 } from "react";
2464
2454
  function useAttachmentTarget({
2465
2455
  onFiles,
2466
2456
  disabled = false
2467
2457
  }) {
2468
- const [isDropTarget, setIsDropTarget] = useState4(false);
2458
+ const [isDropTarget, setIsDropTarget] = useState3(false);
2469
2459
  const depth = useRef5(0);
2470
2460
  function handleDragEnter(event) {
2471
2461
  if (disabled || !event.dataTransfer.types.includes("Files")) return;
@@ -2516,7 +2506,7 @@ var widgetButtonVariants = cva(
2516
2506
  variant: {
2517
2507
  default: "bg-primary text-primary-foreground hover:bg-primary/80",
2518
2508
  outline: "border-border bg-background hover:bg-muted hover:text-foreground aria-expanded:bg-muted aria-expanded:text-foreground dark:border-input dark:bg-input/30 dark:hover:bg-input/50",
2519
- secondary: "bg-secondary text-secondary-foreground hover:bg-[color-mix(in_oklch,var(--secondary),var(--foreground)_5%)] aria-expanded:bg-secondary aria-expanded:text-secondary-foreground",
2509
+ secondary: "bg-secondary text-secondary-foreground hover:bg-[color-mix(in_oklch,var(--secondary,oklch(0.97_0_0)),var(--foreground,oklch(0.145_0_0))_5%)] aria-expanded:bg-secondary aria-expanded:text-secondary-foreground",
2520
2510
  ghost: "hover:bg-muted hover:text-foreground aria-expanded:bg-muted aria-expanded:text-foreground dark:hover:bg-muted/50",
2521
2511
  destructive: "bg-destructive/10 text-destructive hover:bg-destructive/20 focus-visible:border-destructive/40 focus-visible:ring-destructive/20 dark:bg-destructive/20 dark:hover:bg-destructive/30 dark:focus-visible:ring-destructive/40",
2522
2512
  link: "text-primary underline-offset-4 hover:underline"
@@ -2561,12 +2551,10 @@ function WidgetButton({
2561
2551
  );
2562
2552
  }
2563
2553
 
2564
- // ../../apps/web/src/components/ui/text-field.ts
2565
- var TEXT_FIELD_SHELL = [
2554
+ // ../../apps/web/src/components/support-widget/widget-field.ts
2555
+ var WIDGET_TEXT_FIELD_SHELL = [
2566
2556
  "rounded-lg border border-input bg-transparent text-sm max-sm:text-base transition-[color,box-shadow] outline-none dark:bg-input/30",
2567
2557
  "placeholder:text-muted-foreground",
2568
- // Focus recolours the hairline rather than adding a ring on top of it, so
2569
- // the outline stays exactly 1px and nothing swells on click.
2570
2558
  "focus-within:border-ring",
2571
2559
  "aria-invalid:border-destructive",
2572
2560
  "disabled:pointer-events-none disabled:opacity-50"
@@ -2591,12 +2579,12 @@ function Textarea({ className, ...props }) {
2591
2579
  }
2592
2580
 
2593
2581
  // ../../apps/web/src/hooks/use-typing-signal.ts
2594
- import { useCallback as useCallback5, useEffect as useEffect5, useRef as useRef6 } from "react";
2582
+ import { useCallback as useCallback5, useEffect as useEffect4, useRef as useRef6 } from "react";
2595
2583
  var REFRESH_MS = 2500;
2596
2584
  var IDLE_MS = 8e3;
2597
2585
  function useTypingSignal(onChange) {
2598
2586
  const onChangeRef = useRef6(onChange);
2599
- useEffect5(() => {
2587
+ useEffect4(() => {
2600
2588
  onChangeRef.current = onChange;
2601
2589
  }, [onChange]);
2602
2590
  const isTypingRef = useRef6(false);
@@ -2630,10 +2618,33 @@ function useTypingSignal(onChange) {
2630
2618
  send(true);
2631
2619
  }
2632
2620
  }, [send, stop]);
2633
- useEffect5(() => () => stop(), [stop]);
2621
+ useEffect4(() => () => stop(), [stop]);
2634
2622
  return { ping, stop, settle };
2635
2623
  }
2636
2624
 
2625
+ // ../../apps/web/src/components/support-widget/use-eased-height.ts
2626
+ import { useLayoutEffect as useLayoutEffect4, useRef as useRef7 } from "react";
2627
+ function useEasedHeight(field) {
2628
+ const box = useRef7(null);
2629
+ useLayoutEffect4(() => {
2630
+ const outer = box.current;
2631
+ const inner = field.current;
2632
+ if (!outer || !inner) return;
2633
+ const follow = () => {
2634
+ outer.style.height = `${inner.offsetHeight}px`;
2635
+ };
2636
+ follow();
2637
+ if (typeof ResizeObserver === "undefined") return;
2638
+ const observer = new ResizeObserver(follow);
2639
+ observer.observe(inner);
2640
+ return () => {
2641
+ observer.disconnect();
2642
+ outer.style.height = "";
2643
+ };
2644
+ }, [field]);
2645
+ return box;
2646
+ }
2647
+
2637
2648
  // ../../apps/web/src/components/brand-art.ts
2638
2649
  var BRAND_INK = "#191919";
2639
2650
  var LOGO = {
@@ -2661,7 +2672,7 @@ var LOGO_MASK_URL = `url("data:image/svg+xml,${encodeURIComponent(
2661
2672
  )}")`;
2662
2673
 
2663
2674
  // ../../apps/web/src/components/brand.tsx
2664
- import { jsx as jsx11 } from "react/jsx-runtime";
2675
+ import { jsx as jsx11, jsxs as jsxs4 } from "react/jsx-runtime";
2665
2676
  var INVERT_IN_DARK = "dark:invert";
2666
2677
  function Wordmark({
2667
2678
  width = 132,
@@ -2714,7 +2725,11 @@ function ShimmerLogo({
2714
2725
  WebkitMaskSize: "contain",
2715
2726
  WebkitMaskRepeat: "no-repeat",
2716
2727
  WebkitMaskPosition: "center",
2717
- backgroundImage: "linear-gradient(115deg, var(--muted) 34%, var(--muted-foreground) 50%, var(--muted) 66%)",
2728
+ backgroundImage: (
2729
+ // With the widget's own light values behind the host's, since this
2730
+ // mark also draws on pages that define neither.
2731
+ "linear-gradient(115deg, var(--muted, oklch(0.955 0 0)) 34%, var(--muted-foreground, oklch(0.556 0 0)) 50%, var(--muted, oklch(0.955 0 0)) 66%)"
2732
+ ),
2718
2733
  backgroundSize: "300% 100%"
2719
2734
  },
2720
2735
  className: cn(
@@ -2726,10 +2741,10 @@ function ShimmerLogo({
2726
2741
  }
2727
2742
 
2728
2743
  // ../../apps/web/src/components/support-widget/widget-badge.tsx
2729
- import { jsx as jsx12, jsxs as jsxs4 } from "react/jsx-runtime";
2744
+ import { jsx as jsx12, jsxs as jsxs5 } from "react/jsx-runtime";
2730
2745
  var MEDIAN_URL = "https://median.sh";
2731
2746
  function PoweredByMedian({ className }) {
2732
- return /* @__PURE__ */ jsxs4(
2747
+ return /* @__PURE__ */ jsxs5(
2733
2748
  "a",
2734
2749
  {
2735
2750
  href: MEDIAN_URL,
@@ -2754,12 +2769,13 @@ function PoweredByMedian({ className }) {
2754
2769
  }
2755
2770
 
2756
2771
  // ../../apps/web/src/components/support-widget/widget-composer.tsx
2757
- import { Fragment as Fragment26, jsx as jsx13, jsxs as jsxs5 } from "react/jsx-runtime";
2772
+ import { Fragment as Fragment26, jsx as jsx13, jsxs as jsxs6 } from "react/jsx-runtime";
2758
2773
  var EMAIL_PATTERN = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
2759
2774
  var SWAP = "transition-[opacity,translate] duration-300 ease-out motion-reduce:transition-none";
2760
2775
  var RISE = "transition-transform duration-450 ease-spring motion-reduce:transition-none";
2761
2776
  var SINK = "transition-transform duration-250 ease-in motion-reduce:transition-none";
2762
2777
  var ROLL = "transition-transform duration-350 ease-out-quint motion-reduce:transition-none";
2778
+ var GROW = "transition-[height] duration-200 ease-out-quint motion-reduce:transition-none";
2763
2779
  var SAVED_MS = 1200;
2764
2780
  function WidgetComposer({
2765
2781
  onSend,
@@ -2773,11 +2789,12 @@ function WidgetComposer({
2773
2789
  compact = false
2774
2790
  }) {
2775
2791
  const isStacked = variant === "stacked";
2776
- const [draft, setDraft] = useState5("");
2777
- const [email, setEmail] = useState5("");
2778
- const [answer, setAnswer] = useState5("none");
2779
- const fileInputRef = useRef7(null);
2780
- const draftRef = useRef7(null);
2792
+ const [draft, setDraft] = useState4("");
2793
+ const [email, setEmail] = useState4("");
2794
+ const [answer, setAnswer] = useState4("none");
2795
+ const fileInputRef = useRef8(null);
2796
+ const draftRef = useRef8(null);
2797
+ const draftBoxRef = useEasedHeight(draftRef);
2781
2798
  const attachments = useAttachmentDrafts();
2782
2799
  const target = useAttachmentTarget({ onFiles: attachments.add });
2783
2800
  const typing = useTypingSignal(onTypingChange);
@@ -2793,12 +2810,12 @@ function WidgetComposer({
2793
2810
  setAnswer("saved");
2794
2811
  onSubmitEmail(email.trim());
2795
2812
  }
2796
- useEffect6(() => {
2813
+ useEffect5(() => {
2797
2814
  if (answer !== "saved") return;
2798
2815
  const timer = setTimeout(() => setAnswer("settled"), SAVED_MS);
2799
2816
  return () => clearTimeout(timer);
2800
2817
  }, [answer]);
2801
- useEffect6(() => {
2818
+ useEffect5(() => {
2802
2819
  if (answer !== "saved" || askForEmail) return;
2803
2820
  draftRef.current?.focus({ preventScroll: true });
2804
2821
  }, [answer, askForEmail]);
@@ -2848,24 +2865,42 @@ function WidgetComposer({
2848
2865
  children: /* @__PURE__ */ jsx13(Plus, { className: isStacked ? "size-5" : void 0 })
2849
2866
  }
2850
2867
  );
2851
- const draftBox = /* @__PURE__ */ jsx13(
2852
- Textarea,
2853
- {
2854
- ref: draftRef,
2855
- rows: 1,
2856
- value: draft,
2857
- onChange: (event) => {
2858
- setDraft(event.target.value);
2859
- typing.ping();
2860
- },
2861
- onKeyDown: handleKeyDown,
2862
- placeholder: target.isDropTarget ? "Drop to attach" : placeholder,
2863
- "aria-label": "Message",
2864
- className: cn(
2865
- "max-h-40 flex-1 max-sm:text-[16px]",
2866
- isStacked ? "min-h-9 px-2 pt-1.5 pb-1 text-sm" : "min-h-10 px-2 py-2 text-base"
2867
- )
2868
- }
2868
+ const draftBox = (
2869
+ // The field sizes itself to its words and sits at the foot of the box,
2870
+ // so a line that wraps stays where it was typed and the box's top edge
2871
+ // rises to take the first line in: the composer grows upward, and the
2872
+ // words hold still under the caret. The box follows the field on a
2873
+ // curve and clips whatever it is behind by.
2874
+ /* @__PURE__ */ jsx13(
2875
+ "div",
2876
+ {
2877
+ ref: draftBoxRef,
2878
+ className: cn(
2879
+ "flex flex-col justify-end overflow-clip",
2880
+ isStacked ? "w-full" : "min-w-0 flex-1",
2881
+ draft.length > 0 && GROW
2882
+ ),
2883
+ children: /* @__PURE__ */ jsx13(
2884
+ Textarea,
2885
+ {
2886
+ ref: draftRef,
2887
+ rows: 1,
2888
+ value: draft,
2889
+ onChange: (event) => {
2890
+ setDraft(event.target.value);
2891
+ typing.ping();
2892
+ },
2893
+ onKeyDown: handleKeyDown,
2894
+ placeholder: target.isDropTarget ? "Drop to attach" : placeholder,
2895
+ "aria-label": "Message",
2896
+ className: cn(
2897
+ "max-h-40 shrink-0 max-sm:text-[16px]",
2898
+ isStacked ? "min-h-9 px-2 pt-1.5 pb-1 text-sm" : "min-h-10 px-2 py-2 text-base"
2899
+ )
2900
+ }
2901
+ )
2902
+ }
2903
+ )
2869
2904
  );
2870
2905
  const sendButton = /* @__PURE__ */ jsx13(
2871
2906
  WidgetButton,
@@ -2925,7 +2960,7 @@ function WidgetComposer({
2925
2960
  // anyway. Paste rides on the same element, so it is caught wherever the
2926
2961
  // caret happens to be inside the composer. None of that while the email
2927
2962
  // is asked for: there is no message yet to attach anything to.
2928
- /* @__PURE__ */ jsxs5(
2963
+ /* @__PURE__ */ jsxs6(
2929
2964
  "div",
2930
2965
  {
2931
2966
  className: cn(
@@ -2941,7 +2976,7 @@ function WidgetComposer({
2941
2976
  onRemove: () => attachments.remove(item.id)
2942
2977
  }
2943
2978
  ) }, item.id)) }),
2944
- /* @__PURE__ */ jsxs5("div", { className: "relative", children: [
2979
+ /* @__PURE__ */ jsxs6("div", { className: "relative", children: [
2945
2980
  /* @__PURE__ */ jsx13(
2946
2981
  "div",
2947
2982
  {
@@ -2958,8 +2993,8 @@ function WidgetComposer({
2958
2993
  showTab ? RISE : SINK,
2959
2994
  !showTab && "translate-y-full"
2960
2995
  ),
2961
- children: /* @__PURE__ */ jsxs5("span", { className: "grid overflow-hidden", children: [
2962
- /* @__PURE__ */ jsxs5(
2996
+ children: /* @__PURE__ */ jsxs6("span", { className: "grid overflow-hidden", children: [
2997
+ /* @__PURE__ */ jsxs6(
2963
2998
  "span",
2964
2999
  {
2965
3000
  "aria-hidden": isSaved,
@@ -2974,7 +3009,7 @@ function WidgetComposer({
2974
3009
  ]
2975
3010
  }
2976
3011
  ),
2977
- /* @__PURE__ */ jsxs5(
3012
+ /* @__PURE__ */ jsxs6(
2978
3013
  "span",
2979
3014
  {
2980
3015
  "aria-hidden": !isSaved,
@@ -2994,11 +3029,11 @@ function WidgetComposer({
2994
3029
  )
2995
3030
  }
2996
3031
  ),
2997
- /* @__PURE__ */ jsxs5(
3032
+ /* @__PURE__ */ jsxs6(
2998
3033
  "div",
2999
3034
  {
3000
3035
  className: cn(
3001
- TEXT_FIELD_SHELL,
3036
+ WIDGET_TEXT_FIELD_SHELL,
3002
3037
  "relative z-10 grid bg-card p-2 transition-colors dark:bg-card",
3003
3038
  // Half the pill's one line height, so it is a full pill at one
3004
3039
  // line and keeps those corners as it grows. The box keeps a
@@ -3020,17 +3055,17 @@ function WidgetComposer({
3020
3055
  SWAP,
3021
3056
  isAsking && "-translate-y-1 opacity-0 pointer-events-none"
3022
3057
  ),
3023
- children: isStacked ? /* @__PURE__ */ jsxs5(Fragment26, { children: [
3058
+ children: isStacked ? /* @__PURE__ */ jsxs6(Fragment26, { children: [
3024
3059
  draftBox,
3025
- /* @__PURE__ */ jsxs5("div", { className: "flex items-center gap-1.5", children: [
3060
+ /* @__PURE__ */ jsxs6("div", { className: "flex items-center gap-1.5", children: [
3026
3061
  fileInput,
3027
3062
  addButton,
3028
- /* @__PURE__ */ jsxs5("div", { className: "ml-auto flex items-center gap-2", children: [
3063
+ /* @__PURE__ */ jsxs6("div", { className: "ml-auto flex items-center gap-2", children: [
3029
3064
  badge,
3030
3065
  sendButton
3031
3066
  ] })
3032
3067
  ] })
3033
- ] }) : /* @__PURE__ */ jsxs5(Fragment26, { children: [
3068
+ ] }) : /* @__PURE__ */ jsxs6(Fragment26, { children: [
3034
3069
  fileInput,
3035
3070
  addButton,
3036
3071
  draftBox,
@@ -3049,13 +3084,13 @@ function WidgetComposer({
3049
3084
  SWAP,
3050
3085
  !isAsking && "pointer-events-none translate-y-1 opacity-0"
3051
3086
  ),
3052
- children: isStacked ? /* @__PURE__ */ jsxs5(Fragment26, { children: [
3087
+ children: isStacked ? /* @__PURE__ */ jsxs6(Fragment26, { children: [
3053
3088
  emailBox,
3054
- /* @__PURE__ */ jsxs5("div", { className: "flex items-center justify-end gap-2", children: [
3089
+ /* @__PURE__ */ jsxs6("div", { className: "flex items-center justify-end gap-2", children: [
3055
3090
  badge,
3056
3091
  emailButton
3057
3092
  ] })
3058
- ] }) : /* @__PURE__ */ jsxs5(Fragment26, { children: [
3093
+ ] }) : /* @__PURE__ */ jsxs6(Fragment26, { children: [
3059
3094
  emailBox,
3060
3095
  emailButton
3061
3096
  ] })
@@ -3072,13 +3107,17 @@ function WidgetComposer({
3072
3107
  );
3073
3108
  }
3074
3109
 
3110
+ // ../../apps/web/src/components/support-widget/widget-corner.ts
3111
+ var WIDGET_RADIUS = "0.875rem";
3112
+ var WIDGET_CORNER = "rounded-[0.875rem]";
3113
+
3075
3114
  // ../../apps/web/src/components/support-widget/widget-destination.tsx
3076
- import { jsx as jsx14, jsxs as jsxs6 } from "react/jsx-runtime";
3115
+ import { jsx as jsx14, jsxs as jsxs7 } from "react/jsx-runtime";
3077
3116
  function WidgetDestination({
3078
3117
  destination,
3079
3118
  onFollow
3080
3119
  }) {
3081
- return /* @__PURE__ */ jsxs6(
3120
+ return /* @__PURE__ */ jsxs7(
3082
3121
  WidgetButton,
3083
3122
  {
3084
3123
  type: "button",
@@ -3100,10 +3139,24 @@ function WidgetDestination({
3100
3139
  }
3101
3140
 
3102
3141
  // ../../apps/web/src/components/avatar.tsx
3103
- import { useState as useState6 } from "react";
3142
+ import { useState as useState5 } from "react";
3104
3143
 
3105
3144
  // ../../apps/web/src/components/initial-badge.tsx
3106
3145
  import { jsx as jsx15 } from "react/jsx-runtime";
3146
+ var TONES = [
3147
+ "bg-[var(--avatar-blue,#e0edfa)] text-[var(--avatar-blue-foreground,#37628c)]",
3148
+ "bg-[var(--avatar-violet,#eae4f7)] text-[var(--avatar-violet-foreground,#705894)]",
3149
+ "bg-[var(--avatar-amber,#f8ead6)] text-[var(--avatar-amber-foreground,#8b6434)]",
3150
+ "bg-[var(--avatar-teal,#dbefea)] text-[var(--avatar-teal-foreground,#3c766d)]",
3151
+ "bg-[var(--avatar-rose,#f5e2e8)] text-[var(--avatar-rose-foreground,#92586c)]"
3152
+ ];
3153
+ function toneFor(value) {
3154
+ let hash = 0;
3155
+ for (const character of value.trim().toLowerCase()) {
3156
+ hash = hash * 31 + character.charCodeAt(0) >>> 0;
3157
+ }
3158
+ return TONES[hash % TONES.length];
3159
+ }
3107
3160
  function InitialBadge({
3108
3161
  value,
3109
3162
  className
@@ -3114,7 +3167,8 @@ function InitialBadge({
3114
3167
  "aria-hidden": true,
3115
3168
  className: cn(
3116
3169
  // Sized to match UserAvatar, since the two sit side by side in the nav.
3117
- "grid size-4.5 shrink-0 place-items-center rounded border text-[10px] font-semibold text-muted-foreground",
3170
+ "surface-control grid size-4.5 shrink-0 place-items-center rounded border border-foreground/5 text-[10px] font-medium",
3171
+ toneFor(value),
3118
3172
  className
3119
3173
  ),
3120
3174
  children: value.trim().charAt(0).toUpperCase() || "?"
@@ -3131,7 +3185,7 @@ function Avatar({
3131
3185
  unoptimized = false,
3132
3186
  className
3133
3187
  }) {
3134
- const [failedSrc, setFailedSrc] = useState6(null);
3188
+ const [failedSrc, setFailedSrc] = useState5(null);
3135
3189
  const hasFailed = failedSrc !== null && failedSrc === src;
3136
3190
  const shape2 = "size-4.5 shrink-0 rounded-full";
3137
3191
  if (!src || hasFailed) {
@@ -3139,7 +3193,7 @@ function Avatar({
3139
3193
  InitialBadge,
3140
3194
  {
3141
3195
  value: name,
3142
- className: cn(shape2, "border-none bg-muted text-[10px]", className)
3196
+ className: cn(shape2, "border-none text-[10px]", className)
3143
3197
  }
3144
3198
  );
3145
3199
  }
@@ -3153,7 +3207,7 @@ function Avatar({
3153
3207
  unoptimized,
3154
3208
  onError: () => setFailedSrc(src),
3155
3209
  draggable: false,
3156
- className: cn(shape2, "bg-muted object-cover", className)
3210
+ className: cn(shape2, "bg-muted object-cover ring-1 ring-foreground/5", className)
3157
3211
  }
3158
3212
  );
3159
3213
  }
@@ -3192,7 +3246,7 @@ function WidgetAvatar({
3192
3246
  {
3193
3247
  src: agent.avatarUrl,
3194
3248
  name: agent.name,
3195
- className: "size-full rounded-[inherit]"
3249
+ className: "size-full rounded-[inherit] bg-muted bg-none font-semibold text-muted-foreground shadow-none ring-0"
3196
3250
  }
3197
3251
  )
3198
3252
  }
@@ -3215,7 +3269,7 @@ function WidgetAvatarStack({
3215
3269
  }
3216
3270
 
3217
3271
  // ../../apps/web/src/components/support-widget/widget-empty.tsx
3218
- import { jsx as jsx18, jsxs as jsxs7 } from "react/jsx-runtime";
3272
+ import { jsx as jsx18, jsxs as jsxs8 } from "react/jsx-runtime";
3219
3273
  var STARTERS = [
3220
3274
  {
3221
3275
  label: "Ask a question",
@@ -3242,7 +3296,7 @@ function WidgetEmpty({
3242
3296
  compact = false
3243
3297
  }) {
3244
3298
  const agent = team.find((member) => member.kind === "ai") ?? team[0];
3245
- return /* @__PURE__ */ jsxs7("div", { className: "flex min-h-0 flex-1 flex-col items-center justify-center gap-5 px-6 pb-2 text-center", children: [
3299
+ return /* @__PURE__ */ jsxs8("div", { className: "flex min-h-0 flex-1 flex-col items-center justify-center gap-5 px-6 pb-2 text-center", children: [
3246
3300
  agent && /* @__PURE__ */ jsx18(
3247
3301
  WidgetAvatar,
3248
3302
  {
@@ -3253,7 +3307,7 @@ function WidgetEmpty({
3253
3307
  )
3254
3308
  }
3255
3309
  ),
3256
- /* @__PURE__ */ jsxs7("div", { className: "flex max-w-72 animate-in flex-col gap-1 delay-150 duration-500 ease-out fade-in-0 slide-in-from-bottom-2 fill-mode-backwards motion-reduce:animate-none", children: [
3310
+ /* @__PURE__ */ jsxs8("div", { className: "flex max-w-72 animate-in flex-col gap-1 delay-150 duration-500 ease-out fade-in-0 slide-in-from-bottom-2 fill-mode-backwards motion-reduce:animate-none", children: [
3257
3311
  agent && /* @__PURE__ */ jsx18(
3258
3312
  "p",
3259
3313
  {
@@ -3275,7 +3329,7 @@ function WidgetEmpty({
3275
3329
  }
3276
3330
  )
3277
3331
  ] }),
3278
- onPick && /* @__PURE__ */ jsx18("div", { className: "flex w-full max-w-md flex-col gap-2 pt-1", children: STARTERS.map((starter, index) => /* @__PURE__ */ jsxs7(
3332
+ onPick && /* @__PURE__ */ jsx18("div", { className: "flex w-full max-w-md flex-col gap-2 pt-1", children: STARTERS.map((starter, index) => /* @__PURE__ */ jsxs8(
3279
3333
  WidgetButton,
3280
3334
  {
3281
3335
  type: "button",
@@ -3316,7 +3370,7 @@ function WidgetEmpty({
3316
3370
  }
3317
3371
 
3318
3372
  // ../../apps/web/src/components/support-widget/widget-header.tsx
3319
- import { jsx as jsx19, jsxs as jsxs8 } from "react/jsx-runtime";
3373
+ import { jsx as jsx19, jsxs as jsxs9 } from "react/jsx-runtime";
3320
3374
  function WidgetHeader({
3321
3375
  presence,
3322
3376
  onClose,
@@ -3327,7 +3381,7 @@ function WidgetHeader({
3327
3381
  compact = false,
3328
3382
  className
3329
3383
  }) {
3330
- return /* @__PURE__ */ jsxs8(
3384
+ return /* @__PURE__ */ jsxs9(
3331
3385
  "header",
3332
3386
  {
3333
3387
  ...grabHandlers,
@@ -3344,7 +3398,7 @@ function WidgetHeader({
3344
3398
  ]
3345
3399
  ),
3346
3400
  children: [
3347
- /* @__PURE__ */ jsxs8("div", { className: "flex min-w-0 flex-1 flex-col pt-0.5", children: [
3401
+ /* @__PURE__ */ jsxs9("div", { className: "flex min-w-0 flex-1 flex-col pt-0.5", children: [
3348
3402
  /* @__PURE__ */ jsx19(
3349
3403
  "h2",
3350
3404
  {
@@ -3366,7 +3420,7 @@ function WidgetHeader({
3366
3420
  }
3367
3421
  )
3368
3422
  ] }),
3369
- /* @__PURE__ */ jsxs8("div", { className: "flex shrink-0 items-center gap-3", children: [
3423
+ /* @__PURE__ */ jsxs9("div", { className: "flex shrink-0 items-center gap-3", children: [
3370
3424
  /* @__PURE__ */ jsx19(
3371
3425
  WidgetAvatarStack,
3372
3426
  {
@@ -3374,7 +3428,7 @@ function WidgetHeader({
3374
3428
  avatarClassName: compact ? "size-8" : void 0
3375
3429
  }
3376
3430
  ),
3377
- /* @__PURE__ */ jsxs8("div", { className: "flex items-center gap-0.5", children: [
3431
+ /* @__PURE__ */ jsxs9("div", { className: "flex items-center gap-0.5", children: [
3378
3432
  onToggleExpanded && /* @__PURE__ */ jsx19(
3379
3433
  WidgetButton,
3380
3434
  {
@@ -3412,16 +3466,16 @@ function WidgetLoading() {
3412
3466
  }
3413
3467
 
3414
3468
  // ../../apps/web/src/components/support-widget/widget-locked.tsx
3415
- import { jsx as jsx21, jsxs as jsxs9 } from "react/jsx-runtime";
3469
+ import { jsx as jsx21, jsxs as jsxs10 } from "react/jsx-runtime";
3416
3470
  function WidgetLocked({
3417
3471
  orgName,
3418
3472
  onSignIn,
3419
3473
  showBadge = true
3420
3474
  }) {
3421
- return /* @__PURE__ */ jsxs9("div", { className: "flex min-h-0 flex-1 flex-col", children: [
3422
- /* @__PURE__ */ jsxs9("div", { className: "flex min-h-0 flex-1 flex-col items-center justify-center gap-5 px-8 text-center", children: [
3475
+ return /* @__PURE__ */ jsxs10("div", { className: "flex min-h-0 flex-1 flex-col", children: [
3476
+ /* @__PURE__ */ jsxs10("div", { className: "flex min-h-0 flex-1 flex-col items-center justify-center gap-5 px-8 text-center", children: [
3423
3477
  /* @__PURE__ */ jsx21("span", { className: "grid size-16 animate-in place-items-center rounded-2xl bg-muted duration-500 ease-out fade-in-0 zoom-in-95 motion-reduce:animate-none", children: /* @__PURE__ */ jsx21(LockSimple, { className: "size-7 text-muted-foreground" }) }),
3424
- /* @__PURE__ */ jsxs9("div", { className: "flex max-w-72 animate-in flex-col gap-1 delay-150 duration-500 ease-out fade-in-0 slide-in-from-bottom-2 fill-mode-backwards motion-reduce:animate-none", children: [
3478
+ /* @__PURE__ */ jsxs10("div", { className: "flex max-w-72 animate-in flex-col gap-1 delay-150 duration-500 ease-out fade-in-0 slide-in-from-bottom-2 fill-mode-backwards motion-reduce:animate-none", children: [
3425
3479
  /* @__PURE__ */ jsx21("p", { className: "text-xl font-semibold tracking-tight", children: "Sign in to chat" }),
3426
3480
  /* @__PURE__ */ jsx21("p", { className: "text-base text-balance text-muted-foreground", children: orgName ? `Ask ${orgName} anything once you are signed in.` : "Sign in to contact our support team." })
3427
3481
  ] }),
@@ -3440,7 +3494,7 @@ function WidgetLocked({
3440
3494
  }
3441
3495
 
3442
3496
  // ../../apps/web/src/components/support-widget/widget-message.tsx
3443
- import { useState as useState9 } from "react";
3497
+ import { useState as useState8 } from "react";
3444
3498
 
3445
3499
  // ../../apps/web/src/components/ui/badge.tsx
3446
3500
  import { jsx as jsx22 } from "react/jsx-runtime";
@@ -3449,10 +3503,10 @@ var badgeVariants = cva(
3449
3503
  {
3450
3504
  variants: {
3451
3505
  variant: {
3452
- default: "bg-secondary text-secondary-foreground",
3453
- muted: "bg-muted text-muted-foreground",
3506
+ default: "bg-secondary text-secondary-foreground inset-ring-1 inset-ring-foreground/5",
3507
+ muted: "bg-muted/70 text-muted-foreground inset-ring-1 inset-ring-foreground/5",
3454
3508
  outline: "border border-border text-muted-foreground",
3455
- solid: "bg-primary text-primary-foreground"
3509
+ solid: "surface-primary bg-primary text-primary-foreground"
3456
3510
  }
3457
3511
  },
3458
3512
  defaultVariants: {
@@ -3479,7 +3533,7 @@ function Badge({
3479
3533
  }
3480
3534
 
3481
3535
  // ../../apps/web/src/components/ui/morph-bubble.tsx
3482
- import { useLayoutEffect as useLayoutEffect4, useRef as useRef8 } from "react";
3536
+ import { useLayoutEffect as useLayoutEffect5, useRef as useRef9 } from "react";
3483
3537
  import { jsx as jsx23 } from "react/jsx-runtime";
3484
3538
  var MORPH_MS = 350;
3485
3539
  var MORPH_EASING = "ease-in-out";
@@ -3507,11 +3561,11 @@ function MorphBubble({
3507
3561
  contentClassName,
3508
3562
  children
3509
3563
  }) {
3510
- const box = useRef8(null);
3511
- const content = useRef8(null);
3512
- const previous = useRef8(null);
3513
- const running = useRef8([]);
3514
- useLayoutEffect4(() => {
3564
+ const box = useRef9(null);
3565
+ const content = useRef9(null);
3566
+ const previous = useRef9(null);
3567
+ const running = useRef9([]);
3568
+ useLayoutEffect5(() => {
3515
3569
  const outer = box.current;
3516
3570
  const inner = content.current;
3517
3571
  if (!outer || !inner) return;
@@ -3564,8 +3618,8 @@ function MorphBubble({
3564
3618
  }
3565
3619
 
3566
3620
  // ../../apps/web/src/components/support-widget/widget-attachments.tsx
3567
- import { useState as useState7 } from "react";
3568
- import { Fragment as Fragment27, jsx as jsx24, jsxs as jsxs10 } from "react/jsx-runtime";
3621
+ import { useState as useState6 } from "react";
3622
+ import { Fragment as Fragment27, jsx as jsx24, jsxs as jsxs11 } from "react/jsx-runtime";
3569
3623
  var SURFACE = "rounded-xl border border-foreground/10 bg-card/70";
3570
3624
  function OpenAttachment({
3571
3625
  attachment,
@@ -3593,9 +3647,9 @@ function MediaTile({
3593
3647
  attachment,
3594
3648
  className
3595
3649
  }) {
3596
- const [hasFailed, setHasFailed] = useState7(false);
3650
+ const [hasFailed, setHasFailed] = useState6(false);
3597
3651
  if (!attachment.previewUrl || hasFailed) {
3598
- return /* @__PURE__ */ jsxs10(
3652
+ return /* @__PURE__ */ jsxs11(
3599
3653
  "div",
3600
3654
  {
3601
3655
  className: cn(
@@ -3610,7 +3664,7 @@ function MediaTile({
3610
3664
  }
3611
3665
  );
3612
3666
  }
3613
- return /* @__PURE__ */ jsxs10("figure", { className: cn(SURFACE, "relative overflow-hidden", className), children: [
3667
+ return /* @__PURE__ */ jsxs11("figure", { className: cn(SURFACE, "relative overflow-hidden", className), children: [
3614
3668
  /* @__PURE__ */ jsx24(
3615
3669
  "img",
3616
3670
  {
@@ -3634,9 +3688,9 @@ function MediaTile({
3634
3688
  ] });
3635
3689
  }
3636
3690
  function FileRow({ attachment }) {
3637
- return /* @__PURE__ */ jsx24(OpenAttachment, { attachment, children: /* @__PURE__ */ jsxs10("div", { className: cn(SURFACE, "flex items-center gap-2.5 px-2.5 py-2"), children: [
3638
- /* @__PURE__ */ jsx24(File, { className: "size-5 shrink-0 text-muted-foreground" }),
3639
- /* @__PURE__ */ jsxs10("div", { className: "flex min-w-0 flex-1 flex-col text-left", children: [
3691
+ return /* @__PURE__ */ jsx24(OpenAttachment, { attachment, children: /* @__PURE__ */ jsxs11("div", { className: cn(SURFACE, "flex items-center gap-2.5 px-2.5 py-2"), children: [
3692
+ /* @__PURE__ */ jsx24(File2, { className: "size-5 shrink-0 text-muted-foreground" }),
3693
+ /* @__PURE__ */ jsxs11("div", { className: "flex min-w-0 flex-1 flex-col text-left", children: [
3640
3694
  /* @__PURE__ */ jsx24("p", { className: "truncate text-sm font-medium", children: attachment.name }),
3641
3695
  /* @__PURE__ */ jsx24("p", { className: "truncate text-sm text-muted-foreground", children: attachment.size })
3642
3696
  ] })
@@ -3647,7 +3701,7 @@ function WidgetAttachments({
3647
3701
  }) {
3648
3702
  const media = attachments.filter((one) => one.kind !== "file");
3649
3703
  const files = attachments.filter((one) => one.kind === "file");
3650
- return /* @__PURE__ */ jsxs10("div", { className: "flex w-64 max-w-full flex-col gap-2", children: [
3704
+ return /* @__PURE__ */ jsxs11("div", { className: "flex w-64 max-w-full flex-col gap-2", children: [
3651
3705
  media.length > 0 && /* @__PURE__ */ jsx24("div", { className: cn("grid gap-2", media.length > 1 && "grid-cols-2"), children: media.map((attachment) => /* @__PURE__ */ jsx24(
3652
3706
  OpenAttachment,
3653
3707
  {
@@ -3685,7 +3739,7 @@ function WidgetBubble({
3685
3739
  import { Suspense } from "react";
3686
3740
 
3687
3741
  // ../../apps/web/src/components/ui/line-reveal.tsx
3688
- import { useLayoutEffect as useLayoutEffect5, useRef as useRef9, useState as useState8 } from "react";
3742
+ import { useLayoutEffect as useLayoutEffect6, useRef as useRef10, useState as useState7 } from "react";
3689
3743
 
3690
3744
  // ../../apps/web/src/hooks/use-media-query.ts
3691
3745
  import { useSyncExternalStore as useSyncExternalStore3 } from "react";
@@ -3712,7 +3766,7 @@ function durationMs(token, fallback, from = document.documentElement) {
3712
3766
  }
3713
3767
 
3714
3768
  // ../../apps/web/src/components/ui/line-reveal.tsx
3715
- import { jsx as jsx26, jsxs as jsxs11 } from "react/jsx-runtime";
3769
+ import { jsx as jsx26, jsxs as jsxs12 } from "react/jsx-runtime";
3716
3770
  var LAST_BEAT = 11;
3717
3771
  function lineSlices(root) {
3718
3772
  const host = root.getBoundingClientRect();
@@ -3748,7 +3802,7 @@ function lineSlices(root) {
3748
3802
  return [{ top: 0, height: host.height }];
3749
3803
  }
3750
3804
  const unique = [...new Set(tops.map((top) => Math.round(top)))].sort(
3751
- (a28, b2) => a28 - b2
3805
+ (a30, b2) => a30 - b2
3752
3806
  );
3753
3807
  return unique.map((top, index) => {
3754
3808
  const next = unique[index + 1] ?? Math.ceil(host.height);
@@ -3798,10 +3852,10 @@ function LineReveal({
3798
3852
  children,
3799
3853
  className
3800
3854
  }) {
3801
- const sourceRef = useRef9(null);
3802
- const stageRef = useRef9(null);
3855
+ const sourceRef = useRef10(null);
3856
+ const stageRef = useRef10(null);
3803
3857
  const reduce = useMediaQuery("(prefers-reduced-motion: reduce)");
3804
- const [play2, setPlay] = useState8({
3858
+ const [play2, setPlay] = useState7({
3805
3859
  key: contentKey,
3806
3860
  delay,
3807
3861
  isPlaying: !reduce
@@ -3810,19 +3864,20 @@ function LineReveal({
3810
3864
  setPlay({ key: contentKey, delay, isPlaying: !reduce });
3811
3865
  }
3812
3866
  const { key: playKey, delay: hold, isPlaying } = play2;
3813
- useLayoutEffect5(() => {
3867
+ useLayoutEffect6(() => {
3814
3868
  if (reduce || !isPlaying) return;
3815
3869
  const source = sourceRef.current;
3816
3870
  const stage = stageRef.current;
3817
3871
  if (source === null || stage === null) return;
3872
+ const wait = typeof hold === "number" ? hold : durationMs(hold, 0, stage);
3818
3873
  let painted = false;
3819
3874
  let timer = 0;
3820
3875
  const end = () => setPlay((now) => ({ ...now, isPlaying: false }));
3821
3876
  const paint = () => {
3822
3877
  if (painted) return;
3823
- if (!paintSlices(source, stage, hold)) return;
3878
+ if (!paintSlices(source, stage, wait)) return;
3824
3879
  painted = true;
3825
- timer = window.setTimeout(end, playMs(stage, hold));
3880
+ timer = window.setTimeout(end, playMs(stage, wait));
3826
3881
  observer.disconnect();
3827
3882
  };
3828
3883
  const observer = new ResizeObserver(paint);
@@ -3839,7 +3894,7 @@ function LineReveal({
3839
3894
  };
3840
3895
  }, [playKey, hold, isPlaying, reduce]);
3841
3896
  const showSlices = isPlaying && !reduce;
3842
- return /* @__PURE__ */ jsxs11("div", { className: cn("relative", className), children: [
3897
+ return /* @__PURE__ */ jsxs12("div", { className: cn("relative", className), children: [
3843
3898
  /* @__PURE__ */ jsx26("div", { className: showSlices ? "pointer-events-none opacity-0" : void 0, children: /* @__PURE__ */ jsx26("div", { ref: sourceRef, children }) }),
3844
3899
  showSlices && /* @__PURE__ */ jsx26(
3845
3900
  "div",
@@ -3868,10 +3923,10 @@ function WidgetMessageBody({
3868
3923
  }
3869
3924
 
3870
3925
  // ../../apps/web/src/components/support-widget/widget-typing.tsx
3871
- import { jsx as jsx28, jsxs as jsxs12 } from "react/jsx-runtime";
3926
+ import { jsx as jsx28, jsxs as jsxs13 } from "react/jsx-runtime";
3872
3927
  var DOTS = [0, 160, 320];
3873
3928
  function WidgetTyping({ label }) {
3874
- return /* @__PURE__ */ jsxs12("span", { className: "flex items-center gap-1.5 py-2", children: [
3929
+ return /* @__PURE__ */ jsxs13("span", { className: "flex items-center gap-1.5 py-2", children: [
3875
3930
  /* @__PURE__ */ jsx28("span", { className: "sr-only", children: label }),
3876
3931
  DOTS.map((delay) => /* @__PURE__ */ jsx28(
3877
3932
  "span",
@@ -3886,16 +3941,17 @@ function WidgetTyping({ label }) {
3886
3941
  }
3887
3942
 
3888
3943
  // ../../apps/web/src/components/support-widget/widget-message.tsx
3889
- import { Fragment as Fragment28, jsx as jsx29, jsxs as jsxs13 } from "react/jsx-runtime";
3944
+ import { Fragment as Fragment28, jsx as jsx29, jsxs as jsxs14 } from "react/jsx-runtime";
3890
3945
  var BUBBLE = "max-w-[min(80%,36rem)] rounded-2xl px-4 py-2.5 text-base";
3891
3946
  var COMPACT_BUBBLE = "max-w-[min(80%,36rem)] rounded-2xl px-3.5 py-2 text-sm";
3892
- var VISITOR_BUBBLE = "bg-[color-mix(in_oklch,var(--muted),var(--foreground)_12%)]";
3947
+ var VISITOR_BUBBLE = "bg-[color-mix(in_oklch,var(--muted,oklch(0.955_0_0)),var(--foreground,oklch(0.145_0_0))_12%)]";
3893
3948
  var ARRIVES = "animate-in fade-in-0 slide-in-from-bottom-1 duration-300 ease-out-quint motion-reduce:animate-none";
3949
+ var SENT = "animate-in fade-in-0 slide-in-from-bottom-3 zoom-in-95 origin-bottom-right duration-400 ease-out-quint motion-reduce:animate-none";
3894
3950
  var LADDER_DELAY_MS = Math.round(MORPH_MS * 0.7);
3895
3951
  function WidgetDivider({ message }) {
3896
3952
  const label = message.startsConversation ? "New conversation" : message.dayLabel;
3897
3953
  if (!label) return null;
3898
- return /* @__PURE__ */ jsxs13("div", { className: "flex items-center gap-3 py-1", children: [
3954
+ return /* @__PURE__ */ jsxs14("div", { className: "flex items-center gap-3 py-1", children: [
3899
3955
  /* @__PURE__ */ jsx29("span", { className: "h-px flex-1 bg-border" }),
3900
3956
  /* @__PURE__ */ jsx29("span", { className: "text-sm text-muted-foreground", children: label }),
3901
3957
  /* @__PURE__ */ jsx29("span", { className: "h-px flex-1 bg-border" })
@@ -3908,7 +3964,7 @@ function WidgetMessage({
3908
3964
  compact = false,
3909
3965
  isLive = false
3910
3966
  }) {
3911
- return message.sender === "visitor" ? /* @__PURE__ */ jsx29(VisitorMessage, { message, compact }) : /* @__PURE__ */ jsx29(
3967
+ return message.sender === "visitor" ? /* @__PURE__ */ jsx29(VisitorMessage, { message, compact, isLive }) : /* @__PURE__ */ jsx29(
3912
3968
  AgentMessage,
3913
3969
  {
3914
3970
  message,
@@ -3932,7 +3988,7 @@ function BubbleContent({
3932
3988
  }
3933
3989
  );
3934
3990
  }
3935
- return /* @__PURE__ */ jsxs13(Fragment28, { children: [
3991
+ return /* @__PURE__ */ jsxs14(Fragment28, { children: [
3936
3992
  hasAttachments && /* @__PURE__ */ jsx29(WidgetAttachments, { attachments: message.attachments ?? [] }),
3937
3993
  message.body && /* @__PURE__ */ jsx29(
3938
3994
  WidgetMessageBody,
@@ -3946,9 +4002,10 @@ function BubbleContent({
3946
4002
  }
3947
4003
  function VisitorMessage({
3948
4004
  message,
3949
- compact
4005
+ compact,
4006
+ isLive
3950
4007
  }) {
3951
- return /* @__PURE__ */ jsx29("div", { className: cn("flex justify-end", ARRIVES), children: /* @__PURE__ */ jsx29(
4008
+ return /* @__PURE__ */ jsx29("div", { className: cn("flex justify-end", isLive ? SENT : ARRIVES), children: /* @__PURE__ */ jsx29(
3952
4009
  WidgetBubble,
3953
4010
  {
3954
4011
  morphKey: "settled",
@@ -3965,7 +4022,7 @@ function AgentMessage({
3965
4022
  isLive
3966
4023
  }) {
3967
4024
  const { agent } = message;
3968
- const [sawDots, setSawDots] = useState9(message.pending === true);
4025
+ const [sawDots, setSawDots] = useState8(message.pending === true);
3969
4026
  if (message.pending && !sawDots) setSawDots(true);
3970
4027
  const arrival = isLive && !message.pending && message.body.length > 0 ? { key: message.id, delay: sawDots ? LADDER_DELAY_MS : 0 } : null;
3971
4028
  const destination = message.navigateTo === void 0 ? null : {
@@ -3979,8 +4036,8 @@ function AgentMessage({
3979
4036
  // below, which spread four small things across three rows and pushed every
3980
4037
  // bubble in off the edge. All of it is one line now, and the bubble gets
3981
4038
  // the full width back.
3982
- /* @__PURE__ */ jsxs13("div", { className: cn("flex flex-col items-start gap-1", ARRIVES), children: [
3983
- group.isFirst && /* @__PURE__ */ jsxs13(
4039
+ /* @__PURE__ */ jsxs14("div", { className: cn("flex flex-col items-start gap-1", ARRIVES), children: [
4040
+ group.isFirst && /* @__PURE__ */ jsxs14(
3984
4041
  "div",
3985
4042
  {
3986
4043
  className: cn(
@@ -3988,7 +4045,7 @@ function AgentMessage({
3988
4045
  compact ? "text-xs" : "text-sm"
3989
4046
  ),
3990
4047
  children: [
3991
- agent && /* @__PURE__ */ jsxs13(Fragment28, { children: [
4048
+ agent && /* @__PURE__ */ jsxs14(Fragment28, { children: [
3992
4049
  /* @__PURE__ */ jsx29(
3993
4050
  WidgetAvatar,
3994
4051
  {
@@ -3997,7 +4054,7 @@ function AgentMessage({
3997
4054
  }
3998
4055
  ),
3999
4056
  /* @__PURE__ */ jsx29("span", { className: "truncate font-medium", children: agent.name }),
4000
- agent.kind === "ai" && /* @__PURE__ */ jsx29(Badge, { variant: "muted", className: "shrink-0 rounded-md px-1", children: "AI" })
4057
+ agent.kind === "ai" && /* @__PURE__ */ jsx29(Badge, { variant: "muted", className: "shrink-0 rounded-md bg-muted px-1 inset-ring-0", children: "AI" })
4001
4058
  ] }),
4002
4059
  !message.pending && /* @__PURE__ */ jsx29("span", { className: "shrink-0 text-muted-foreground tabular-nums", children: message.time })
4003
4060
  ]
@@ -4035,21 +4092,21 @@ var TYPING_TTL_MS = 12e3;
4035
4092
  var SNOOZE_MAX_MS = 365 * 24 * 60 * 6e4;
4036
4093
 
4037
4094
  // ../../apps/web/src/components/support-widget/widget-rating.tsx
4038
- import { useEffect as useEffect7, useRef as useRef10, useState as useState10 } from "react";
4039
- import { jsx as jsx30, jsxs as jsxs14 } from "react/jsx-runtime";
4095
+ import { useEffect as useEffect6, useRef as useRef11, useState as useState9 } from "react";
4096
+ import { jsx as jsx30, jsxs as jsxs15 } from "react/jsx-runtime";
4040
4097
  var STARS = Array.from({ length: RATING_MAX_STARS }, (_, index) => index + 1);
4041
4098
  var THANKS_MS = 1400;
4042
4099
  var STAR_STAGGER_MS = 40;
4043
4100
  function WidgetRating({ rating }) {
4044
- const [isOpen, setIsOpen] = useState10(false);
4045
- const [picked, setPicked] = useState10(rating.stars);
4046
- const [hovered, setHovered] = useState10(null);
4047
- const [feedback, setFeedback] = useState10("");
4048
- const [isSending, setIsSending] = useState10(false);
4049
- const [error, setError] = useState10(null);
4050
- const [isDone, setIsDone] = useState10(false);
4051
- const timers = useRef10([]);
4052
- useEffect7(() => {
4101
+ const [isOpen, setIsOpen] = useState9(false);
4102
+ const [picked, setPicked] = useState9(rating.stars);
4103
+ const [hovered, setHovered] = useState9(null);
4104
+ const [feedback, setFeedback] = useState9("");
4105
+ const [isSending, setIsSending] = useState9(false);
4106
+ const [error, setError] = useState9(null);
4107
+ const [isDone, setIsDone] = useState9(false);
4108
+ const timers = useRef11([]);
4109
+ useEffect6(() => {
4053
4110
  const frame = requestAnimationFrame(() => setIsOpen(true));
4054
4111
  const pending = timers.current;
4055
4112
  return () => {
@@ -4090,9 +4147,9 @@ function WidgetRating({ rating }) {
4090
4147
  setIsDone(true);
4091
4148
  later(THANKS_MS, leave);
4092
4149
  }
4093
- return /* @__PURE__ */ jsx30(Collapse, { isOpen, className: "shrink-0", children: /* @__PURE__ */ jsxs14("div", { className: "mx-4 mb-3 rounded-xl border bg-muted/40 px-4 dark:bg-muted/20", children: [
4094
- /* @__PURE__ */ jsx30(Collapse, { isOpen: !isDone, children: /* @__PURE__ */ jsxs14("div", { className: "py-3", children: [
4095
- /* @__PURE__ */ jsxs14("div", { className: "flex items-start justify-between gap-2", children: [
4150
+ return /* @__PURE__ */ jsx30(Collapse, { isOpen, className: "shrink-0", children: /* @__PURE__ */ jsxs15("div", { className: "mx-4 mb-3 rounded-xl border bg-muted/40 px-4 dark:bg-muted/20", children: [
4151
+ /* @__PURE__ */ jsx30(Collapse, { isOpen: !isDone, children: /* @__PURE__ */ jsxs15("div", { className: "py-3", children: [
4152
+ /* @__PURE__ */ jsxs15("div", { className: "flex items-start justify-between gap-2", children: [
4096
4153
  /* @__PURE__ */ jsx30("p", { className: "text-base font-medium", children: picked === null ? "How did we do?" : "Anything we could improve?" }),
4097
4154
  /* @__PURE__ */ jsx30(
4098
4155
  WidgetButton,
@@ -4149,7 +4206,7 @@ function WidgetRating({ rating }) {
4149
4206
  ))
4150
4207
  }
4151
4208
  ),
4152
- /* @__PURE__ */ jsx30(Collapse, { isOpen: picked !== null, children: /* @__PURE__ */ jsxs14("div", { className: "flex flex-col gap-2 pt-2", children: [
4209
+ /* @__PURE__ */ jsx30(Collapse, { isOpen: picked !== null, children: /* @__PURE__ */ jsxs15("div", { className: "flex flex-col gap-2 pt-2", children: [
4153
4210
  /* @__PURE__ */ jsx30(
4154
4211
  Textarea,
4155
4212
  {
@@ -4161,7 +4218,7 @@ function WidgetRating({ rating }) {
4161
4218
  maxLength: RATING_FEEDBACK_MAX_LENGTH,
4162
4219
  disabled: isSending,
4163
4220
  className: cn(
4164
- TEXT_FIELD_SHELL,
4221
+ WIDGET_TEXT_FIELD_SHELL,
4165
4222
  "max-h-28 min-h-20 bg-card px-3 py-2.5 text-base"
4166
4223
  )
4167
4224
  }
@@ -4184,16 +4241,16 @@ function WidgetRating({ rating }) {
4184
4241
  }
4185
4242
 
4186
4243
  // ../../apps/web/src/components/support-widget/widget-status.tsx
4187
- import { jsx as jsx31, jsxs as jsxs15 } from "react/jsx-runtime";
4244
+ import { jsx as jsx31, jsxs as jsxs16 } from "react/jsx-runtime";
4188
4245
  function WidgetAwaitingHuman() {
4189
- return /* @__PURE__ */ jsxs15("p", { className: "flex items-center justify-center gap-1.5 px-4 pb-1.5 text-sm text-muted-foreground", children: [
4246
+ return /* @__PURE__ */ jsxs16("p", { className: "flex items-center justify-center gap-1.5 px-4 pb-1.5 text-sm text-muted-foreground", children: [
4190
4247
  /* @__PURE__ */ jsx31(UserCircle, { "aria-hidden": true, className: "size-4" }),
4191
4248
  "Waiting for the support team"
4192
4249
  ] });
4193
4250
  }
4194
4251
 
4195
4252
  // ../../apps/web/src/components/support-widget/support-widget.tsx
4196
- import { jsx as jsx32, jsxs as jsxs16 } from "react/jsx-runtime";
4253
+ import { jsx as jsx32, jsxs as jsxs17 } from "react/jsx-runtime";
4197
4254
  function joinsPreviousTurn(message, previous) {
4198
4255
  return !message.dayLabel && !message.startsConversation && message.sender === previous.sender && message.agent?.id === previous.agent?.id && withinRun(previous.sentAt, message.sentAt);
4199
4256
  }
@@ -4223,7 +4280,7 @@ function SupportWidget({
4223
4280
  className
4224
4281
  }) {
4225
4282
  const stack = useStackShift(messages.length);
4226
- return /* @__PURE__ */ jsxs16(
4283
+ return /* @__PURE__ */ jsxs17(
4227
4284
  "section",
4228
4285
  {
4229
4286
  "aria-label": `${presence.name} support`,
@@ -4231,7 +4288,7 @@ function SupportWidget({
4231
4288
  // The widget keeps its own radius scale. The app's token can climb as
4232
4289
  // the chrome gets rounder without the widget changing shape under the
4233
4290
  // hosts embedding it.
4234
- "flex h-full w-full flex-col overflow-hidden rounded-xl border bg-card text-card-foreground [--radius:0.625rem]",
4291
+ `flex h-full w-full flex-col overflow-hidden ${WIDGET_CORNER} border bg-card text-card-foreground [--radius:0.625rem]`,
4235
4292
  // And its own pace for a reply arriving a line at a time: a step
4236
4293
  // quicker than the palette's ladder, since a support answer is a
4237
4294
  // few short lines in a bubble rather than an essay in a column.
@@ -4305,14 +4362,14 @@ function Conversation({
4305
4362
  stackRef
4306
4363
  }) {
4307
4364
  const groups = groupMessages(messages, joinsPreviousTurn);
4308
- const [openedWith] = useState11(
4365
+ const [openedWith] = useState10(
4309
4366
  () => new Set(messages.map((message) => message.id))
4310
4367
  );
4311
- const [heldOffer, setHeldOffer] = useState11(offer);
4368
+ const [heldOffer, setHeldOffer] = useState10(offer);
4312
4369
  if (offer !== null && offer.key !== heldOffer?.key) setHeldOffer(offer);
4313
- const [heldNotice, setHeldNotice] = useState11(notice ?? null);
4370
+ const [heldNotice, setHeldNotice] = useState10(notice ?? null);
4314
4371
  if (notice && notice !== heldNotice) setHeldNotice(notice);
4315
- return /* @__PURE__ */ jsxs16(
4372
+ return /* @__PURE__ */ jsxs17(
4316
4373
  "div",
4317
4374
  {
4318
4375
  className: cn(
@@ -4340,7 +4397,7 @@ function Conversation({
4340
4397
  so a scroller left alone slides the newest message under the
4341
4398
  composer and has to catch up afterwards. Following it is what the
4342
4399
  scroller stops easing for, so this tracks frame by frame. */
4343
- /* @__PURE__ */ jsx32(MessageScrollerProvider, { children: /* @__PURE__ */ jsxs16(MessageScroller, { className: "h-auto min-h-0 flex-1", children: [
4400
+ /* @__PURE__ */ jsx32(MessageScrollerProvider, { children: /* @__PURE__ */ jsxs17(MessageScroller, { className: "h-auto min-h-0 flex-1", children: [
4344
4401
  /* @__PURE__ */ jsx32(
4345
4402
  MessageScrollerViewport,
4346
4403
  {
@@ -4354,7 +4411,7 @@ function Conversation({
4354
4411
  {
4355
4412
  ref: stackRef,
4356
4413
  className: "justify-end gap-0",
4357
- children: messages.map((message, index) => /* @__PURE__ */ jsxs16(
4414
+ children: messages.map((message, index) => /* @__PURE__ */ jsxs17(
4358
4415
  MessageScrollerItem,
4359
4416
  {
4360
4417
  messageId: message.id,
@@ -4417,10 +4474,10 @@ function Conversation({
4417
4474
  // ../../apps/web/src/components/support-widget/use-launcher-drag.ts
4418
4475
  import {
4419
4476
  useCallback as useCallback6,
4420
- useEffect as useEffect8,
4421
- useLayoutEffect as useLayoutEffect6,
4422
- useRef as useRef11,
4423
- useState as useState12,
4477
+ useEffect as useEffect7,
4478
+ useLayoutEffect as useLayoutEffect7,
4479
+ useRef as useRef12,
4480
+ useState as useState11,
4424
4481
  useSyncExternalStore as useSyncExternalStore4
4425
4482
  } from "react";
4426
4483
  import { flushSync } from "react-dom";
@@ -4439,9 +4496,9 @@ function useLauncherDrag({
4439
4496
  subject
4440
4497
  }) {
4441
4498
  const spot = useSyncExternalStore4(subscribeSpot, getSpot, getDefaultSpot);
4442
- const [isMoving, setIsMoving] = useState12(false);
4443
- const grip = useRef11(null);
4444
- const justDragged = useRef11(false);
4499
+ const [isMoving, setIsMoving] = useState11(false);
4500
+ const grip = useRef12(null);
4501
+ const justDragged = useRef12(false);
4445
4502
  const measure = useCallback6(() => {
4446
4503
  const box = boxRef.current;
4447
4504
  const button = buttonRef.current;
@@ -4492,12 +4549,12 @@ function useLauncherDrag({
4492
4549
  box.style.top = `${metrics.boxTop - taken.bounds.top}px`;
4493
4550
  box.style.setProperty(BUTTON_Y, `${metrics.buttonInBox}px`);
4494
4551
  }, [boxRef, measure, spot]);
4495
- useLayoutEffect6(place, [place]);
4496
- useEffect8(() => {
4552
+ useLayoutEffect7(place, [place]);
4553
+ useEffect7(() => {
4497
4554
  window.addEventListener("resize", place);
4498
4555
  return () => window.removeEventListener("resize", place);
4499
4556
  }, [place]);
4500
- useEffect8(() => {
4557
+ useEffect7(() => {
4501
4558
  const box = boxRef.current;
4502
4559
  if (!box || typeof ResizeObserver === "undefined") return;
4503
4560
  const observer = new ResizeObserver(place);
@@ -4508,10 +4565,10 @@ function useLauncherDrag({
4508
4565
  const box = boxRef.current;
4509
4566
  if (box) box.style.transform = "";
4510
4567
  }, [boxRef]);
4511
- const holdPointer = useCallback6((held) => {
4512
- document.body.style.userSelect = held ? "none" : "";
4513
- document.body.style.cursor = held ? "grabbing" : "";
4514
- if (held) window.getSelection()?.removeAllRanges();
4568
+ const holdPointer = useCallback6((held2) => {
4569
+ document.body.style.userSelect = held2 ? "none" : "";
4570
+ document.body.style.cursor = held2 ? "grabbing" : "";
4571
+ if (held2) window.getSelection()?.removeAllRanges();
4515
4572
  }, []);
4516
4573
  const handlePointerDown = useCallback6(
4517
4574
  (event) => {
@@ -4548,57 +4605,57 @@ function useLauncherDrag({
4548
4605
  );
4549
4606
  const handlePointerMove = useCallback6(
4550
4607
  (event) => {
4551
- const held = grip.current;
4608
+ const held2 = grip.current;
4552
4609
  const box = boxRef.current;
4553
- if (held === null || box === null) return;
4554
- if (!held.moved) {
4610
+ if (held2 === null || box === null) return;
4611
+ if (!held2.moved) {
4555
4612
  const travelled = Math.hypot(
4556
- event.clientX - held.startX,
4557
- event.clientY - held.startY
4613
+ event.clientX - held2.startX,
4614
+ event.clientY - held2.startY
4558
4615
  );
4559
4616
  if (travelled < DRAG_THRESHOLD) return;
4560
- held.moved = true;
4617
+ held2.moved = true;
4561
4618
  holdPointer(true);
4562
4619
  setIsMoving(true);
4563
4620
  }
4564
- held.at = {
4621
+ held2.at = {
4565
4622
  x: clamp2(
4566
- event.clientX - held.grabX,
4567
- held.bounds.left + held.margin,
4568
- held.bounds.left + held.bounds.width - held.margin - held.dragged.width
4623
+ event.clientX - held2.grabX,
4624
+ held2.bounds.left + held2.margin,
4625
+ held2.bounds.left + held2.bounds.width - held2.margin - held2.dragged.width
4569
4626
  ),
4570
4627
  y: clamp2(
4571
- event.clientY - held.grabY,
4572
- held.bounds.top + held.margin,
4573
- held.bounds.top + held.bounds.height - held.margin - held.dragged.height
4628
+ event.clientY - held2.grabY,
4629
+ held2.bounds.top + held2.margin,
4630
+ held2.bounds.top + held2.bounds.height - held2.margin - held2.dragged.height
4574
4631
  )
4575
4632
  };
4576
- box.style.transform = `translate3d(${held.at.x - held.resting.x}px, ${held.at.y - held.resting.y}px, 0)`;
4633
+ box.style.transform = `translate3d(${held2.at.x - held2.resting.x}px, ${held2.at.y - held2.resting.y}px, 0)`;
4577
4634
  },
4578
4635
  [boxRef, holdPointer]
4579
4636
  );
4580
4637
  const finishDrag = useCallback6(() => {
4581
- const held = grip.current;
4582
- if (held === null) return;
4638
+ const held2 = grip.current;
4639
+ if (held2 === null) return;
4583
4640
  grip.current = null;
4584
4641
  holdPointer(false);
4585
4642
  const box = boxRef.current;
4586
- if (!held.moved || box === null) {
4643
+ if (!held2.moved || box === null) {
4587
4644
  setIsMoving(false);
4588
4645
  return;
4589
4646
  }
4590
4647
  justDragged.current = true;
4591
- const target = held.subject === "panel" ? spotFromPanel(held.at, held.bounds, held.size, held.box, held.margin) : spotFrom(held.at, held.bounds, held.size, held.margin);
4648
+ const target = held2.subject === "panel" ? spotFromPanel(held2.at, held2.bounds, held2.size, held2.box, held2.margin) : spotFrom(held2.at, held2.bounds, held2.size, held2.margin);
4592
4649
  const home = metricsFor(
4593
4650
  target,
4594
- held.bounds,
4595
- held.size,
4596
- held.box.height,
4597
- held.margin
4651
+ held2.bounds,
4652
+ held2.size,
4653
+ held2.box.height,
4654
+ held2.margin
4598
4655
  );
4599
4656
  const from = {
4600
- x: held.at.x - (target.side === "left" ? held.bounds.left + held.margin : held.bounds.left + held.bounds.width - held.margin - held.dragged.width),
4601
- y: held.at.y - (held.subject === "panel" ? home.boxTop : home.buttonY)
4657
+ x: held2.at.x - (target.side === "left" ? held2.bounds.left + held2.margin : held2.bounds.left + held2.bounds.width - held2.margin - held2.dragged.width),
4658
+ y: held2.at.y - (held2.subject === "panel" ? home.boxTop : home.buttonY)
4602
4659
  };
4603
4660
  flushSync(() => setSpot(target));
4604
4661
  settle();
@@ -4625,7 +4682,7 @@ function useLauncherDrag({
4625
4682
  },
4626
4683
  [finishDrag]
4627
4684
  );
4628
- useEffect8(() => {
4685
+ useEffect7(() => {
4629
4686
  if (!isMoving) return;
4630
4687
  const end = () => finishDrag();
4631
4688
  window.addEventListener("pointerup", end);
@@ -4643,7 +4700,7 @@ function useLauncherDrag({
4643
4700
  onPointerUp: handlePointerUp,
4644
4701
  onPointerCancel: handlePointerUp
4645
4702
  };
4646
- useEffect8(() => {
4703
+ useEffect7(() => {
4647
4704
  function abandon() {
4648
4705
  if (grip.current === null) return;
4649
4706
  grip.current = null;
@@ -4682,15 +4739,15 @@ function clamp2(value, low, high) {
4682
4739
  }
4683
4740
 
4684
4741
  // ../../apps/web/src/components/support-widget/use-teaser-placement.ts
4685
- import { useLayoutEffect as useLayoutEffect7, useState as useState13 } from "react";
4742
+ import { useLayoutEffect as useLayoutEffect8, useState as useState12 } from "react";
4686
4743
  var ROOM_ABOVE_PX = 128;
4687
4744
  function useTeaserPlacement({
4688
4745
  buttonRef,
4689
4746
  enabled: enabled2,
4690
4747
  isMoving
4691
4748
  }) {
4692
- const [placement, setPlacement] = useState13("above");
4693
- useLayoutEffect7(() => {
4749
+ const [placement, setPlacement] = useState12("above");
4750
+ useLayoutEffect8(() => {
4694
4751
  if (!enabled2 || isMoving) return;
4695
4752
  const measure = () => {
4696
4753
  const button = buttonRef.current;
@@ -4706,18 +4763,18 @@ function useTeaserPlacement({
4706
4763
 
4707
4764
  // ../../apps/web/src/components/support-widget/use-widget-sheet.ts
4708
4765
  import {
4709
- useLayoutEffect as useLayoutEffect8,
4710
- useState as useState14,
4766
+ useLayoutEffect as useLayoutEffect9,
4767
+ useState as useState13,
4711
4768
  useSyncExternalStore as useSyncExternalStore5
4712
4769
  } from "react";
4713
4770
 
4714
4771
  // ../../apps/web/src/components/support-widget/use-page-lock.ts
4715
- import { useEffect as useEffect9 } from "react";
4772
+ import { useEffect as useEffect8 } from "react";
4716
4773
  function usePageLock(isLocked) {
4717
- useEffect9(() => {
4774
+ useEffect8(() => {
4718
4775
  if (!isLocked) return;
4719
4776
  const { style } = document.body;
4720
- const held = {
4777
+ const held2 = {
4721
4778
  position: style.position,
4722
4779
  top: style.top,
4723
4780
  left: style.left,
@@ -4731,11 +4788,11 @@ function usePageLock(isLocked) {
4731
4788
  style.right = "0";
4732
4789
  style.overflow = "hidden";
4733
4790
  return () => {
4734
- style.position = held.position;
4735
- style.top = held.top;
4736
- style.left = held.left;
4737
- style.right = held.right;
4738
- style.overflow = held.overflow;
4791
+ style.position = held2.position;
4792
+ style.top = held2.top;
4793
+ style.left = held2.left;
4794
+ style.right = held2.right;
4795
+ style.overflow = held2.overflow;
4739
4796
  window.scrollTo({ top: scrollY, behavior: "instant" });
4740
4797
  };
4741
4798
  }, [isLocked]);
@@ -4743,7 +4800,7 @@ function usePageLock(isLocked) {
4743
4800
 
4744
4801
  // ../../apps/web/src/components/support-widget/use-widget-sheet.ts
4745
4802
  var PHONE = "(max-width: 40rem)";
4746
- var SETTLE_MS2 = 400;
4803
+ var SETTLE_MS = 400;
4747
4804
  var KEYBOARD_PX = 96;
4748
4805
  function subscribePhone(onChange) {
4749
4806
  const media = window.matchMedia(PHONE);
@@ -4762,13 +4819,13 @@ function useWidgetSheet({
4762
4819
  isPhoneNow,
4763
4820
  isPhoneOnServer
4764
4821
  );
4765
- const [isFramed, setIsFramed] = useState14(false);
4766
- useLayoutEffect8(() => {
4822
+ const [isFramed, setIsFramed] = useState13(false);
4823
+ useLayoutEffect9(() => {
4767
4824
  setIsFramed(boxRef.current?.offsetParent != null);
4768
4825
  }, [boxRef, isPhone]);
4769
4826
  const isSheet = isPhone && !isFramed;
4770
4827
  const isUp = isSheet && isOpen;
4771
- useLayoutEffect8(() => {
4828
+ useLayoutEffect9(() => {
4772
4829
  if (!isUp) return;
4773
4830
  const panel = panelRef.current;
4774
4831
  const viewport = window.visualViewport;
@@ -4797,7 +4854,7 @@ function useWidgetSheet({
4797
4854
  frame = performance.now() < until ? requestAnimationFrame(follow) : 0;
4798
4855
  };
4799
4856
  const track = () => {
4800
- until = performance.now() + SETTLE_MS2;
4857
+ until = performance.now() + SETTLE_MS;
4801
4858
  if (frame === 0) frame = requestAnimationFrame(follow);
4802
4859
  };
4803
4860
  fit();
@@ -4820,7 +4877,7 @@ function useWidgetSheet({
4820
4877
  }
4821
4878
 
4822
4879
  // ../../apps/web/src/components/support-widget/widget-modal.tsx
4823
- import { useRef as useRef12 } from "react";
4880
+ import { useRef as useRef13 } from "react";
4824
4881
  import { createPortal } from "react-dom";
4825
4882
 
4826
4883
  // ../../apps/web/src/components/support-widget/use-hydrated.ts
@@ -4834,12 +4891,12 @@ function useHydrated() {
4834
4891
  }
4835
4892
 
4836
4893
  // ../../apps/web/src/components/support-widget/use-scroll-lock.ts
4837
- import { useEffect as useEffect10 } from "react";
4894
+ import { useEffect as useEffect9 } from "react";
4838
4895
  function useScrollLock(isLocked) {
4839
- useEffect10(() => {
4896
+ useEffect9(() => {
4840
4897
  if (!isLocked) return;
4841
4898
  const { body } = document;
4842
- const held = {
4899
+ const held2 = {
4843
4900
  overflow: body.style.overflow,
4844
4901
  paddingRight: body.style.paddingRight
4845
4902
  };
@@ -4850,14 +4907,14 @@ function useScrollLock(isLocked) {
4850
4907
  body.style.paddingRight = `${existing + gutter}px`;
4851
4908
  }
4852
4909
  return () => {
4853
- body.style.overflow = held.overflow;
4854
- body.style.paddingRight = held.paddingRight;
4910
+ body.style.overflow = held2.overflow;
4911
+ body.style.paddingRight = held2.paddingRight;
4855
4912
  };
4856
4913
  }, [isLocked]);
4857
4914
  }
4858
4915
 
4859
4916
  // ../../apps/web/src/components/support-widget/widget-modal.tsx
4860
- import { jsx as jsx33, jsxs as jsxs17 } from "react/jsx-runtime";
4917
+ import { jsx as jsx33, jsxs as jsxs18 } from "react/jsx-runtime";
4861
4918
  var LAYER = 2147483e3;
4862
4919
  var WIDTH = "50rem";
4863
4920
  var HEIGHT = "40rem";
@@ -4873,13 +4930,13 @@ function SupportModal({
4873
4930
  label,
4874
4931
  children
4875
4932
  }) {
4876
- const layerRef = useRef12(null);
4933
+ const layerRef = useRef13(null);
4877
4934
  const isHydrated = useHydrated();
4878
4935
  const isSheet = useWidgetSheet({ boxRef: layerRef, panelRef, isOpen });
4879
4936
  useScrollLock(isOpen && !isSheet);
4880
4937
  if (!isHydrated) return null;
4881
4938
  return createPortal(
4882
- /* @__PURE__ */ jsxs17(
4939
+ /* @__PURE__ */ jsxs18(
4883
4940
  "div",
4884
4941
  {
4885
4942
  ref: layerRef,
@@ -4924,7 +4981,7 @@ function SupportModal({
4924
4981
  isOpen ? "translate-y-0 opacity-100" : "translate-y-8 opacity-0"
4925
4982
  ] : [
4926
4983
  // Never past the layer's padding, whatever the size says.
4927
- "relative max-h-full max-w-full rounded-xl shadow-2xl",
4984
+ `relative max-h-full max-w-full ${WIDGET_CORNER} shadow-2xl`,
4928
4985
  MOTION,
4929
4986
  isOpen ? [OPEN, "scale-100 opacity-100 translate-y-0"] : [SHUT, "scale-[0.96] opacity-0 translate-y-2"]
4930
4987
  ]
@@ -4947,7 +5004,7 @@ var SHEET_PANEL = cn(
4947
5004
  );
4948
5005
 
4949
5006
  // ../../apps/web/src/components/support-widget/widget-teaser.tsx
4950
- import { useState as useState15 } from "react";
5007
+ import { useState as useState14 } from "react";
4951
5008
 
4952
5009
  // ../../apps/web/src/components/support-widget/plain-text.ts
4953
5010
  function plainTextOf(markdown) {
@@ -4955,7 +5012,7 @@ function plainTextOf(markdown) {
4955
5012
  }
4956
5013
 
4957
5014
  // ../../apps/web/src/components/support-widget/widget-teaser.tsx
4958
- import { Fragment as Fragment29, jsx as jsx34, jsxs as jsxs18 } from "react/jsx-runtime";
5015
+ import { Fragment as Fragment29, jsx as jsx34, jsxs as jsxs19 } from "react/jsx-runtime";
4959
5016
  var WIDTH2 = "min(20rem, 100%)";
4960
5017
  var GAP = "0.5rem";
4961
5018
  function WidgetTeaser({
@@ -4968,13 +5025,13 @@ function WidgetTeaser({
4968
5025
  onOpen,
4969
5026
  onDismiss
4970
5027
  }) {
4971
- const [held, setHeld] = useState15(message);
4972
- if (message !== null && message !== held) setHeld(message);
4973
- const shown = message ?? held;
5028
+ const [held2, setHeld] = useState14(message);
5029
+ if (message !== null && message !== held2) setHeld(message);
5030
+ const shown = message ?? held2;
4974
5031
  if (shown === null) return null;
4975
5032
  const agent = shown.agent;
4976
5033
  const text = plainTextOf(shown.body);
4977
- return /* @__PURE__ */ jsxs18(
5034
+ return /* @__PURE__ */ jsxs19(
4978
5035
  "div",
4979
5036
  {
4980
5037
  inert: !isShown,
@@ -4999,7 +5056,7 @@ function WidgetTeaser({
4999
5056
  ] : "pointer-events-none scale-95 opacity-0 duration-150 ease-out-quint"
5000
5057
  ),
5001
5058
  children: [
5002
- /* @__PURE__ */ jsxs18(
5059
+ /* @__PURE__ */ jsxs19(
5003
5060
  "button",
5004
5061
  {
5005
5062
  type: "button",
@@ -5008,17 +5065,17 @@ function WidgetTeaser({
5008
5065
  className: cn(
5009
5066
  // Its own radius scale, like the panel, so the app's token cannot
5010
5067
  // reshape a card that sits on somebody else's site.
5011
- "block w-full rounded-xl border bg-card p-3.5 pr-10 text-left text-card-foreground shadow-lg [--radius:0.625rem]",
5068
+ `block w-full ${WIDGET_CORNER} border bg-card p-3.5 pr-10 text-left text-card-foreground shadow-lg [--radius:0.625rem]`,
5012
5069
  "transition-[background-color,box-shadow] outline-none",
5013
5070
  "hover:bg-muted/50 focus-visible:ring-3 focus-visible:ring-ring/50",
5014
5071
  "cursor-pointer select-none"
5015
5072
  ),
5016
5073
  children: [
5017
- /* @__PURE__ */ jsxs18("span", { className: "flex items-center gap-2", children: [
5018
- agent && /* @__PURE__ */ jsxs18(Fragment29, { children: [
5074
+ /* @__PURE__ */ jsxs19("span", { className: "flex items-center gap-2", children: [
5075
+ agent && /* @__PURE__ */ jsxs19(Fragment29, { children: [
5019
5076
  /* @__PURE__ */ jsx34(WidgetAvatar, { agent, className: "size-6 rounded-md" }),
5020
5077
  /* @__PURE__ */ jsx34("span", { className: "truncate text-sm font-medium", children: agent.name }),
5021
- agent.kind === "ai" && /* @__PURE__ */ jsx34(Badge, { variant: "muted", className: "shrink-0 rounded-md px-1", children: "AI" })
5078
+ agent.kind === "ai" && /* @__PURE__ */ jsx34(Badge, { variant: "muted", className: "shrink-0 rounded-md bg-muted px-1 inset-ring-0", children: "AI" })
5022
5079
  ] }),
5023
5080
  /* @__PURE__ */ jsx34("span", { className: "ml-auto shrink-0 text-sm text-muted-foreground tabular-nums", children: shown.time })
5024
5081
  ] }),
@@ -5048,14 +5105,13 @@ function WidgetTeaser({
5048
5105
  }
5049
5106
 
5050
5107
  // ../../apps/web/src/components/support-widget/widget-launcher.tsx
5051
- import { Fragment as Fragment30, jsx as jsx35, jsxs as jsxs19 } from "react/jsx-runtime";
5108
+ import { Fragment as Fragment30, jsx as jsx35, jsxs as jsxs20 } from "react/jsx-runtime";
5052
5109
  var DEFAULT_WIDTH = "28rem";
5053
5110
  var DEFAULT_HEIGHT = "40rem";
5054
5111
  var DEFAULT_BUTTON_SIZE = "3rem";
5055
5112
  var EXPANDED_WIDTH = "34rem";
5056
5113
  var EXPANDED_HEIGHT = "52rem";
5057
- var PANEL_RADIUS = "0.75rem";
5058
- var OPEN_CLIP = `inset(0px 0px 0px 0px round ${PANEL_RADIUS})`;
5114
+ var OPEN_CLIP = `inset(0px 0px 0px 0px round ${WIDGET_RADIUS})`;
5059
5115
  var buttonY = (buttonSize) => `var(${BUTTON_Y}, calc(100% - ${buttonSize}))`;
5060
5116
  var MARGIN = "1rem";
5061
5117
  var CLOSED_SCALE = "scale(0.94)";
@@ -5105,14 +5161,14 @@ function SupportLauncher({
5105
5161
  onOpenChange,
5106
5162
  className
5107
5163
  }) {
5108
- const [uncontrolledOpen, setUncontrolledOpen] = useState16(defaultOpen);
5164
+ const [uncontrolledOpen, setUncontrolledOpen] = useState15(defaultOpen);
5109
5165
  const isOpen = open ?? uncontrolledOpen;
5110
5166
  const hasBubble = launcher === "bubble";
5111
5167
  const isModal = presentation === "modal";
5112
- const [isExpanded, setExpanded] = useState16(false);
5113
- const launcherRef = useRef13(null);
5114
- const panelRef = useRef13(null);
5115
- const boxRef = useRef13(null);
5168
+ const [isExpanded, setExpanded] = useState15(false);
5169
+ const launcherRef = useRef14(null);
5170
+ const panelRef = useRef14(null);
5171
+ const boxRef = useRef14(null);
5116
5172
  const drag = useLauncherDrag({
5117
5173
  boxRef,
5118
5174
  buttonRef: launcherRef,
@@ -5132,8 +5188,8 @@ function SupportLauncher({
5132
5188
  enabled: hasTeaser,
5133
5189
  isMoving: drag.isMoving
5134
5190
  });
5135
- const opener = useRef13(null);
5136
- const wasOpen = useRef13(isOpen);
5191
+ const opener = useRef14(null);
5192
+ const wasOpen = useRef14(isOpen);
5137
5193
  const setOpen = useCallback7(
5138
5194
  (next) => {
5139
5195
  if (open === void 0) setUncontrolledOpen(next);
@@ -5149,10 +5205,10 @@ function SupportLauncher({
5149
5205
  },
5150
5206
  [onFollow, isSheet, isModal, close]
5151
5207
  );
5152
- useEffect11(() => {
5208
+ useEffect10(() => {
5153
5209
  if (isOpen) warmMarkdown();
5154
5210
  }, [isOpen]);
5155
- useEffect11(() => {
5211
+ useEffect10(() => {
5156
5212
  if (!isOpen) return;
5157
5213
  function handleKeyDown(event) {
5158
5214
  if (event.key === "Escape") close();
@@ -5160,7 +5216,7 @@ function SupportLauncher({
5160
5216
  document.addEventListener("keydown", handleKeyDown);
5161
5217
  return () => document.removeEventListener("keydown", handleKeyDown);
5162
5218
  }, [isOpen, close]);
5163
- useEffect11(() => {
5219
+ useEffect10(() => {
5164
5220
  if (wasOpen.current === isOpen) return;
5165
5221
  wasOpen.current = isOpen;
5166
5222
  if (isOpen) {
@@ -5201,8 +5257,8 @@ function SupportLauncher({
5201
5257
  compact: isModal
5202
5258
  }
5203
5259
  );
5204
- return /* @__PURE__ */ jsxs19(Fragment30, { children: [
5205
- /* @__PURE__ */ jsxs19(
5260
+ return /* @__PURE__ */ jsxs20(Fragment30, { children: [
5261
+ /* @__PURE__ */ jsxs20(
5206
5262
  "div",
5207
5263
  {
5208
5264
  ref: boxRef,
@@ -5271,7 +5327,7 @@ function SupportLauncher({
5271
5327
  children: widget(isSheet)
5272
5328
  }
5273
5329
  ),
5274
- hasBubble && /* @__PURE__ */ jsxs19(
5330
+ hasBubble && /* @__PURE__ */ jsxs20(
5275
5331
  "button",
5276
5332
  {
5277
5333
  ref: launcherRef,
@@ -5386,9 +5442,9 @@ function SupportLauncher({
5386
5442
  }
5387
5443
 
5388
5444
  // ../../apps/web/src/components/support-widget/widget-spotlight.tsx
5389
- import { useEffect as useEffect12, useRef as useRef14, useState as useState17 } from "react";
5445
+ import { useEffect as useEffect11, useRef as useRef15, useState as useState16 } from "react";
5390
5446
  import { createPortal as createPortal2 } from "react-dom";
5391
- import { jsx as jsx36, jsxs as jsxs20 } from "react/jsx-runtime";
5447
+ import { jsx as jsx36, jsxs as jsxs21 } from "react/jsx-runtime";
5392
5448
  var FIND_TIMEOUT_MS = 4e3;
5393
5449
  var HOLD_MS = 2600;
5394
5450
  var FADE_MS = 300;
@@ -5397,10 +5453,10 @@ function prefersReducedMotion4() {
5397
5453
  return window.matchMedia("(prefers-reduced-motion: reduce)").matches;
5398
5454
  }
5399
5455
  function WidgetSpotlight({ selector }) {
5400
- const [isShown, setIsShown] = useState17(false);
5401
- const [isLeaving, setIsLeaving] = useState17(false);
5402
- const ring = useRef14(null);
5403
- useEffect12(() => {
5456
+ const [isShown, setIsShown] = useState16(false);
5457
+ const [isLeaving, setIsLeaving] = useState16(false);
5458
+ const ring = useRef15(null);
5459
+ useEffect11(() => {
5404
5460
  let target = null;
5405
5461
  let frame = 0;
5406
5462
  let startedAt = 0;
@@ -5459,7 +5515,7 @@ function WidgetSpotlight({ selector }) {
5459
5515
  }, [selector]);
5460
5516
  if (!isShown || typeof document === "undefined") return null;
5461
5517
  return createPortal2(
5462
- /* @__PURE__ */ jsxs20(
5518
+ /* @__PURE__ */ jsxs21(
5463
5519
  "div",
5464
5520
  {
5465
5521
  ref: ring,
@@ -5473,7 +5529,9 @@ function WidgetSpotlight({ selector }) {
5473
5529
  {
5474
5530
  className: [
5475
5531
  "absolute -inset-1 rounded-lg outline-2 outline-offset-0 outline-primary",
5476
- "shadow-[0_0_0_6px_color-mix(in_oklch,var(--primary)_20%,transparent)]",
5532
+ // With the widget's own primary behind the host's, like every
5533
+ // other colour it draws.
5534
+ "shadow-[0_0_0_6px_color-mix(in_oklch,var(--primary,oklch(0.205_0_0))_20%,transparent)]",
5477
5535
  "transition-opacity duration-300 ease-out motion-reduce:transition-none",
5478
5536
  isLeaving ? "opacity-0" : "opacity-100"
5479
5537
  ].join(" ")
@@ -5497,7 +5555,7 @@ function WidgetSpotlight({ selector }) {
5497
5555
  }
5498
5556
 
5499
5557
  // ../../apps/web/src/components/support-widget/sdk/control.ts
5500
- import { useSyncExternalStore as useSyncExternalStore11 } from "react";
5558
+ import { useSyncExternalStore as useSyncExternalStore13 } from "react";
5501
5559
 
5502
5560
  // ../backend/convex/shared/customData.ts
5503
5561
  var MAX_CUSTOM_ENTRIES = 8;
@@ -5544,7 +5602,20 @@ function mergeCustomData(...sources) {
5544
5602
  }
5545
5603
 
5546
5604
  // ../../apps/web/src/components/support-widget/sdk/custom-data.ts
5605
+ import { useEffect as useEffect12 } from "react";
5547
5606
  var queued = [];
5607
+ var senders = 0;
5608
+ function useCustomDataSender() {
5609
+ useEffect12(() => {
5610
+ senders += 1;
5611
+ return () => {
5612
+ senders -= 1;
5613
+ };
5614
+ }, []);
5615
+ }
5616
+ function hasCustomDataSender() {
5617
+ return senders > 0;
5618
+ }
5548
5619
  function queueCustomData(label, value) {
5549
5620
  const cleaned = cleanCustomData([{ label, value }]);
5550
5621
  if (cleaned === void 0) return false;
@@ -5562,23 +5633,26 @@ function restoreCustomData(entries) {
5562
5633
  }
5563
5634
 
5564
5635
  // ../../apps/web/src/components/support-widget/sdk/open-store.ts
5565
- import { useCallback as useCallback8, useEffect as useEffect13, useSyncExternalStore as useSyncExternalStore7 } from "react";
5636
+ import { useCallback as useCallback8, useEffect as useEffect13, useRef as useRef16, useSyncExternalStore as useSyncExternalStore7 } from "react";
5566
5637
  var isDevelopment = typeof process !== "undefined" && process.env.NODE_ENV === "development";
5567
5638
  function createOpenStore(component) {
5568
5639
  let requested = null;
5569
5640
  let effective = false;
5570
5641
  let mounted = 0;
5571
- const listeners6 = /* @__PURE__ */ new Set();
5642
+ const mountedNames = [];
5643
+ const listeners7 = /* @__PURE__ */ new Set();
5644
+ const requestListeners = /* @__PURE__ */ new Set();
5645
+ let isReporting = false;
5572
5646
  function emit() {
5573
- for (const listener of listeners6) listener();
5647
+ for (const listener of listeners7) listener();
5574
5648
  }
5575
5649
  return {
5576
5650
  /** The component a warning should name, so it reads like the host's code. */
5577
5651
  component,
5578
5652
  subscribe(listener) {
5579
- listeners6.add(listener);
5653
+ listeners7.add(listener);
5580
5654
  return () => {
5581
- listeners6.delete(listener);
5655
+ listeners7.delete(listener);
5582
5656
  };
5583
5657
  },
5584
5658
  readRequested: () => requested,
@@ -5592,6 +5666,20 @@ function createOpenStore(component) {
5592
5666
  request(next) {
5593
5667
  requested = next;
5594
5668
  emit();
5669
+ if (isReporting) return;
5670
+ isReporting = true;
5671
+ try {
5672
+ for (const listener of requestListeners) listener(next);
5673
+ } finally {
5674
+ isReporting = false;
5675
+ }
5676
+ },
5677
+ /** Hears each request: a trigger, the hook, or the panel's own button. */
5678
+ onRequest(listener) {
5679
+ requestListeners.add(listener);
5680
+ return () => {
5681
+ requestListeners.delete(listener);
5682
+ };
5595
5683
  },
5596
5684
  /** Told, not asked: the panel saying what it settled on. */
5597
5685
  settle(next) {
@@ -5599,12 +5687,17 @@ function createOpenStore(component) {
5599
5687
  effective = next;
5600
5688
  emit();
5601
5689
  },
5602
- countMounted() {
5690
+ countMounted(name = component) {
5603
5691
  mounted += 1;
5692
+ mountedNames.push(name);
5604
5693
  return mounted;
5605
5694
  },
5606
- countUnmounted() {
5695
+ /** The components on the page, for a warning that names them. */
5696
+ mountedNames: () => [...mountedNames],
5697
+ countUnmounted(name = component) {
5607
5698
  mounted -= 1;
5699
+ const at2 = mountedNames.indexOf(name);
5700
+ if (at2 !== -1) mountedNames.splice(at2, 1);
5608
5701
  if (mounted === 0 && effective) {
5609
5702
  effective = false;
5610
5703
  emit();
@@ -5645,24 +5738,48 @@ function useOpenState(store3, {
5645
5738
  useEffect13(() => {
5646
5739
  store3.settle(isOpen);
5647
5740
  }, [store3, isOpen]);
5741
+ const reportRef = useRef16(onOpenChange);
5742
+ useEffect13(() => {
5743
+ reportRef.current = onOpenChange;
5744
+ });
5745
+ useEffect13(
5746
+ () => store3.onRequest((next) => reportRef.current?.(next)),
5747
+ [store3]
5748
+ );
5648
5749
  const setOpen = useCallback8(
5649
5750
  (next) => {
5650
5751
  store3.request(next);
5651
- onOpenChange?.(next);
5652
5752
  },
5653
- [store3, onOpenChange]
5753
+ [store3]
5654
5754
  );
5655
5755
  return [isOpen, setOpen];
5656
5756
  }
5657
- function useRegister(store3) {
5757
+ function useRegister(store3, name = store3.component) {
5658
5758
  useEffect13(() => {
5659
- if (store3.countMounted() > 1 && isDevelopment) {
5759
+ if (store3.countMounted(name) > 1 && isDevelopment) {
5760
+ const names = [...new Set(store3.mountedNames())];
5761
+ const which = names.length === 1 ? `more than one <${names[0]}> is mounted` : `${names.map((one) => `<${one}>`).join(" and ")} are both mounted`;
5660
5762
  console.warn(
5661
- `Median: more than one <${store3.component}> is mounted. They share one open state and open together. Mount it once, near the root of your app.`
5763
+ `Median: ${which}. They share one open state and open together. Mount one, once, near the root of your app.`
5662
5764
  );
5663
5765
  }
5664
- return () => store3.countUnmounted();
5665
- }, [store3]);
5766
+ return () => store3.countUnmounted(name);
5767
+ }, [store3, name]);
5768
+ }
5769
+
5770
+ // ../backend/convex/shared/mutationResult.ts
5771
+ import { ConvexError } from "convex/values";
5772
+ function unwrapResult(result) {
5773
+ if (result.auditResult === "error") throw new ConvexError(result.data);
5774
+ return result.value;
5775
+ }
5776
+
5777
+ // ../backend/client/mutationResult.ts
5778
+ function unwrapMutationResult(result) {
5779
+ if (result && typeof result === "object" && "auditResult" in result && (result.auditResult === "ok" && "value" in result || result.auditResult === "error" && "data" in result)) {
5780
+ return unwrapResult(result);
5781
+ }
5782
+ return result;
5666
5783
  }
5667
5784
 
5668
5785
  // ../backend/convex/_generated/api.js
@@ -5702,6 +5819,33 @@ function cleanCrash(input) {
5702
5819
  import { ConvexHttpClient } from "convex/browser";
5703
5820
  import { useSyncExternalStore as useSyncExternalStore10 } from "react";
5704
5821
 
5822
+ // ../../apps/web/src/lib/upload.ts
5823
+ async function uploadFile(getUploadUrl, file) {
5824
+ const url = await getUploadUrl();
5825
+ const response = await fetch(url, {
5826
+ method: "POST",
5827
+ headers: { "Content-Type": file.type || "application/octet-stream" },
5828
+ body: file
5829
+ });
5830
+ if (!response.ok) {
5831
+ throw new Error(`Uploading ${file.name} failed with ${response.status}`);
5832
+ }
5833
+ const { storageId } = await response.json();
5834
+ return storageId;
5835
+ }
5836
+ async function uploadAttachments(getUploadUrl, files) {
5837
+ const attachments = [];
5838
+ for (const file of files) {
5839
+ attachments.push({
5840
+ storageId: await uploadFile(getUploadUrl, file),
5841
+ name: file.name,
5842
+ size: file.size,
5843
+ type: file.type || "application/octet-stream"
5844
+ });
5845
+ }
5846
+ return attachments;
5847
+ }
5848
+
5705
5849
  // ../../apps/web/src/components/support-widget/sdk/context.ts
5706
5850
  var BROWSERS = [
5707
5851
  { name: "Edge", token: /Edg(?:e|A|iOS)?\/(\d+)/ },
@@ -5867,7 +6011,7 @@ function cleanClientDiagnostics(input) {
5867
6011
  }
5868
6012
 
5869
6013
  // ../../apps/web/src/components/support-widget/sdk/diagnostics.ts
5870
- import { useCallback as useCallback9, useEffect as useEffect14, useRef as useRef15 } from "react";
6014
+ import { useCallback as useCallback9, useEffect as useEffect14, useRef as useRef17 } from "react";
5871
6015
  function settingsOf(option) {
5872
6016
  if (option === void 0 || option === false) return null;
5873
6017
  if (option === true) return { errors: true, network: false, console: false };
@@ -5897,9 +6041,8 @@ function messageOf(value) {
5897
6041
  function stackOf(value) {
5898
6042
  return value instanceof Error && typeof value.stack === "string" ? value.stack.slice(0, STACK_MAX_LENGTH) : void 0;
5899
6043
  }
5900
- function collect(settings) {
5901
- const undo = [];
5902
- if (settings.errors) {
6044
+ var INSTALLERS = {
6045
+ errors: () => {
5903
6046
  const onError = (event) => {
5904
6047
  if (!event.error && !event.message) return;
5905
6048
  record({
@@ -5925,12 +6068,14 @@ function collect(settings) {
5925
6068
  };
5926
6069
  window.addEventListener("error", onError);
5927
6070
  window.addEventListener("unhandledrejection", onRejection);
5928
- undo.push(() => {
6071
+ return () => {
5929
6072
  window.removeEventListener("error", onError);
5930
6073
  window.removeEventListener("unhandledrejection", onRejection);
5931
- });
5932
- }
5933
- if (settings.network && typeof window.fetch === "function") {
6074
+ };
6075
+ },
6076
+ network: () => {
6077
+ if (typeof window.fetch !== "function") return () => {
6078
+ };
5934
6079
  const original = window.fetch;
5935
6080
  window.fetch = async function medianFetch(...args) {
5936
6081
  const [input, init] = args;
@@ -5964,11 +6109,11 @@ function collect(settings) {
5964
6109
  throw thrown;
5965
6110
  }
5966
6111
  };
5967
- undo.push(() => {
6112
+ return () => {
5968
6113
  window.fetch = original;
5969
- });
5970
- }
5971
- if (settings.console) {
6114
+ };
6115
+ },
6116
+ console: () => {
5972
6117
  const original = console.error;
5973
6118
  console.error = function medianConsoleError(...args) {
5974
6119
  record({
@@ -5979,12 +6124,32 @@ function collect(settings) {
5979
6124
  });
5980
6125
  original.apply(console, args);
5981
6126
  };
5982
- undo.push(() => {
6127
+ return () => {
5983
6128
  console.error = original;
5984
- });
6129
+ };
6130
+ }
6131
+ };
6132
+ var wanted = {
6133
+ errors: 0,
6134
+ network: 0,
6135
+ console: 0
6136
+ };
6137
+ var removers = {};
6138
+ function collect(settings) {
6139
+ const kinds = Object.keys(settings).filter(
6140
+ (kind) => settings[kind]
6141
+ );
6142
+ for (const kind of kinds) {
6143
+ wanted[kind] += 1;
6144
+ if (wanted[kind] === 1) removers[kind] = INSTALLERS[kind]();
5985
6145
  }
5986
6146
  return () => {
5987
- for (const off of undo) off();
6147
+ for (const kind of kinds) {
6148
+ wanted[kind] -= 1;
6149
+ if (wanted[kind] > 0) continue;
6150
+ removers[kind]?.();
6151
+ delete removers[kind];
6152
+ }
5988
6153
  };
5989
6154
  }
5990
6155
  function snapshot(state) {
@@ -5998,24 +6163,37 @@ function snapshot(state) {
5998
6163
  });
5999
6164
  }
6000
6165
  function snapshotNow(extraState) {
6001
- return snapshot({ ...latestState, ...extraState });
6166
+ const merged = { ...extraState };
6167
+ for (const [key, value] of Object.entries(latestState ?? {})) {
6168
+ if (!Object.prototype.hasOwnProperty.call(merged, key)) {
6169
+ merged[key] = value;
6170
+ }
6171
+ }
6172
+ return snapshot(merged);
6002
6173
  }
6003
6174
  function useDiagnostics(option, appState) {
6004
6175
  const settings = settingsOf(option);
6005
6176
  const key = settings === null ? null : JSON.stringify(settings);
6006
- const stateRef = useRef15(appState);
6177
+ const stateRef = useRef17(appState);
6178
+ const shared = useRef17(void 0);
6007
6179
  useEffect14(() => {
6008
6180
  stateRef.current = appState;
6009
- latestState = appState;
6181
+ if (appState !== void 0) {
6182
+ latestState = appState;
6183
+ } else if (shared.current !== void 0 && latestState === shared.current) {
6184
+ latestState = void 0;
6185
+ }
6186
+ shared.current = appState;
6010
6187
  });
6011
- const sentAt = useRef15(null);
6188
+ const sentAt = useRef17(null);
6012
6189
  useEffect14(() => {
6013
6190
  if (key === null) return;
6014
6191
  return collect(JSON.parse(key));
6015
6192
  }, [key]);
6016
6193
  const take = useCallback9(() => {
6017
6194
  if (key === null) return void 0;
6018
- if (sentAt.current !== null && sentAt.current === recorded) return void 0;
6195
+ if (sentAt.current !== null && sentAt.current === recorded)
6196
+ return void 0;
6019
6197
  sentAt.current = recorded;
6020
6198
  return snapshot(stateRef.current);
6021
6199
  }, [key]);
@@ -6051,10 +6229,117 @@ function useRememberInstall(apiKey, user) {
6051
6229
  });
6052
6230
  }
6053
6231
 
6232
+ // ../../apps/web/src/components/support-widget/sdk/screenshot.ts
6233
+ var MAX_WIDTH = 1440;
6234
+ var TIMEOUT_MS = 6e3;
6235
+ var TYPE = "image/jpeg";
6236
+ var QUALITY = 0.85;
6237
+ var SCREENSHOT_NAME = "screenshot.jpg";
6238
+ var PIN_ATTRIBUTE = "data-median-pin";
6239
+ async function capturePage() {
6240
+ if (typeof window === "undefined" || typeof document === "undefined") {
6241
+ return null;
6242
+ }
6243
+ const page = document.documentElement;
6244
+ if (page === null) return null;
6245
+ try {
6246
+ const blob = await Promise.race([
6247
+ draw(page),
6248
+ new Promise((resolve) => setTimeout(() => resolve(null), TIMEOUT_MS))
6249
+ ]);
6250
+ if (blob === null) return null;
6251
+ return new File([blob], SCREENSHOT_NAME, { type: TYPE });
6252
+ } catch (caught) {
6253
+ console.warn("Median: taking a screenshot for this report failed", caught);
6254
+ return null;
6255
+ }
6256
+ }
6257
+ async function draw(page) {
6258
+ const { domToBlob } = await import("./dist-VC6ZVTA2.js");
6259
+ const width = page.clientWidth || window.innerWidth;
6260
+ const height = page.clientHeight || window.innerHeight;
6261
+ const unpin = pinFixed();
6262
+ try {
6263
+ return await domToBlob(page, {
6264
+ width,
6265
+ height,
6266
+ // Their view, not the whole document: the page is drawn in full and
6267
+ // the part they are looking at is what the frame is put around. A page
6268
+ // held still by `usePageLock` has already been scrolled to nothing,
6269
+ // and is shifted by nothing here.
6270
+ style: {
6271
+ transform: `translate(${-window.scrollX}px, ${-window.scrollY}px)`,
6272
+ transformOrigin: "top left"
6273
+ },
6274
+ scale: Math.min(1, MAX_WIDTH / Math.max(width, 1)),
6275
+ type: TYPE,
6276
+ quality: QUALITY,
6277
+ // JPEG has no transparency, so anything clear would come out black.
6278
+ backgroundColor: pageBackground(),
6279
+ filter: notOurs,
6280
+ onCloneNode: placePinned,
6281
+ timeout: TIMEOUT_MS,
6282
+ features: { copyScrollbar: false }
6283
+ });
6284
+ } finally {
6285
+ unpin();
6286
+ }
6287
+ }
6288
+ function pinFixed() {
6289
+ const pinned = [];
6290
+ for (const element of document.body.querySelectorAll("*")) {
6291
+ if (window.getComputedStyle(element).position !== "fixed") continue;
6292
+ const box = element.getBoundingClientRect();
6293
+ if (box.width === 0 || box.height === 0) continue;
6294
+ element.setAttribute(
6295
+ PIN_ATTRIBUTE,
6296
+ [box.left, box.top, box.width, box.height].join(",")
6297
+ );
6298
+ pinned.push(element);
6299
+ }
6300
+ return () => {
6301
+ for (const element of pinned) element.removeAttribute(PIN_ATTRIBUTE);
6302
+ };
6303
+ }
6304
+ function placePinned(clone) {
6305
+ if (!(clone instanceof Element)) return;
6306
+ for (const element of clone.querySelectorAll(`[${PIN_ATTRIBUTE}]`)) {
6307
+ const [left, top, width, height] = (element.getAttribute(PIN_ATTRIBUTE) ?? "").split(",").map(Number);
6308
+ element.removeAttribute(PIN_ATTRIBUTE);
6309
+ if (!(element instanceof HTMLElement) || Number.isNaN(top)) continue;
6310
+ element.style.setProperty("left", `${left + window.scrollX}px`, "important");
6311
+ element.style.setProperty("top", `${top + window.scrollY}px`, "important");
6312
+ element.style.setProperty("right", "auto", "important");
6313
+ element.style.setProperty("bottom", "auto", "important");
6314
+ element.style.setProperty("width", `${width}px`, "important");
6315
+ element.style.setProperty("height", `${height}px`, "important");
6316
+ element.style.setProperty("margin", "0", "important");
6317
+ }
6318
+ }
6319
+ function notOurs(node) {
6320
+ return !(node instanceof Element && node.hasAttribute("data-median-widget"));
6321
+ }
6322
+ function pageBackground() {
6323
+ for (const element of [document.body, document.documentElement]) {
6324
+ if (element === null) continue;
6325
+ const color = window.getComputedStyle(element).backgroundColor;
6326
+ if (color && !isClear(color)) return color;
6327
+ }
6328
+ return "#ffffff";
6329
+ }
6330
+ function isClear(color) {
6331
+ const clear = color.replace(/\s/g, "");
6332
+ return clear === "transparent" || clear === "rgba(0,0,0,0)";
6333
+ }
6334
+
6054
6335
  // ../../apps/web/src/components/support-widget/sdk/session.ts
6055
6336
  var STORAGE_PREFIX = "median:session:";
6056
6337
  var inMemorySessions = /* @__PURE__ */ new Map();
6057
6338
  var listeners4 = /* @__PURE__ */ new Set();
6339
+ var resetCount = 0;
6340
+ function getResetCount() {
6341
+ return resetCount;
6342
+ }
6058
6343
  function subscribeSession(listener) {
6059
6344
  listeners4.add(listener);
6060
6345
  return () => {
@@ -6077,6 +6362,7 @@ function getOrCreateSessionId(apiKey) {
6077
6362
  }
6078
6363
  }
6079
6364
  function resetSessions() {
6365
+ resetCount += 1;
6080
6366
  inMemorySessions.clear();
6081
6367
  if (typeof window !== "undefined") {
6082
6368
  try {
@@ -6093,19 +6379,32 @@ function resetSessions() {
6093
6379
  }
6094
6380
 
6095
6381
  // ../../apps/web/src/components/support-widget/sdk/use-median-thread.ts
6096
- import { useMutation, useQuery } from "convex/react";
6382
+ import { useMutation as useMutation2, useQuery } from "convex/react";
6383
+
6384
+ // ../../apps/web/src/components/support-widget/sdk/use-widget-mutation.ts
6385
+ import { useMutation } from "convex/react";
6386
+ import { useCallback as useCallback10 } from "react";
6387
+ function useWidgetMutation(reference) {
6388
+ const mutation = useMutation(reference);
6389
+ return useCallback10(
6390
+ async (...args) => unwrapMutationResult(await mutation(...args)),
6391
+ [mutation]
6392
+ );
6393
+ }
6394
+
6395
+ // ../../apps/web/src/components/support-widget/sdk/use-median-thread.ts
6097
6396
  import {
6098
- useCallback as useCallback11,
6099
- useEffect as useEffect17,
6397
+ useCallback as useCallback12,
6398
+ useEffect as useEffect18,
6100
6399
  useMemo as useMemo2,
6101
- useState as useState19,
6400
+ useState as useState18,
6102
6401
  useSyncExternalStore as useSyncExternalStore9
6103
6402
  } from "react";
6104
6403
 
6105
6404
  // ../../apps/web/src/hooks/use-now.ts
6106
- import { useEffect as useEffect16, useState as useState18 } from "react";
6405
+ import { useEffect as useEffect16, useState as useState17 } from "react";
6107
6406
  function useNow(intervalMs = 3e4) {
6108
- const [now, setNow] = useState18(() => Date.now());
6407
+ const [now, setNow] = useState17(() => Date.now());
6109
6408
  useEffect16(() => {
6110
6409
  const timer = setInterval(() => setNow(Date.now()), intervalMs);
6111
6410
  return () => clearInterval(timer);
@@ -6114,16 +6413,21 @@ function useNow(intervalMs = 3e4) {
6114
6413
  }
6115
6414
 
6116
6415
  // ../../apps/web/src/lib/errors.ts
6117
- import { ConvexError } from "convex/values";
6416
+ import { ConvexError as ConvexError2 } from "convex/values";
6118
6417
  var FALLBACK = "Something went wrong on our end. Please try again.";
6418
+ var OUT_OF_DATE = "This page is out of date. Refresh to get the latest version.";
6419
+ function isMissingFunction(error) {
6420
+ return error instanceof Error && error.message.includes("Could not find public function");
6421
+ }
6119
6422
  function isErrorPayload(data) {
6120
6423
  return typeof data === "object" && data !== null && "message" in data && typeof data.message === "string";
6121
6424
  }
6122
6425
  function errorMessage(error) {
6123
- if (error instanceof ConvexError) {
6426
+ if (error instanceof ConvexError2) {
6124
6427
  if (typeof error.data === "string") return error.data;
6125
6428
  if (isErrorPayload(error.data)) return error.data.message;
6126
6429
  }
6430
+ if (isMissingFunction(error)) return OUT_OF_DATE;
6127
6431
  return FALLBACK;
6128
6432
  }
6129
6433
 
@@ -6161,33 +6465,6 @@ function isNewDay(previous, next) {
6161
6465
  return startOfDay(previous) !== startOfDay(next);
6162
6466
  }
6163
6467
 
6164
- // ../../apps/web/src/lib/upload.ts
6165
- async function uploadFile(getUploadUrl, file) {
6166
- const url = await getUploadUrl();
6167
- const response = await fetch(url, {
6168
- method: "POST",
6169
- headers: { "Content-Type": file.type || "application/octet-stream" },
6170
- body: file
6171
- });
6172
- if (!response.ok) {
6173
- throw new Error(`Uploading ${file.name} failed with ${response.status}`);
6174
- }
6175
- const { storageId } = await response.json();
6176
- return storageId;
6177
- }
6178
- async function uploadAttachments(getUploadUrl, files) {
6179
- const attachments = [];
6180
- for (const file of files) {
6181
- attachments.push({
6182
- storageId: await uploadFile(getUploadUrl, file),
6183
- name: file.name,
6184
- size: file.size,
6185
- type: file.type || "application/octet-stream"
6186
- });
6187
- }
6188
- return attachments;
6189
- }
6190
-
6191
6468
  // ../../apps/web/src/components/support-widget/sdk/rating-skips.ts
6192
6469
  var STORAGE_PREFIX2 = "median:rating-skips:";
6193
6470
  var MAX_REMEMBERED = 20;
@@ -6222,45 +6499,56 @@ function writeSkippedRating(apiKey, conversationId) {
6222
6499
  }
6223
6500
  }
6224
6501
 
6225
- // ../../apps/web/src/components/support-widget/sdk/stamps.ts
6226
- var inMemory2 = /* @__PURE__ */ new Map();
6227
- function keyOf(name, apiKey) {
6228
- return `median:${name}:${apiKey}`;
6229
- }
6230
- function readStamp(name, apiKey) {
6231
- if (typeof window === "undefined") return 0;
6232
- const key = keyOf(name, apiKey);
6233
- try {
6234
- const stored = window.localStorage.getItem(key);
6235
- const at2 = stored === null ? Number.NaN : Number(stored);
6236
- return Number.isFinite(at2) ? at2 : 0;
6237
- } catch {
6238
- return inMemory2.get(key) ?? 0;
6239
- }
6502
+ // ../../apps/web/src/components/support-widget/sdk/read-state.ts
6503
+ function isUnreadReply(message) {
6504
+ return message.sender === "agent" && !message.pending && !message.isRead;
6240
6505
  }
6241
- function writeStamp(name, apiKey, at2) {
6242
- if (typeof window === "undefined") return;
6243
- const key = keyOf(name, apiKey);
6244
- inMemory2.set(key, at2);
6245
- try {
6246
- window.localStorage.setItem(key, String(at2));
6247
- } catch {
6506
+ function widgetReadReceipts(messages, now) {
6507
+ const receipts = /* @__PURE__ */ new Map();
6508
+ const blocked = /* @__PURE__ */ new Set();
6509
+ for (const message of messages) {
6510
+ const { conversationId } = message;
6511
+ if (!receipts.has(conversationId)) {
6512
+ receipts.set(conversationId, { conversationId });
6513
+ }
6514
+ if (blocked.has(conversationId)) continue;
6515
+ if (message.pending) {
6516
+ if (message.typingAt === null || now - message.typingAt <= TYPING_TTL_MS) {
6517
+ blocked.add(conversationId);
6518
+ }
6519
+ continue;
6520
+ }
6521
+ if (message.sender === "agent" && (message.body.trim() || message.attachments.length > 0)) {
6522
+ receipts.set(conversationId, { conversationId, messageId: message._id });
6523
+ }
6248
6524
  }
6525
+ return [...receipts.values()];
6249
6526
  }
6250
6527
 
6251
- // ../../apps/web/src/components/support-widget/sdk/read-state.ts
6252
- var READ = "read";
6253
- function readLastSeenAt(apiKey) {
6254
- return readStamp(READ, apiKey);
6255
- }
6256
- function writeLastSeenAt(apiKey, at2) {
6257
- writeStamp(READ, apiKey, at2);
6528
+ // ../../apps/web/src/components/support-widget/sdk/use-screenshot-ask.ts
6529
+ import { useEffect as useEffect17, useRef as useRef18 } from "react";
6530
+ function useScreenshotAsk({
6531
+ ask,
6532
+ enabled: enabled2,
6533
+ answer
6534
+ }) {
6535
+ const answered = useRef18(/* @__PURE__ */ new Set());
6536
+ useEffect17(() => {
6537
+ if (ask === null || answered.current.has(ask.key)) return;
6538
+ answered.current.add(ask.key);
6539
+ void (async () => {
6540
+ const shot = enabled2 ? await capturePage() : null;
6541
+ await answer(ask.key, shot);
6542
+ })().catch((caught) => {
6543
+ console.warn("Median: answering the agent's request for a picture failed", caught);
6544
+ });
6545
+ }, [ask, enabled2, answer]);
6258
6546
  }
6259
6547
 
6260
6548
  // ../../apps/web/src/components/support-widget/sdk/visitor-email.ts
6261
- import { useCallback as useCallback10, useSyncExternalStore as useSyncExternalStore8 } from "react";
6549
+ import { useCallback as useCallback11, useSyncExternalStore as useSyncExternalStore8 } from "react";
6262
6550
  var STORAGE_PREFIX3 = "median:email:";
6263
- var inMemory3 = /* @__PURE__ */ new Map();
6551
+ var inMemory2 = /* @__PURE__ */ new Map();
6264
6552
  var listeners5 = /* @__PURE__ */ new Set();
6265
6553
  function subscribeVisitorEmail(listener) {
6266
6554
  listeners5.add(listener);
@@ -6274,11 +6562,11 @@ function readVisitorEmail(apiKey) {
6274
6562
  const stored = window.localStorage.getItem(`${STORAGE_PREFIX3}${apiKey}`);
6275
6563
  return stored && stored.length > 0 ? stored : null;
6276
6564
  } catch {
6277
- return inMemory3.get(apiKey) ?? null;
6565
+ return inMemory2.get(apiKey) ?? null;
6278
6566
  }
6279
6567
  }
6280
6568
  function writeVisitorEmail(apiKey, email) {
6281
- inMemory3.set(apiKey, email);
6569
+ inMemory2.set(apiKey, email);
6282
6570
  try {
6283
6571
  window.localStorage.setItem(`${STORAGE_PREFIX3}${apiKey}`, email);
6284
6572
  } catch {
@@ -6286,7 +6574,7 @@ function writeVisitorEmail(apiKey, email) {
6286
6574
  for (const listener of listeners5) listener();
6287
6575
  }
6288
6576
  function resetVisitorEmails() {
6289
- inMemory3.clear();
6577
+ inMemory2.clear();
6290
6578
  if (typeof window !== "undefined") {
6291
6579
  try {
6292
6580
  const stale = [];
@@ -6303,10 +6591,10 @@ function resetVisitorEmails() {
6303
6591
  function useVisitorEmail(apiKey) {
6304
6592
  const email = useSyncExternalStore8(
6305
6593
  subscribeVisitorEmail,
6306
- useCallback10(() => readVisitorEmail(apiKey), [apiKey]),
6594
+ useCallback11(() => readVisitorEmail(apiKey), [apiKey]),
6307
6595
  () => null
6308
6596
  );
6309
- const setEmail = useCallback10(
6597
+ const setEmail = useCallback11(
6310
6598
  (next) => writeVisitorEmail(apiKey, next),
6311
6599
  [apiKey]
6312
6600
  );
@@ -6333,10 +6621,20 @@ var IDENTITY_COMPLAINTS = {
6333
6621
  unverified: "Median: the user hash passed to <MedianSupport> did not match. Sign the same string you pass as `user.id` with the Median key that produced this widget id. Until it matches, this visitor gets a conversation of their own on every device.",
6334
6622
  unconfigured: "Median: a user hash was passed to <MedianSupport>, but its widget id and hash came from different Median keys. Derive both from the same key."
6335
6623
  };
6336
- function useMedianThread(apiKey, user, telemetry = true, isLocked = false, diagnostics, appState, requireEmail = true, messagePreview = true) {
6624
+ function useMedianThread({
6625
+ apiKey,
6626
+ user,
6627
+ telemetry = true,
6628
+ screenshot = true,
6629
+ isLocked = false,
6630
+ diagnostics,
6631
+ appState,
6632
+ requireEmail = true,
6633
+ messagePreview = true
6634
+ }) {
6337
6635
  const sessionId = useSyncExternalStore9(
6338
6636
  subscribeSession,
6339
- useCallback11(() => getOrCreateSessionId(apiKey), [apiKey]),
6637
+ useCallback12(() => getOrCreateSessionId(apiKey), [apiKey]),
6340
6638
  () => null
6341
6639
  );
6342
6640
  const config = useQuery(api.widget.config, { apiKey });
@@ -6346,18 +6644,45 @@ function useMedianThread(apiKey, user, telemetry = true, isLocked = false, diagn
6346
6644
  api.widget.thread,
6347
6645
  sessionId === null || isLocked ? "skip" : { apiKey, sessionId }
6348
6646
  );
6349
- const send = useMutation(api.widget.send);
6350
- const sendTyping = useMutation(api.widget.setTyping);
6351
- const sendPresence = useMutation(api.widget.presence);
6352
- const rateConversation = useMutation(api.widget.rate);
6353
- const identify = useMutation(api.widget.identify);
6354
- const generateUploadUrl = useMutation(api.widget.generateUploadUrl);
6647
+ const send = useWidgetMutation(api.widget.sendV2);
6648
+ const sendTyping = useMutation2(api.widget.setTyping);
6649
+ const sendPresence = useMutation2(api.widget.presence);
6650
+ const rateConversation = useWidgetMutation(api.widget.rateV2);
6651
+ const identify = useWidgetMutation(api.widget.identifyV2);
6652
+ const generateUploadUrl = useWidgetMutation(api.widget.generateUploadUrlV2);
6653
+ const answerScreenshot = useWidgetMutation(api.widget.answerScreenshotV2);
6654
+ const answerAsk = useCallback12(
6655
+ async (key, shot) => {
6656
+ if (sessionId === null) return;
6657
+ let attachment;
6658
+ if (shot !== null) {
6659
+ try {
6660
+ [attachment] = await uploadAttachments(
6661
+ () => generateUploadUrl({ apiKey, sessionId }),
6662
+ [shot]
6663
+ );
6664
+ } catch (caught) {
6665
+ console.warn(
6666
+ "Median: the picture the agent asked for could not be uploaded",
6667
+ caught
6668
+ );
6669
+ }
6670
+ }
6671
+ await answerScreenshot({
6672
+ apiKey,
6673
+ sessionId,
6674
+ key,
6675
+ ...attachment === void 0 ? {} : { attachment }
6676
+ });
6677
+ },
6678
+ [apiKey, sessionId, generateUploadUrl, answerScreenshot]
6679
+ );
6355
6680
  const identityKey = user ? JSON.stringify(user) : null;
6356
6681
  const identity = useMemo2(
6357
6682
  () => identityKey === null ? null : JSON.parse(identityKey),
6358
6683
  [identityKey]
6359
6684
  );
6360
- useEffect17(() => {
6685
+ useEffect18(() => {
6361
6686
  if (sessionId === null || identity === null) return;
6362
6687
  const { facts, claim } = splitIdentity(identity);
6363
6688
  identify({
@@ -6376,8 +6701,8 @@ function useMedianThread(apiKey, user, telemetry = true, isLocked = false, diagn
6376
6701
  ) ?? false;
6377
6702
  const now = useNow(hasLiveDots ? 1e3 : 6e4);
6378
6703
  const takeDiagnostics = useDiagnostics(diagnostics, appState);
6379
- const [isSending, setIsSending] = useState19(false);
6380
- const [sendError, setSendError] = useState19(null);
6704
+ const [isSending, setIsSending] = useState18(false);
6705
+ const [sendError, setSendError] = useState18(null);
6381
6706
  const agent = useMemo2(
6382
6707
  () => config === void 0 ? null : {
6383
6708
  id: "median-agent",
@@ -6450,17 +6775,16 @@ function useMedianThread(apiKey, user, telemetry = true, isLocked = false, diagn
6450
6775
  team: [...[...humans.values()].slice(-2), agent]
6451
6776
  };
6452
6777
  }, [config, agent, messages]);
6453
- const [lastSeenAt, setLastSeenAt] = useState19(() => readLastSeenAt(apiKey));
6454
- const unread = useMemo2(
6455
- () => messages.filter(
6456
- (message) => message.sender === "agent" && !message.pending && (message.sentAt ?? 0) > lastSeenAt
6457
- ),
6458
- [messages, lastSeenAt]
6459
- );
6778
+ const unread = useMemo2(() => {
6779
+ const unreadIds = new Set(
6780
+ thread?.messages.filter(isUnreadReply).map((message) => message._id)
6781
+ );
6782
+ return messages.filter((message) => unreadIds.has(message.id));
6783
+ }, [messages, thread]);
6460
6784
  const unreadCount = unread.length;
6461
6785
  const latestUnread = messagePreview && (config?.messagePreview ?? true) ? unread[unread.length - 1] ?? null : null;
6462
- const [picked, setPicked] = useState19(null);
6463
- const [skipped, setSkipped] = useState19(
6786
+ const [picked, setPicked] = useState18(null);
6787
+ const [skipped, setSkipped] = useState18(
6464
6788
  () => readSkippedRatings(apiKey)
6465
6789
  );
6466
6790
  const conversationId = thread?.conversationId ?? null;
@@ -6484,15 +6808,6 @@ function useMedianThread(apiKey, user, telemetry = true, isLocked = false, diagn
6484
6808
  setSkipped((current2) => new Set(current2).add(conversationId));
6485
6809
  }
6486
6810
  } : null;
6487
- const markSeen = useCallback11(() => {
6488
- const newest = messages.reduce(
6489
- (latest, message) => Math.max(latest, message.sentAt ?? 0),
6490
- 0
6491
- );
6492
- if (newest <= lastSeenAt) return;
6493
- writeLastSeenAt(apiKey, newest);
6494
- setLastSeenAt(newest);
6495
- }, [apiKey, messages, lastSeenAt]);
6496
6811
  async function handleSend(body, files) {
6497
6812
  if (sessionId === null || isSending) return;
6498
6813
  setSendError(null);
@@ -6534,12 +6849,29 @@ function useMedianThread(apiKey, user, telemetry = true, isLocked = false, diagn
6534
6849
  sendTyping({ apiKey, sessionId, isTyping }).catch(() => {
6535
6850
  });
6536
6851
  }
6537
- const reportPresence = useCallback11(() => {
6852
+ const readsKey = JSON.stringify(
6853
+ widgetReadReceipts(thread?.messages ?? [], now)
6854
+ );
6855
+ const reads = useMemo2(
6856
+ () => JSON.parse(readsKey),
6857
+ [readsKey]
6858
+ );
6859
+ const reportPresence = useCallback12(() => {
6538
6860
  if (sessionId === null || isLocked) return;
6539
- sendPresence({ apiKey, sessionId }).catch(() => {
6861
+ sendPresence({
6862
+ apiKey,
6863
+ sessionId,
6864
+ reads
6865
+ }).catch((caught) => {
6866
+ console.warn("Median: support read status could not be synced", caught);
6540
6867
  });
6541
- }, [apiKey, sessionId, isLocked, sendPresence]);
6868
+ }, [apiKey, sessionId, isLocked, sendPresence, reads]);
6542
6869
  const ask = thread?.direction ?? null;
6870
+ useScreenshotAsk({
6871
+ ask: thread?.screenshotAsk ?? null,
6872
+ enabled: screenshot,
6873
+ answer: answerAsk
6874
+ });
6543
6875
  return {
6544
6876
  presence,
6545
6877
  messages,
@@ -6574,7 +6906,6 @@ function useMedianThread(apiKey, user, telemetry = true, isLocked = false, diagn
6574
6906
  unreadCount,
6575
6907
  /** The newest of those, for the card on the closed button. */
6576
6908
  latestUnread,
6577
- markSeen,
6578
6909
  /** Say the panel is on screen, so replies are not also emailed. */
6579
6910
  reportPresence,
6580
6911
  sendError,
@@ -6649,17 +6980,19 @@ async function reportError(error, options = {}) {
6649
6980
  const diagnostics = snapshotNow({ ...factsOf(crash), ...options.state });
6650
6981
  try {
6651
6982
  client ??= new ConvexHttpClient(MEDIAN_DEPLOYMENT_URL);
6652
- const outcome = await client.mutation(api.feedback.reportError, {
6983
+ const images = options.screenshot === false ? [] : await screenshotOf(client, apiKey, sessionId);
6984
+ const outcome = await client.mutation(api.feedback.reportErrorV2, {
6653
6985
  apiKey,
6654
6986
  sessionId,
6655
6987
  error: crash,
6988
+ ...images.length > 0 ? { images } : {},
6656
6989
  ...user ? { user: facts } : {},
6657
6990
  ...claim ? { identity: claim } : {},
6658
6991
  ...context ? { context } : {},
6659
6992
  ...page ? { page } : {},
6660
6993
  ...diagnostics ? { diagnostics } : {},
6661
6994
  ...custom ? { customData: custom } : {}
6662
- });
6995
+ }).then(unwrapMutationResult);
6663
6996
  if (outcome === "off") {
6664
6997
  if (custom) restoreCustomData(custom);
6665
6998
  return { reported: false, reason: REASONS.off };
@@ -6671,6 +7004,22 @@ async function reportError(error, options = {}) {
6671
7004
  return { reported: false, reason: REASONS.failed };
6672
7005
  }
6673
7006
  }
7007
+ async function screenshotOf(client3, apiKey, sessionId) {
7008
+ try {
7009
+ const shot = await capturePage();
7010
+ if (shot === null) return [];
7011
+ return await uploadAttachments(
7012
+ () => client3.mutation(api.widget.generateUploadUrlV2, { apiKey, sessionId }).then(unwrapMutationResult),
7013
+ [shot]
7014
+ );
7015
+ } catch (caught) {
7016
+ console.warn(
7017
+ "Median: the crash report's screenshot could not be sent",
7018
+ caught
7019
+ );
7020
+ return [];
7021
+ }
7022
+ }
6674
7023
  function useCanReportError() {
6675
7024
  const isInstalled = useSyncExternalStore10(
6676
7025
  subscribeInstall,
@@ -6683,6 +7032,77 @@ function useCanReportError() {
6683
7032
  return isInstalled && Boolean(MEDIAN_DEPLOYMENT_URL);
6684
7033
  }
6685
7034
 
7035
+ // ../../apps/web/src/components/support-widget/sdk/signed-out.ts
7036
+ import { useEffect as useEffect19, useRef as useRef19, useState as useState19, useSyncExternalStore as useSyncExternalStore11 } from "react";
7037
+ var held = /* @__PURE__ */ new Map();
7038
+ var signedOut = /* @__PURE__ */ new Set();
7039
+ var RELEASE_AFTER_MS = 2e3;
7040
+ function signOutHeldUsers() {
7041
+ for (const person of held.values()) signedOut.add(person);
7042
+ }
7043
+ function releaseLater(person) {
7044
+ if (!signedOut.has(person)) return;
7045
+ setTimeout(() => {
7046
+ for (const other of held.values()) if (other === person) return;
7047
+ signedOut.delete(person);
7048
+ }, RELEASE_AFTER_MS);
7049
+ }
7050
+ function personKey(user) {
7051
+ if (user === void 0) return null;
7052
+ return user.id && user.hash ? `id:${user.id}` : JSON.stringify(user);
7053
+ }
7054
+ function useSignedInUser(user) {
7055
+ const person = personKey(user);
7056
+ useSyncExternalStore11(subscribeSession, getResetCount, () => 0);
7057
+ const [token] = useState19(() => Symbol("median-panel"));
7058
+ const previous = useRef19(null);
7059
+ useEffect19(() => {
7060
+ if (previous.current !== null && previous.current !== person) {
7061
+ signedOut.delete(previous.current);
7062
+ }
7063
+ previous.current = person;
7064
+ if (person === null) held.delete(token);
7065
+ else held.set(token, person);
7066
+ }, [person, token]);
7067
+ useEffect19(
7068
+ () => () => {
7069
+ const person2 = held.get(token);
7070
+ held.delete(token);
7071
+ if (person2 !== void 0) releaseLater(person2);
7072
+ },
7073
+ [token]
7074
+ );
7075
+ return person !== null && signedOut.has(person) ? void 0 : user;
7076
+ }
7077
+
7078
+ // ../../apps/web/src/components/support-widget/sdk/unread-store.ts
7079
+ import { useSyncExternalStore as useSyncExternalStore12 } from "react";
7080
+ var count = 0;
7081
+ var listeners6 = /* @__PURE__ */ new Set();
7082
+ var unreadStore = {
7083
+ subscribe(listener) {
7084
+ listeners6.add(listener);
7085
+ return () => {
7086
+ listeners6.delete(listener);
7087
+ };
7088
+ },
7089
+ read: () => count,
7090
+ /** The server has no thread to count, and neither does the first paint. */
7091
+ readServer: () => 0,
7092
+ set(next) {
7093
+ if (count === next) return;
7094
+ count = next;
7095
+ for (const listener of listeners6) listener();
7096
+ }
7097
+ };
7098
+ function useUnreadCount() {
7099
+ return useSyncExternalStore12(
7100
+ unreadStore.subscribe,
7101
+ unreadStore.read,
7102
+ unreadStore.readServer
7103
+ );
7104
+ }
7105
+
6686
7106
  // ../../apps/web/src/components/support-widget/sdk/control.ts
6687
7107
  var store = createOpenStore("MedianSupport");
6688
7108
  var medianSupport = {
@@ -6690,6 +7110,10 @@ var medianSupport = {
6690
7110
  get isOpen() {
6691
7111
  return store.readEffective();
6692
7112
  },
7113
+ /** Replies the visitor has not read. Zero while nothing is mounted. */
7114
+ get unreadCount() {
7115
+ return unreadStore.read();
7116
+ },
6693
7117
  open() {
6694
7118
  store.request(true);
6695
7119
  store.warnIfNothingIsMounted("the widget was asked to open");
@@ -6717,7 +7141,13 @@ var medianSupport = {
6717
7141
  }
6718
7142
  return;
6719
7143
  }
6720
- store.warnIfNothingIsMounted("data was attached");
7144
+ if (!isDevelopment || hasCustomDataSender()) return;
7145
+ setTimeout(() => {
7146
+ if (hasCustomDataSender()) return;
7147
+ console.warn(
7148
+ "Median: data was attached, but no <MedianSupport>, <MedianFeedback> or contact form is on the page to send it. It goes with the next message, note, sendMedianContact() or reportError() call."
7149
+ );
7150
+ }, 1e3);
6721
7151
  },
6722
7152
  /**
6723
7153
  * File the crash your error screen is showing, with everything the browser
@@ -6736,20 +7166,27 @@ var medianSupport = {
6736
7166
  * Forget this browser's conversation, so the next one starts fresh and
6737
7167
  * anonymous. Call it when your user signs out: whoever sits down next
6738
7168
  * should not find somebody else's thread waiting.
7169
+ *
7170
+ * The user the widget holds right now is signed out with it, even if your
7171
+ * own state still passes them for a moment, and an identity route is read
7172
+ * again.
6739
7173
  */
6740
7174
  reset() {
7175
+ signOutHeldUsers();
6741
7176
  resetSessions();
6742
7177
  resetVisitorEmails();
6743
7178
  }
6744
7179
  };
6745
7180
  function useMedianSupport() {
6746
- const isOpen = useSyncExternalStore11(
7181
+ const isOpen = useSyncExternalStore13(
6747
7182
  store.subscribe,
6748
7183
  store.readEffective,
6749
7184
  store.readServerEffective
6750
7185
  );
7186
+ const unreadCount = useUnreadCount();
6751
7187
  return {
6752
7188
  isOpen,
7189
+ unreadCount,
6753
7190
  open: medianSupport.open,
6754
7191
  close: medianSupport.close,
6755
7192
  toggle: medianSupport.toggle,
@@ -6761,18 +7198,18 @@ function useMedianSupport() {
6761
7198
  function useMedianOpenState(options) {
6762
7199
  return useOpenState(store, options);
6763
7200
  }
6764
- function useRegisterMedianSupport() {
6765
- useRegister(store);
7201
+ function useRegisterMedianSupport(name = "MedianSupport") {
7202
+ useRegister(store, name);
6766
7203
  }
6767
7204
 
6768
7205
  // ../../apps/web/src/components/support-widget/sdk/error-boundary.tsx
6769
- import { ConvexError as ConvexError2 } from "convex/values";
7206
+ import { ConvexError as ConvexError3 } from "convex/values";
6770
7207
  import { Component } from "react";
6771
- function adviceFor(error) {
6772
- if (error instanceof ConvexError2 && typeof error.data === "object" && error.data !== null && error.data.code === "invalid_api_key") {
6773
- return "Median did not recognize the apiKey passed to <MedianSupport>, so the widget has been hidden. Derive it from an active MEDIAN_KEY with publicKeyFromMedianKey.";
7208
+ function adviceFor(error, component) {
7209
+ if (error instanceof ConvexError3 && typeof error.data === "object" && error.data !== null && error.data.code === "invalid_api_key") {
7210
+ return `Median did not recognize the apiKey passed to <${component}>, so it has been hidden. Derive it from an active MEDIAN_KEY with publicKeyFromMedianKey.`;
6774
7211
  }
6775
- return "The Median support widget hit an error and has been hidden. Your page is unaffected.";
7212
+ return `Median: <${component}> hit an error and has been hidden. Your page is unaffected.`;
6776
7213
  }
6777
7214
  var MedianErrorBoundary = class extends Component {
6778
7215
  state = { hasFailed: false };
@@ -6780,7 +7217,7 @@ var MedianErrorBoundary = class extends Component {
6780
7217
  return { hasFailed: true };
6781
7218
  }
6782
7219
  componentDidCatch(error) {
6783
- console.error(adviceFor(error), error);
7220
+ console.error(adviceFor(error, this.props.component), error);
6784
7221
  }
6785
7222
  render() {
6786
7223
  return this.state.hasFailed ? null : this.props.children;
@@ -6788,7 +7225,7 @@ var MedianErrorBoundary = class extends Component {
6788
7225
  };
6789
7226
 
6790
7227
  // ../../apps/web/src/components/support-widget/sdk/use-identity.ts
6791
- import { useEffect as useEffect18, useState as useState20 } from "react";
7228
+ import { useEffect as useEffect20, useState as useState20, useSyncExternalStore as useSyncExternalStore14 } from "react";
6792
7229
  var NOTHING = {
6793
7230
  apiKey: void 0,
6794
7231
  user: void 0,
@@ -6796,8 +7233,9 @@ var NOTHING = {
6796
7233
  };
6797
7234
  var WAITING = { ...NOTHING, isLoading: true };
6798
7235
  var inFlight2 = /* @__PURE__ */ new Map();
6799
- function read3(endpoint) {
6800
- const existing = inFlight2.get(endpoint);
7236
+ function read3(endpoint, resets) {
7237
+ const cacheKey = `${resets}:${endpoint}`;
7238
+ const existing = inFlight2.get(cacheKey);
6801
7239
  if (existing !== void 0) return existing;
6802
7240
  const request = (async () => {
6803
7241
  const response = await fetch(endpoint, {
@@ -6812,26 +7250,27 @@ function read3(endpoint) {
6812
7250
  );
6813
7251
  }
6814
7252
  return await response.json();
6815
- })().finally(() => inFlight2.delete(endpoint));
6816
- inFlight2.set(endpoint, request);
7253
+ })().finally(() => inFlight2.delete(cacheKey));
7254
+ inFlight2.set(cacheKey, request);
6817
7255
  return request;
6818
7256
  }
6819
- function useMissingCredentials(component, apiKey, endpoint) {
6820
- useEffect18(() => {
7257
+ function useMissingCredentials(caller, apiKey, endpoint, consequence = "nothing is on the page") {
7258
+ useEffect20(() => {
6821
7259
  if (apiKey !== void 0 || endpoint !== void 0) return;
6822
7260
  console.error(
6823
- `Median: <${component}> was given neither apiKey nor identity, so nothing is on the page. Pass the median_pk_ id from publicKeyFromMedianKey(MEDIAN_KEY), or the path of a route built with medianIdentity().`
7261
+ `Median: ${caller} was given neither apiKey nor identity, so ${consequence}. Pass the median_pk_ id from publicKeyFromMedianKey(MEDIAN_KEY), or the path of a route built with medianIdentity().`
6824
7262
  );
6825
- }, [component, apiKey, endpoint]);
7263
+ }, [caller, apiKey, endpoint, consequence]);
6826
7264
  }
6827
7265
  function useMedianIdentity(endpoint) {
6828
7266
  const [answered, setAnswered] = useState20(null);
6829
- useEffect18(() => {
7267
+ const resets = useSyncExternalStore14(subscribeSession, getResetCount, () => 0);
7268
+ useEffect20(() => {
6830
7269
  if (endpoint === void 0) return;
6831
7270
  let listening = true;
6832
7271
  void (async () => {
6833
7272
  try {
6834
- const body = await read3(endpoint);
7273
+ const body = await read3(endpoint, resets);
6835
7274
  if (!listening) return;
6836
7275
  setAnswered({
6837
7276
  endpoint,
@@ -6855,21 +7294,60 @@ function useMedianIdentity(endpoint) {
6855
7294
  return () => {
6856
7295
  listening = false;
6857
7296
  };
6858
- }, [endpoint]);
7297
+ }, [endpoint, resets]);
6859
7298
  if (endpoint === void 0) return NOTHING;
6860
7299
  if (answered?.endpoint === endpoint) return answered.identity;
6861
7300
  return WAITING;
6862
7301
  }
6863
7302
 
7303
+ // ../../apps/web/src/components/support-widget/sdk/use-median-branding.ts
7304
+ import { ConvexHttpClient as ConvexHttpClient2 } from "convex/browser";
7305
+ import { useEffect as useEffect21, useState as useState21 } from "react";
7306
+ function useMedianBranding(apiKey) {
7307
+ const [state, setState] = useState21({ key: apiKey, show: true });
7308
+ useEffect21(() => {
7309
+ if (!apiKey || !MEDIAN_DEPLOYMENT_URL) return;
7310
+ const client3 = new ConvexHttpClient2(MEDIAN_DEPLOYMENT_URL);
7311
+ let active = true;
7312
+ let expiry;
7313
+ async function refresh() {
7314
+ try {
7315
+ const result = await client3.action(api.billingAccess.branding, {
7316
+ apiKey
7317
+ });
7318
+ if (!active) return;
7319
+ clearTimeout(expiry);
7320
+ const expired = result.expiresAt !== null && result.expiresAt <= Date.now();
7321
+ setState({ key: apiKey, show: result.showBranding || expired });
7322
+ if (!result.showBranding && result.expiresAt !== null)
7323
+ expiry = setTimeout(
7324
+ () => setState({ key: apiKey, show: true }),
7325
+ Math.min(2147483647, Math.max(0, result.expiresAt - Date.now()))
7326
+ );
7327
+ } catch {
7328
+ if (active) setState({ key: apiKey, show: true });
7329
+ }
7330
+ }
7331
+ void refresh();
7332
+ const timer = setInterval(() => void refresh(), 6e4);
7333
+ return () => {
7334
+ active = false;
7335
+ clearInterval(timer);
7336
+ clearTimeout(expiry);
7337
+ };
7338
+ }, [apiKey]);
7339
+ return state.key === apiKey ? state.show : true;
7340
+ }
7341
+
6864
7342
  // ../../apps/web/src/components/support-widget/sdk/median-connection.tsx
6865
7343
  import { ConvexProvider, ConvexReactClient } from "convex/react";
6866
- import { useState as useState21 } from "react";
7344
+ import { useState as useState22 } from "react";
6867
7345
  import { jsx as jsx37 } from "react/jsx-runtime";
6868
7346
  function MedianConnection({
6869
7347
  what,
6870
7348
  children
6871
7349
  }) {
6872
- const [client3] = useState21(() => {
7350
+ const [client3] = useState22(() => {
6873
7351
  if (!MEDIAN_DEPLOYMENT_URL) {
6874
7352
  throw new Error(
6875
7353
  `The Median ${what} was built without a deployment URL. Set NEXT_PUBLIC_CONVEX_URL.`
@@ -6881,15 +7359,15 @@ function MedianConnection({
6881
7359
  }
6882
7360
 
6883
7361
  // ../../apps/web/src/components/support-widget/sdk/use-api-key-warning.ts
6884
- import { useEffect as useEffect19 } from "react";
6885
- function useApiKeyShapeWarning(component, apiKey) {
6886
- useEffect19(() => {
7362
+ import { useEffect as useEffect22 } from "react";
7363
+ function useApiKeyShapeWarning(caller, apiKey) {
7364
+ useEffect22(() => {
6887
7365
  if (apiKey === void 0 || hasPrefix(apiKey, API_KEY_PREFIX)) return;
6888
7366
  const shown = apiKey === "" ? "an empty string" : `"${apiKey.length > 16 ? `${apiKey.slice(0, 16)}\u2026` : apiKey}"`;
6889
7367
  console.error(
6890
- `Median: the apiKey passed to <${component}> is ${shown}, which does not look like the browser-safe id from publicKeyFromMedianKey(MEDIAN_KEY). It should start with ${API_KEY_PREFIX}.`
7368
+ `Median: the apiKey passed to ${caller} is ${shown}, which does not look like the browser-safe id from publicKeyFromMedianKey(MEDIAN_KEY). It should start with ${API_KEY_PREFIX}.`
6891
7369
  );
6892
- }, [component, apiKey]);
7370
+ }, [caller, apiKey]);
6893
7371
  }
6894
7372
 
6895
7373
  // ../../node_modules/.bun/cuelume@0.2.2/node_modules/cuelume/dist/sounds/recipes.js
@@ -7210,16 +7688,16 @@ function play(sound = "chime", options) {
7210
7688
  }
7211
7689
 
7212
7690
  // ../../apps/web/src/components/support-widget/sdk/use-message-sound.ts
7213
- import { useEffect as useEffect20, useRef as useRef16 } from "react";
7691
+ import { useEffect as useEffect23, useRef as useRef20 } from "react";
7214
7692
  var ARRIVAL_VOLUME = 0.45;
7215
7693
  function useMessageSound({
7216
7694
  messages,
7217
7695
  conversationId = null,
7218
7696
  isLoading
7219
7697
  }) {
7220
- const heard = useRef16(null);
7221
- const watching = useRef16(null);
7222
- useEffect20(() => {
7698
+ const heard = useRef20(null);
7699
+ const watching = useRef20(null);
7700
+ useEffect23(() => {
7223
7701
  if (isLoading) {
7224
7702
  heard.current = null;
7225
7703
  return;
@@ -7243,7 +7721,7 @@ function useMessageSound({
7243
7721
  }
7244
7722
 
7245
7723
  // ../../apps/web/src/components/support-widget/sdk/use-navigation.ts
7246
- import { useCallback as useCallback12, useEffect as useEffect21, useLayoutEffect as useLayoutEffect9, useRef as useRef17, useState as useState22 } from "react";
7724
+ import { useCallback as useCallback13, useEffect as useEffect24, useLayoutEffect as useLayoutEffect10, useRef as useRef21, useState as useState23 } from "react";
7247
7725
  var isDevelopment2 = typeof process !== "undefined" && process.env.NODE_ENV === "development";
7248
7726
  var STALE_AFTER_MS = 5 * 60 * 1e3;
7249
7727
  function isFresh(at2) {
@@ -7287,12 +7765,12 @@ function useSupportNavigation({
7287
7765
  isLoading,
7288
7766
  onNavigate
7289
7767
  }) {
7290
- const seen = useRef17(null);
7291
- const watching = useRef17(null);
7292
- const [spotlight, setSpotlight] = useState22(null);
7293
- const drawn = useRef17(0);
7294
- const [taken, setTaken] = useState22(null);
7295
- const follow = useCallback12(
7768
+ const seen = useRef21(null);
7769
+ const watching = useRef21(null);
7770
+ const [spotlight, setSpotlight] = useState23(null);
7771
+ const drawn = useRef21(0);
7772
+ const [taken, setTaken] = useState23(null);
7773
+ const follow = useCallback13(
7296
7774
  (destination) => {
7297
7775
  if (onNavigate === void 0) return;
7298
7776
  const path = pathOnThisSite(destination.path);
@@ -7314,7 +7792,7 @@ function useSupportNavigation({
7314
7792
  },
7315
7793
  [onNavigate]
7316
7794
  );
7317
- useLayoutEffect9(() => {
7795
+ useLayoutEffect10(() => {
7318
7796
  if (isLoading) return;
7319
7797
  const asked = ringing(messages, ask);
7320
7798
  if (seen.current === null || watching.current !== conversationId) {
@@ -7332,8 +7810,8 @@ function useSupportNavigation({
7332
7810
  }
7333
7811
  if (latest !== null) setSpotlight(latest);
7334
7812
  }, [messages, ask, conversationId, isLoading]);
7335
- const warned = useRef17(false);
7336
- useEffect21(() => {
7813
+ const warned = useRef21(false);
7814
+ useEffect24(() => {
7337
7815
  if (!isDevelopment2 || onNavigate !== void 0 || warned.current) return;
7338
7816
  const offered = messages.some((message) => message.navigateTo !== void 0) || ask?.navigateTo != null;
7339
7817
  if (!offered) return;
@@ -7342,12 +7820,12 @@ function useSupportNavigation({
7342
7820
  "Median: a tool offered to take this visitor to a page, but <MedianSupport> has no onNavigate, so no button is shown for it. Pass one, such as onNavigate={router.push}."
7343
7821
  );
7344
7822
  }, [messages, ask, onNavigate]);
7345
- const [, expire] = useState22(0);
7346
- useEffect21(() => {
7823
+ const [, expire] = useState23(0);
7824
+ useEffect24(() => {
7347
7825
  if (ask === null || ask.navigateTo === null) return;
7348
7826
  const left = ask.at + STALE_AFTER_MS - Date.now();
7349
7827
  if (left <= 0) return;
7350
- const timer = setTimeout(() => expire((count) => count + 1), left);
7828
+ const timer = setTimeout(() => expire((count2) => count2 + 1), left);
7351
7829
  return () => clearTimeout(timer);
7352
7830
  }, [ask]);
7353
7831
  const offer = ask !== null && ask.navigateTo !== null && ask.key !== taken && onNavigate !== void 0 && isFresh(ask.at) ? { key: ask.key, path: ask.navigateTo, highlight: ask.highlight } : null;
@@ -7355,7 +7833,33 @@ function useSupportNavigation({
7355
7833
  }
7356
7834
 
7357
7835
  // ../../apps/web/src/components/support-widget/sdk/use-teaser.ts
7358
- import { useCallback as useCallback13, useState as useState23 } from "react";
7836
+ import { useCallback as useCallback14, useState as useState24 } from "react";
7837
+
7838
+ // ../../apps/web/src/components/support-widget/sdk/stamps.ts
7839
+ var inMemory3 = /* @__PURE__ */ new Map();
7840
+ function keyOf(name, apiKey) {
7841
+ return `median:${name}:${apiKey}`;
7842
+ }
7843
+ function readStamp(name, apiKey) {
7844
+ if (typeof window === "undefined") return 0;
7845
+ const key = keyOf(name, apiKey);
7846
+ try {
7847
+ const stored = window.localStorage.getItem(key);
7848
+ const at2 = stored === null ? Number.NaN : Number(stored);
7849
+ return Number.isFinite(at2) ? at2 : 0;
7850
+ } catch {
7851
+ return inMemory3.get(key) ?? 0;
7852
+ }
7853
+ }
7854
+ function writeStamp(name, apiKey, at2) {
7855
+ if (typeof window === "undefined") return;
7856
+ const key = keyOf(name, apiKey);
7857
+ inMemory3.set(key, at2);
7858
+ try {
7859
+ window.localStorage.setItem(key, String(at2));
7860
+ } catch {
7861
+ }
7862
+ }
7359
7863
 
7360
7864
  // ../../apps/web/src/components/support-widget/sdk/teaser-state.ts
7361
7865
  var TEASER = "teaser";
@@ -7368,11 +7872,11 @@ function writeTeaserDismissedAt(apiKey, at2) {
7368
7872
 
7369
7873
  // ../../apps/web/src/components/support-widget/sdk/use-teaser.ts
7370
7874
  function useTeaser(apiKey, latestUnread) {
7371
- const [dismissedAt, setDismissedAt] = useState23(
7875
+ const [dismissedAt, setDismissedAt] = useState24(
7372
7876
  () => readTeaserDismissedAt(apiKey)
7373
7877
  );
7374
7878
  const teaser = latestUnread !== null && (latestUnread.sentAt ?? 0) > dismissedAt ? latestUnread : null;
7375
- const dismiss = useCallback13(() => {
7879
+ const dismiss = useCallback14(() => {
7376
7880
  if (teaser === null) return;
7377
7881
  const at2 = teaser.sentAt ?? Date.now();
7378
7882
  writeTeaserDismissedAt(apiKey, at2);
@@ -7381,8 +7885,38 @@ function useTeaser(apiKey, latestUnread) {
7381
7885
  return { teaser, dismiss };
7382
7886
  }
7383
7887
 
7888
+ // ../../apps/web/src/components/support-widget/sdk/use-unread-title.ts
7889
+ import { useEffect as useEffect25 } from "react";
7890
+ function badged(count2, title) {
7891
+ return `(${count2}) ${title}`.trim();
7892
+ }
7893
+ function useUnreadTitle(unreadCount) {
7894
+ useEffect25(() => {
7895
+ if (unreadCount === 0 || typeof document === "undefined") return;
7896
+ let plain = document.title;
7897
+ let shown = badged(unreadCount, plain);
7898
+ document.title = shown;
7899
+ const observer = new MutationObserver(() => {
7900
+ const current2 = document.title;
7901
+ if (current2 === shown) return;
7902
+ plain = current2;
7903
+ shown = badged(unreadCount, plain);
7904
+ document.title = shown;
7905
+ });
7906
+ observer.observe(document.head, {
7907
+ childList: true,
7908
+ subtree: true,
7909
+ characterData: true
7910
+ });
7911
+ return () => {
7912
+ observer.disconnect();
7913
+ if (document.title === shown) document.title = plain;
7914
+ };
7915
+ }, [unreadCount]);
7916
+ }
7917
+
7384
7918
  // ../../apps/web/src/components/support-widget/sdk/median-support.tsx
7385
- import { Fragment as Fragment31, jsx as jsx38, jsxs as jsxs21 } from "react/jsx-runtime";
7919
+ import { Fragment as Fragment31, jsx as jsx38, jsxs as jsxs22 } from "react/jsx-runtime";
7386
7920
  var PRESENCE_BEAT_MS = 45e3;
7387
7921
  function MedianSupport(props) {
7388
7922
  return /* @__PURE__ */ jsx38(MedianSupportPreview, { ...props });
@@ -7393,7 +7927,7 @@ function MedianSupportPreview({
7393
7927
  className,
7394
7928
  ...props
7395
7929
  }) {
7396
- return /* @__PURE__ */ jsx38(MedianErrorBoundary, { children: /* @__PURE__ */ jsx38(
7930
+ return /* @__PURE__ */ jsx38(MedianErrorBoundary, { component: componentName(presentation), children: /* @__PURE__ */ jsx38(
7397
7931
  MedianIdentity,
7398
7932
  {
7399
7933
  ...props,
@@ -7403,17 +7937,25 @@ function MedianSupportPreview({
7403
7937
  }
7404
7938
  ) });
7405
7939
  }
7940
+ function componentName(presentation) {
7941
+ return presentation === "modal" ? "MedianSupportModal" : "MedianSupport";
7942
+ }
7406
7943
  function MedianIdentity({ apiKey, identity, user, ...rest }) {
7407
7944
  const resolved = useMedianIdentity(identity);
7408
- useMissingCredentials("MedianSupport", apiKey, identity);
7945
+ useMissingCredentials(
7946
+ `<${componentName(rest.presentation)}>`,
7947
+ apiKey,
7948
+ identity
7949
+ );
7409
7950
  const key = apiKey ?? resolved.apiKey;
7951
+ const person = useSignedInUser(user ?? resolved.user);
7410
7952
  if (key === void 0) return null;
7411
7953
  return /* @__PURE__ */ jsx38(MedianConnection, { what: "widget", children: /* @__PURE__ */ jsx38(
7412
7954
  MedianLauncher,
7413
7955
  {
7414
7956
  ...rest,
7415
7957
  apiKey: key,
7416
- user: user ?? resolved.user,
7958
+ user: person,
7417
7959
  isResolvingIdentity: resolved.isLoading
7418
7960
  }
7419
7961
  ) });
@@ -7422,12 +7964,14 @@ function MedianLauncher({
7422
7964
  apiKey,
7423
7965
  user,
7424
7966
  telemetry,
7967
+ screenshot,
7425
7968
  diagnostics,
7426
7969
  appState,
7427
7970
  requireSignIn = false,
7428
7971
  onSignIn,
7429
7972
  requireEmail = true,
7430
7973
  messagePreview = true,
7974
+ titleCount = true,
7431
7975
  launcher,
7432
7976
  presentation,
7433
7977
  defaultOpen,
@@ -7437,12 +7981,11 @@ function MedianLauncher({
7437
7981
  isResolvingIdentity,
7438
7982
  className
7439
7983
  }) {
7440
- useRegisterMedianSupport();
7441
- useApiKeyShapeWarning(
7442
- presentation === "modal" ? "MedianSupportModal" : "MedianSupport",
7443
- apiKey
7444
- );
7984
+ useRegisterMedianSupport(componentName(presentation));
7985
+ useCustomDataSender();
7986
+ useApiKeyShapeWarning(`<${componentName(presentation)}>`, apiKey);
7445
7987
  useRememberInstall(apiKey, user);
7988
+ const showBranding = useMedianBranding(apiKey);
7446
7989
  const [isOpen, setOpen] = useMedianOpenState({
7447
7990
  open,
7448
7991
  defaultOpen,
@@ -7462,31 +8005,28 @@ function MedianLauncher({
7462
8005
  rating,
7463
8006
  unreadCount,
7464
8007
  latestUnread,
7465
- markSeen,
7466
8008
  reportPresence,
7467
8009
  sendError,
7468
8010
  send,
7469
8011
  setTyping
7470
- } = useMedianThread(
8012
+ } = useMedianThread({
7471
8013
  apiKey,
7472
8014
  user,
7473
8015
  telemetry,
8016
+ screenshot,
7474
8017
  isLocked,
7475
8018
  diagnostics,
7476
8019
  appState,
7477
8020
  requireEmail,
7478
8021
  messagePreview
7479
- );
8022
+ });
7480
8023
  const { teaser, dismiss: dismissTeaser } = useTeaser(apiKey, latestUnread);
7481
8024
  const isSettling = isLoading || isResolvingIdentity;
7482
- useEffect22(() => {
7483
- if (isOpen) markSeen();
7484
- }, [isOpen, markSeen]);
7485
- useEffect22(() => {
8025
+ useEffect26(() => {
7486
8026
  if (!threadRendersMarkdown(messages)) return;
7487
8027
  return warmMarkdownWhenIdle();
7488
8028
  }, [messages]);
7489
- useEffect22(() => {
8029
+ useEffect26(() => {
7490
8030
  if (!isOpen) return;
7491
8031
  const beat = () => {
7492
8032
  if (document.visibilityState === "visible") reportPresence();
@@ -7519,16 +8059,22 @@ function MedianLauncher({
7519
8059
  conversationId,
7520
8060
  isLoading: isSettling || isLocked
7521
8061
  });
8062
+ useUnreadTitle(titleCount ? unreadCount : 0);
8063
+ useEffect26(() => {
8064
+ unreadStore.set(unreadCount);
8065
+ }, [unreadCount]);
8066
+ useEffect26(() => () => unreadStore.set(0), []);
7522
8067
  const resolvedPresence = presence ?? {
7523
8068
  name: "Support",
7524
8069
  status: "Connecting",
7525
8070
  team: []
7526
8071
  };
7527
- return /* @__PURE__ */ jsxs21(Fragment31, { children: [
8072
+ return /* @__PURE__ */ jsxs22(Fragment31, { children: [
7528
8073
  spotlight !== null && /* @__PURE__ */ jsx38(WidgetSpotlight, { selector: spotlight.selector }, spotlight.key),
7529
8074
  /* @__PURE__ */ jsx38(
7530
8075
  SupportLauncher,
7531
8076
  {
8077
+ showBadge: showBranding,
7532
8078
  presence: resolvedPresence,
7533
8079
  messages,
7534
8080
  onSend: send,
@@ -7644,7 +8190,12 @@ function mountMedianSupportModal(options) {
7644
8190
  }
7645
8191
 
7646
8192
  // ../../apps/web/src/components/support-widget/sdk/median-feedback.tsx
7647
- import { useCallback as useCallback15, useEffect as useEffect25, useRef as useRef20, useState as useState27 } from "react";
8193
+ import {
8194
+ useCallback as useCallback16,
8195
+ useEffect as useEffect29,
8196
+ useRef as useRef24,
8197
+ useState as useState28
8198
+ } from "react";
7648
8199
 
7649
8200
  // ../backend/convex/shared/feedback.ts
7650
8201
  var NOTE_MAX_LENGTH = 2e3;
@@ -7654,17 +8205,17 @@ var MAX_REPORT_IMAGES = 6;
7654
8205
 
7655
8206
  // ../../apps/web/src/components/support-widget/feedback/feedback-panel.tsx
7656
8207
  import {
7657
- useEffect as useEffect24,
7658
- useRef as useRef19,
7659
- useState as useState25
8208
+ useEffect as useEffect28,
8209
+ useRef as useRef23,
8210
+ useState as useState26
7660
8211
  } from "react";
7661
8212
 
7662
8213
  // ../../apps/web/src/components/support-widget/feedback/feedback-popover.tsx
7663
- import { useEffect as useEffect23, useRef as useRef18 } from "react";
8214
+ import { useEffect as useEffect27, useRef as useRef22 } from "react";
7664
8215
  import { createPortal as createPortal3 } from "react-dom";
7665
8216
 
7666
8217
  // ../../apps/web/src/components/support-widget/feedback/use-anchor.ts
7667
- import { useLayoutEffect as useLayoutEffect10, useState as useState24 } from "react";
8218
+ import { useLayoutEffect as useLayoutEffect11, useState as useState25 } from "react";
7668
8219
  var GAP2 = 8;
7669
8220
  var MARGIN2 = 16;
7670
8221
  function findAnchor(selector) {
@@ -7684,8 +8235,8 @@ function useAnchor({
7684
8235
  panelRef,
7685
8236
  isOpen
7686
8237
  }) {
7687
- const [side, setSide] = useState24("bottom");
7688
- useLayoutEffect10(() => {
8238
+ const [side, setSide] = useState25("bottom");
8239
+ useLayoutEffect11(() => {
7689
8240
  if (!isOpen) return;
7690
8241
  let frame = 0;
7691
8242
  function place() {
@@ -7753,12 +8304,12 @@ function FeedbackPopover({
7753
8304
  width,
7754
8305
  children
7755
8306
  }) {
7756
- const panelRef = useRef18(null);
8307
+ const panelRef = useRef22(null);
7757
8308
  const side = useAnchor({ triggerRef, anchor, panelRef, isOpen });
7758
8309
  const isHydrated = useHydrated();
7759
- const opener = useRef18(null);
7760
- const wasOpen = useRef18(isOpen);
7761
- useEffect23(() => {
8310
+ const opener = useRef22(null);
8311
+ const wasOpen = useRef22(isOpen);
8312
+ useEffect27(() => {
7762
8313
  if (!isOpen) return;
7763
8314
  function handleKeyDown(event) {
7764
8315
  if (event.key === "Escape") onClose();
@@ -7766,7 +8317,7 @@ function FeedbackPopover({
7766
8317
  document.addEventListener("keydown", handleKeyDown);
7767
8318
  return () => document.removeEventListener("keydown", handleKeyDown);
7768
8319
  }, [isOpen, onClose]);
7769
- useEffect23(() => {
8320
+ useEffect27(() => {
7770
8321
  if (!isOpen) return;
7771
8322
  function handlePointerDown(event) {
7772
8323
  const target = event.target;
@@ -7778,7 +8329,7 @@ function FeedbackPopover({
7778
8329
  document.addEventListener("pointerdown", handlePointerDown);
7779
8330
  return () => document.removeEventListener("pointerdown", handlePointerDown);
7780
8331
  }, [isOpen, onClose, triggerRef]);
7781
- useEffect23(() => {
8332
+ useEffect27(() => {
7782
8333
  if (wasOpen.current === isOpen) return;
7783
8334
  wasOpen.current = isOpen;
7784
8335
  if (isOpen) {
@@ -7834,7 +8385,7 @@ function FeedbackPopover({
7834
8385
  }
7835
8386
 
7836
8387
  // ../../apps/web/src/components/support-widget/feedback/feedback-panel.tsx
7837
- import { Fragment as Fragment32, jsx as jsx43, jsxs as jsxs22 } from "react/jsx-runtime";
8388
+ import { Fragment as Fragment32, jsx as jsx43, jsxs as jsxs23 } from "react/jsx-runtime";
7838
8389
  var THANKS_MS2 = 1600;
7839
8390
  function FeedbackPanel({
7840
8391
  onSubmit,
@@ -7843,21 +8394,21 @@ function FeedbackPanel({
7843
8394
  notice,
7844
8395
  showBadge = true,
7845
8396
  title = "Share feedback",
7846
- description = "The current page URL is included.",
8397
+ description = "Bugs, ideas, anything.",
7847
8398
  placeholder = "Tell us what could be better.",
7848
8399
  className
7849
8400
  }) {
7850
- const [draft, setDraft] = useState25("");
7851
- const [isSending, setSending] = useState25(false);
7852
- const [isSent, setSent] = useState25(false);
7853
- const fileInputRef = useRef19(null);
8401
+ const [draft, setDraft] = useState26("");
8402
+ const [isSending, setSending] = useState26(false);
8403
+ const [isSent, setSent] = useState26(false);
8404
+ const fileInputRef = useRef23(null);
7854
8405
  const images = useAttachmentDrafts({
7855
8406
  accept: "image",
7856
8407
  max: MAX_REPORT_IMAGES
7857
8408
  });
7858
8409
  const target = useAttachmentTarget({ onFiles: images.add });
7859
8410
  const canSend = draft.trim().length > 0 && !images.hasBlocker && !isSending && !isSent;
7860
- useEffect24(() => {
8411
+ useEffect28(() => {
7861
8412
  if (!isSent) return;
7862
8413
  const timer = setTimeout(() => onSent?.(), THANKS_MS2);
7863
8414
  return () => clearTimeout(timer);
@@ -7885,7 +8436,7 @@ function FeedbackPanel({
7885
8436
  event.preventDefault();
7886
8437
  void send();
7887
8438
  }
7888
- return /* @__PURE__ */ jsxs22(
8439
+ return /* @__PURE__ */ jsxs23(
7889
8440
  "section",
7890
8441
  {
7891
8442
  "aria-label": title,
@@ -7893,12 +8444,12 @@ function FeedbackPanel({
7893
8444
  className: cn(
7894
8445
  // The widget's own radius scale, so the app's token can climb as the
7895
8446
  // chrome gets rounder without this changing shape under its hosts.
7896
- "flex w-full flex-col gap-2 overflow-hidden rounded-xl border bg-card p-3 text-card-foreground [--radius:0.625rem]",
8447
+ `flex w-full flex-col gap-2 overflow-hidden ${WIDGET_CORNER} border bg-card p-3 text-card-foreground [--radius:0.625rem]`,
7897
8448
  className
7898
8449
  ),
7899
8450
  children: [
7900
- /* @__PURE__ */ jsxs22("header", { className: "flex items-start gap-2", children: [
7901
- /* @__PURE__ */ jsxs22("div", { className: "flex min-w-0 flex-1 flex-col", children: [
8451
+ /* @__PURE__ */ jsxs23("header", { className: "flex items-start gap-2", children: [
8452
+ /* @__PURE__ */ jsxs23("div", { className: "flex min-w-0 flex-1 flex-col", children: [
7902
8453
  /* @__PURE__ */ jsx43("h2", { className: "truncate text-sm font-semibold tracking-tight", children: title }),
7903
8454
  /* @__PURE__ */ jsx43("p", { className: "text-xs text-muted-foreground", children: description })
7904
8455
  ] }),
@@ -7915,8 +8466,8 @@ function FeedbackPanel({
7915
8466
  }
7916
8467
  )
7917
8468
  ] }),
7918
- /* @__PURE__ */ jsxs22("div", { className: "grid", children: [
7919
- /* @__PURE__ */ jsx43(Collapse, { isOpen: !isSent, children: /* @__PURE__ */ jsxs22("div", { className: "flex flex-col gap-1.5", children: [
8469
+ /* @__PURE__ */ jsxs23("div", { className: "grid", children: [
8470
+ /* @__PURE__ */ jsx43(Collapse, { isOpen: !isSent, children: /* @__PURE__ */ jsxs23("div", { className: "flex flex-col gap-1.5", children: [
7920
8471
  /* @__PURE__ */ jsx43(
7921
8472
  Textarea,
7922
8473
  {
@@ -7929,7 +8480,7 @@ function FeedbackPanel({
7929
8480
  "aria-label": title,
7930
8481
  ...{ [AUTOFOCUS_ATTRIBUTE]: "" },
7931
8482
  className: cn(
7932
- TEXT_FIELD_SHELL,
8483
+ WIDGET_TEXT_FIELD_SHELL,
7933
8484
  "max-h-40 min-h-18 px-2.5 py-2",
7934
8485
  // 16px on phones, in pixels: any focused field smaller than
7935
8486
  // that makes iOS zoom the host's whole page in.
@@ -7947,14 +8498,14 @@ function FeedbackPanel({
7947
8498
  )) }),
7948
8499
  notice && /* @__PURE__ */ jsx43("p", { className: "animate-in px-0.5 text-xs text-destructive fade-in duration-150", children: notice })
7949
8500
  ] }) }),
7950
- /* @__PURE__ */ jsx43(Collapse, { isOpen: isSent, children: /* @__PURE__ */ jsxs22("div", { className: "flex items-center justify-center gap-2 py-4 text-sm font-medium", children: [
8501
+ /* @__PURE__ */ jsx43(Collapse, { isOpen: isSent, children: /* @__PURE__ */ jsxs23("div", { className: "flex items-center justify-center gap-2 py-4 text-sm font-medium", children: [
7951
8502
  /* @__PURE__ */ jsx43("span", { className: "grid size-5 shrink-0 place-items-center rounded-full bg-primary text-primary-foreground", children: /* @__PURE__ */ jsx43(Check, { className: "size-3" }) }),
7952
8503
  "Thanks for your feedback."
7953
8504
  ] }) })
7954
8505
  ] }),
7955
- /* @__PURE__ */ jsxs22("div", { className: "flex min-h-8 items-center gap-2", children: [
8506
+ /* @__PURE__ */ jsxs23("div", { className: "flex min-h-8 items-center gap-2", children: [
7956
8507
  showBadge && /* @__PURE__ */ jsx43(PoweredByMedian, { className: "justify-start" }),
7957
- !isSent && /* @__PURE__ */ jsxs22(Fragment32, { children: [
8508
+ !isSent && /* @__PURE__ */ jsxs23(Fragment32, { children: [
7958
8509
  /* @__PURE__ */ jsx43(
7959
8510
  "input",
7960
8511
  {
@@ -7980,7 +8531,7 @@ function FeedbackPanel({
7980
8531
  children: /* @__PURE__ */ jsx43(ImageSquare, {})
7981
8532
  }
7982
8533
  ),
7983
- /* @__PURE__ */ jsxs22(
8534
+ /* @__PURE__ */ jsxs23(
7984
8535
  WidgetButton,
7985
8536
  {
7986
8537
  type: "button",
@@ -8017,7 +8568,7 @@ function FeedbackPanel({
8017
8568
  }
8018
8569
 
8019
8570
  // ../../apps/web/src/components/support-widget/sdk/feedback-control.ts
8020
- import { useSyncExternalStore as useSyncExternalStore12 } from "react";
8571
+ import { useSyncExternalStore as useSyncExternalStore15 } from "react";
8021
8572
  var store2 = createOpenStore("MedianFeedback");
8022
8573
  var medianFeedback = {
8023
8574
  /** What the panel is doing right now. */
@@ -8040,7 +8591,7 @@ var medianFeedback = {
8040
8591
  }
8041
8592
  };
8042
8593
  function useMedianFeedback() {
8043
- const isOpen = useSyncExternalStore12(
8594
+ const isOpen = useSyncExternalStore15(
8044
8595
  store2.subscribe,
8045
8596
  store2.readEffective,
8046
8597
  store2.readServerEffective
@@ -8060,8 +8611,7 @@ function useRegisterMedianFeedback() {
8060
8611
  }
8061
8612
 
8062
8613
  // ../../apps/web/src/components/support-widget/sdk/use-feedback-note.ts
8063
- import { useMutation as useMutation2 } from "convex/react";
8064
- import { useCallback as useCallback14, useState as useState26, useSyncExternalStore as useSyncExternalStore13 } from "react";
8614
+ import { useCallback as useCallback15, useState as useState27, useSyncExternalStore as useSyncExternalStore16 } from "react";
8065
8615
  var REFUSALS = {
8066
8616
  off: "Feedback is currently unavailable. Try again later.",
8067
8617
  empty: "Enter your feedback before sending."
@@ -8070,19 +8620,20 @@ function useFeedbackNote({
8070
8620
  apiKey,
8071
8621
  user,
8072
8622
  telemetry = true,
8623
+ screenshot = true,
8073
8624
  diagnostics,
8074
8625
  appState
8075
8626
  }) {
8076
- const sessionId = useSyncExternalStore13(
8627
+ const sessionId = useSyncExternalStore16(
8077
8628
  subscribeSession,
8078
- useCallback14(() => getOrCreateSessionId(apiKey), [apiKey]),
8629
+ useCallback15(() => getOrCreateSessionId(apiKey), [apiKey]),
8079
8630
  () => null
8080
8631
  );
8081
- const submitNote = useMutation2(api.feedback.submit);
8082
- const getUploadUrl = useMutation2(api.widget.generateUploadUrl);
8632
+ const submitNote = useWidgetMutation(api.feedback.submitV2);
8633
+ const getUploadUrl = useWidgetMutation(api.widget.generateUploadUrlV2);
8083
8634
  const takeDiagnostics = useDiagnostics(diagnostics, appState);
8084
- const [notice, setNotice] = useState26(null);
8085
- const submit = useCallback14(
8635
+ const [notice, setNotice] = useState27(null);
8636
+ const submit = useCallback15(
8086
8637
  async (body, images = []) => {
8087
8638
  setNotice(null);
8088
8639
  if (sessionId === null) {
@@ -8096,9 +8647,10 @@ function useFeedbackNote({
8096
8647
  const page = telemetry ? collectPage() : void 0;
8097
8648
  const collected = takeDiagnostics();
8098
8649
  const { facts, claim } = splitIdentity(user ?? null);
8650
+ const shot = screenshot && images.length < MAX_REPORT_IMAGES ? await capturePage() : null;
8099
8651
  const attached = await uploadAttachments(
8100
8652
  () => getUploadUrl({ apiKey, sessionId }),
8101
- images
8653
+ shot === null ? images : [...images, shot]
8102
8654
  );
8103
8655
  const outcome = await submitNote({
8104
8656
  apiKey,
@@ -8114,13 +8666,18 @@ function useFeedbackNote({
8114
8666
  });
8115
8667
  if (outcome !== "taken") {
8116
8668
  explained = true;
8117
- setNotice(REFUSALS[outcome] ?? "Could not send your feedback. Try again.");
8669
+ setNotice(
8670
+ REFUSALS[outcome] ?? "Could not send your feedback. Try again."
8671
+ );
8118
8672
  throw new Error(outcome);
8119
8673
  }
8120
8674
  } catch (caught) {
8121
8675
  if (custom) restoreCustomData(custom);
8122
8676
  if (!explained) {
8123
- console.error("Sending feedback from the Median panel failed", caught);
8677
+ console.error(
8678
+ "Sending feedback from the Median panel failed",
8679
+ caught
8680
+ );
8124
8681
  setNotice(errorMessage(caught));
8125
8682
  }
8126
8683
  throw caught;
@@ -8131,6 +8688,7 @@ function useFeedbackNote({
8131
8688
  sessionId,
8132
8689
  user,
8133
8690
  telemetry,
8691
+ screenshot,
8134
8692
  takeDiagnostics,
8135
8693
  submitNote,
8136
8694
  getUploadUrl
@@ -8140,10 +8698,10 @@ function useFeedbackNote({
8140
8698
  }
8141
8699
 
8142
8700
  // ../../apps/web/src/components/support-widget/sdk/median-feedback.tsx
8143
- import { Fragment as Fragment33, jsx as jsx44, jsxs as jsxs23 } from "react/jsx-runtime";
8701
+ import { Fragment as Fragment33, jsx as jsx44, jsxs as jsxs24 } from "react/jsx-runtime";
8144
8702
  var DEFAULT_WIDTH2 = "20rem";
8145
8703
  function MedianFeedback(props) {
8146
- return /* @__PURE__ */ jsx44(MedianErrorBoundary, { children: /* @__PURE__ */ jsx44(FeedbackIdentity, { ...props }) });
8704
+ return /* @__PURE__ */ jsx44(MedianErrorBoundary, { component: "MedianFeedback", children: /* @__PURE__ */ jsx44(FeedbackIdentity, { ...props }) });
8147
8705
  }
8148
8706
  function FeedbackIdentity({
8149
8707
  apiKey,
@@ -8152,18 +8710,11 @@ function FeedbackIdentity({
8152
8710
  ...rest
8153
8711
  }) {
8154
8712
  const resolved = useMedianIdentity(identity);
8155
- useMissingCredentials("MedianFeedback", apiKey, identity);
8713
+ useMissingCredentials("<MedianFeedback>", apiKey, identity);
8156
8714
  const key = apiKey ?? resolved.apiKey;
8157
- const person = user ?? resolved.user;
8715
+ const person = useSignedInUser(user ?? resolved.user);
8158
8716
  if (key === void 0) return null;
8159
- return /* @__PURE__ */ jsx44(MedianConnection, { what: "feedback panel", children: /* @__PURE__ */ jsx44(
8160
- Feedback,
8161
- {
8162
- ...rest,
8163
- apiKey: key,
8164
- ...person ? { user: person } : {}
8165
- }
8166
- ) });
8717
+ return /* @__PURE__ */ jsx44(MedianConnection, { what: "feedback panel", children: /* @__PURE__ */ jsx44(Feedback, { ...rest, apiKey: key, ...person ? { user: person } : {} }) });
8167
8718
  }
8168
8719
  function Feedback({
8169
8720
  apiKey,
@@ -8171,6 +8722,7 @@ function Feedback({
8171
8722
  anchor,
8172
8723
  user,
8173
8724
  telemetry,
8725
+ screenshot,
8174
8726
  diagnostics,
8175
8727
  appState,
8176
8728
  open,
@@ -8181,9 +8733,10 @@ function Feedback({
8181
8733
  placeholder
8182
8734
  }) {
8183
8735
  useRegisterMedianFeedback();
8184
- useApiKeyShapeWarning("MedianFeedback", apiKey);
8736
+ useApiKeyShapeWarning("<MedianFeedback>", apiKey);
8737
+ useCustomDataSender();
8185
8738
  useRememberInstall(apiKey, user);
8186
- const triggerRef = useRef20(null);
8739
+ const triggerRef = useRef24(null);
8187
8740
  const [isOpen, setOpen] = useMedianFeedbackOpenState({
8188
8741
  ...open !== void 0 ? { open } : {},
8189
8742
  ...onOpenChange ? { onOpenChange } : {}
@@ -8192,18 +8745,20 @@ function Feedback({
8192
8745
  apiKey,
8193
8746
  ...user ? { user } : {},
8194
8747
  ...telemetry !== void 0 ? { telemetry } : {},
8748
+ ...screenshot !== void 0 ? { screenshot } : {},
8195
8749
  ...diagnostics !== void 0 ? { diagnostics } : {},
8196
8750
  ...appState ? { appState } : {}
8197
8751
  });
8198
- const close = useCallback15(() => setOpen(false), [setOpen]);
8199
- const [attempt, setAttempt] = useState27(0);
8200
- const hasSent = useRef20(false);
8201
- useEffect25(() => {
8752
+ const showBranding = useMedianBranding(apiKey);
8753
+ const close = useCallback16(() => setOpen(false), [setOpen]);
8754
+ const [attempt, setAttempt] = useState28(0);
8755
+ const hasSent = useRef24(false);
8756
+ useEffect29(() => {
8202
8757
  if (!isOpen || !hasSent.current) return;
8203
8758
  hasSent.current = false;
8204
- setAttempt((count) => count + 1);
8759
+ setAttempt((count2) => count2 + 1);
8205
8760
  }, [isOpen]);
8206
- return /* @__PURE__ */ jsxs23(Fragment33, { children: [
8761
+ return /* @__PURE__ */ jsxs24(Fragment33, { children: [
8207
8762
  children !== void 0 && // An inline span rather than a clone of whatever was passed: the
8208
8763
  // host's trigger keeps its own props, its own ref and its own
8209
8764
  // styles, and the click is caught on the way past. `display:
@@ -8228,6 +8783,7 @@ function Feedback({
8228
8783
  children: /* @__PURE__ */ jsx44(
8229
8784
  FeedbackPanel,
8230
8785
  {
8786
+ showBadge: showBranding,
8231
8787
  onSubmit: note.submit,
8232
8788
  onClose: close,
8233
8789
  onSent: () => {
@@ -8260,23 +8816,23 @@ function mountMedianFeedback(options) {
8260
8816
  }
8261
8817
 
8262
8818
  // ../../apps/web/src/components/support-widget/sdk/median-contact-form.tsx
8263
- import { useEffect as useEffect27, useMemo as useMemo5 } from "react";
8819
+ import { useEffect as useEffect31, useMemo as useMemo5 } from "react";
8264
8820
 
8265
8821
  // ../../apps/web/src/components/support-widget/contact/contact-form.tsx
8266
8822
  import {
8267
- useEffect as useEffect26,
8823
+ useEffect as useEffect30,
8268
8824
  useId,
8269
8825
  useMemo as useMemo3,
8270
- useRef as useRef22,
8271
- useState as useState29
8826
+ useRef as useRef26,
8827
+ useState as useState30
8272
8828
  } from "react";
8273
8829
 
8274
8830
  // ../../apps/web/src/components/support-widget/contact/contact-field.tsx
8275
- import { useState as useState28 } from "react";
8276
- import { jsx as jsx46, jsxs as jsxs24 } from "react/jsx-runtime";
8831
+ import { useState as useState29 } from "react";
8832
+ import { jsx as jsx46, jsxs as jsxs25 } from "react/jsx-runtime";
8277
8833
  var LINE_MAX_LENGTH = 200;
8278
8834
  var SHELL = cn(
8279
- TEXT_FIELD_SHELL,
8835
+ WIDGET_TEXT_FIELD_SHELL,
8280
8836
  "w-full bg-card text-base max-sm:text-[16px] dark:bg-card"
8281
8837
  );
8282
8838
  var LINE = "h-11 rounded-full px-4";
@@ -8290,9 +8846,9 @@ function ContactField({
8290
8846
  onChange,
8291
8847
  onSubmit
8292
8848
  }) {
8293
- const [held, setHeld] = useState28(error);
8294
- if (error !== null && error !== held) setHeld(error);
8295
- const shown = error ?? held;
8849
+ const [held2, setHeld] = useState29(error);
8850
+ if (error !== null && error !== held2) setHeld(error);
8851
+ const shown = error ?? held2;
8296
8852
  const errorId = `${id}-error`;
8297
8853
  const control = {
8298
8854
  id,
@@ -8308,8 +8864,8 @@ function ContactField({
8308
8864
  event.preventDefault();
8309
8865
  onSubmit?.();
8310
8866
  }
8311
- return /* @__PURE__ */ jsxs24("div", { className: "flex flex-col", children: [
8312
- /* @__PURE__ */ jsxs24(
8867
+ return /* @__PURE__ */ jsxs25("div", { className: "flex flex-col", children: [
8868
+ /* @__PURE__ */ jsxs25(
8313
8869
  "label",
8314
8870
  {
8315
8871
  htmlFor: id,
@@ -8331,8 +8887,8 @@ function ContactField({
8331
8887
  onKeyDown: handleKeyDown,
8332
8888
  className: cn(SHELL, PARAGRAPH)
8333
8889
  }
8334
- ) : field.type === "select" ? /* @__PURE__ */ jsxs24("div", { className: "relative", children: [
8335
- /* @__PURE__ */ jsxs24(
8890
+ ) : field.type === "select" ? /* @__PURE__ */ jsxs25("div", { className: "relative", children: [
8891
+ /* @__PURE__ */ jsxs25(
8336
8892
  "select",
8337
8893
  {
8338
8894
  ...control,
@@ -8498,8 +9054,8 @@ function fieldComplaints(fields) {
8498
9054
  }
8499
9055
 
8500
9056
  // ../../apps/web/src/components/support-widget/contact/sent-mark.tsx
8501
- import { useLayoutEffect as useLayoutEffect11, useRef as useRef21 } from "react";
8502
- import { jsx as jsx47, jsxs as jsxs25 } from "react/jsx-runtime";
9057
+ import { useLayoutEffect as useLayoutEffect12, useRef as useRef25 } from "react";
9058
+ import { jsx as jsx47, jsxs as jsxs26 } from "react/jsx-runtime";
8503
9059
  var EASE_OUT = "cubic-bezier(0.22, 1, 0.36, 1)";
8504
9060
  var TRAVEL_MS = 550;
8505
9061
  var DRAW_LEAD_MS = 150;
@@ -8512,11 +9068,11 @@ function SentMark({
8512
9068
  from = null,
8513
9069
  className
8514
9070
  }) {
8515
- const frame = useRef21(null);
8516
- const square = useRef21(null);
8517
- const tick = useRef21(null);
8518
- const ring = useRef21(null);
8519
- useLayoutEffect11(() => {
9071
+ const frame = useRef25(null);
9072
+ const square = useRef25(null);
9073
+ const tick = useRef25(null);
9074
+ const ring = useRef25(null);
9075
+ useLayoutEffect12(() => {
8520
9076
  const rest = frame.current;
8521
9077
  const box = square.current;
8522
9078
  const path = tick.current;
@@ -8574,7 +9130,7 @@ function SentMark({
8574
9130
  for (const animation of animations) animation.cancel();
8575
9131
  };
8576
9132
  }, [from]);
8577
- return /* @__PURE__ */ jsxs25(
9133
+ return /* @__PURE__ */ jsxs26(
8578
9134
  "span",
8579
9135
  {
8580
9136
  ref: frame,
@@ -8618,7 +9174,7 @@ function SentMark({
8618
9174
  }
8619
9175
 
8620
9176
  // ../../apps/web/src/components/support-widget/contact/contact-form.tsx
8621
- import { jsx as jsx48, jsxs as jsxs26 } from "react/jsx-runtime";
9177
+ import { jsx as jsx48, jsxs as jsxs27 } from "react/jsx-runtime";
8622
9178
  var EMAIL_PATTERN2 = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
8623
9179
  var FORM_SWAP = "transition-[opacity,translate] duration-250 ease-out-quint motion-reduce:transition-none";
8624
9180
  var THANKS_SWAP = "transition-opacity ease-out motion-reduce:transition-none";
@@ -8663,18 +9219,18 @@ function ContactForm({
8663
9219
  }) {
8664
9220
  const fields = useMemo3(() => resolveFields(hostFields), [hostFields]);
8665
9221
  const prefix = useId();
8666
- const formRef = useRef22(null);
8667
- const sendRef = useRef22(null);
8668
- const [values, setValues] = useState29(
9222
+ const formRef = useRef26(null);
9223
+ const sendRef = useRef26(null);
9224
+ const [values, setValues] = useState30(
8669
9225
  () => startingValues(fields, initialValues)
8670
9226
  );
8671
- const [errors, setErrors] = useState29({});
8672
- const [isSending, setSending] = useState29(false);
8673
- const [sent, setSent] = useState29(null);
9227
+ const [errors, setErrors] = useState30({});
9228
+ const [isSending, setSending] = useState30(false);
9229
+ const [sent, setSent] = useState30(null);
8674
9230
  const isSent = sent !== null;
8675
9231
  const knownEmail = initialValues?.email;
8676
9232
  const knownName = initialValues?.name;
8677
- const [known, setKnown] = useState29({ email: knownEmail, name: knownName });
9233
+ const [known, setKnown] = useState30({ email: knownEmail, name: knownName });
8678
9234
  if (known.email !== knownEmail || known.name !== knownName) {
8679
9235
  setKnown({ email: knownEmail, name: knownName });
8680
9236
  setValues((current2) => {
@@ -8684,8 +9240,8 @@ function ContactForm({
8684
9240
  return next;
8685
9241
  });
8686
9242
  }
8687
- const [focusRequest, setFocusRequest] = useState29(0);
8688
- useEffect26(() => {
9243
+ const [focusRequest, setFocusRequest] = useState30(0);
9244
+ useEffect30(() => {
8689
9245
  if (focusRequest === 0) return;
8690
9246
  formRef.current?.querySelector('[aria-invalid="true"]')?.focus({ preventScroll: false });
8691
9247
  }, [focusRequest]);
@@ -8707,7 +9263,7 @@ function ContactForm({
8707
9263
  }
8708
9264
  if (Object.keys(found).length > 0) {
8709
9265
  setErrors(found);
8710
- setFocusRequest((count) => count + 1);
9266
+ setFocusRequest((count2) => count2 + 1);
8711
9267
  return;
8712
9268
  }
8713
9269
  const message = composeMessage(fields, values);
@@ -8736,22 +9292,22 @@ function ContactForm({
8736
9292
  event.preventDefault();
8737
9293
  void send();
8738
9294
  }
8739
- return /* @__PURE__ */ jsxs26(
9295
+ return /* @__PURE__ */ jsxs27(
8740
9296
  "section",
8741
9297
  {
8742
9298
  "aria-label": title,
8743
9299
  "data-median-widget": "",
8744
9300
  className: cn(
8745
- "flex w-full flex-col overflow-hidden rounded-xl border bg-card p-5 text-card-foreground [--radius:0.625rem]",
9301
+ `flex w-full flex-col overflow-hidden ${WIDGET_CORNER} border bg-card p-5 text-card-foreground [--radius:0.625rem]`,
8746
9302
  className
8747
9303
  ),
8748
9304
  children: [
8749
- /* @__PURE__ */ jsxs26("header", { className: "flex flex-col gap-0.5 pb-4", children: [
9305
+ /* @__PURE__ */ jsxs27("header", { className: "flex flex-col gap-0.5 pb-4", children: [
8750
9306
  /* @__PURE__ */ jsx48("h2", { className: "text-lg font-semibold tracking-tight", children: title }),
8751
9307
  /* @__PURE__ */ jsx48("p", { className: "text-sm text-muted-foreground", children: description })
8752
9308
  ] }),
8753
- /* @__PURE__ */ jsxs26("div", { className: "grid", children: [
8754
- /* @__PURE__ */ jsxs26(
9309
+ /* @__PURE__ */ jsxs27("div", { className: "grid", children: [
9310
+ /* @__PURE__ */ jsxs27(
8755
9311
  "form",
8756
9312
  {
8757
9313
  ref: formRef,
@@ -8778,9 +9334,9 @@ function ContactForm({
8778
9334
  field.name
8779
9335
  )),
8780
9336
  notice && /* @__PURE__ */ jsx48("p", { className: "animate-in px-1 text-sm text-destructive fade-in duration-150", children: notice }),
8781
- /* @__PURE__ */ jsxs26("div", { className: "flex min-h-10 flex-wrap items-center gap-3 pt-1", children: [
9337
+ /* @__PURE__ */ jsxs27("div", { className: "flex min-h-10 flex-wrap items-center gap-3 pt-1", children: [
8782
9338
  showBadge && /* @__PURE__ */ jsx48(PoweredByMedian, { className: "justify-start" }),
8783
- /* @__PURE__ */ jsxs26(
9339
+ /* @__PURE__ */ jsxs27(
8784
9340
  WidgetButton,
8785
9341
  {
8786
9342
  ref: sendRef,
@@ -8832,15 +9388,15 @@ function ContactForm({
8832
9388
  THANKS_SWAP,
8833
9389
  isSent ? "duration-0" : "pointer-events-none opacity-0 duration-200"
8834
9390
  ),
8835
- children: sent !== null && /* @__PURE__ */ jsxs26("div", { className: "contents", children: [
9391
+ children: sent !== null && /* @__PURE__ */ jsxs27("div", { className: "contents", children: [
8836
9392
  /* @__PURE__ */ jsx48(SentMark, { from: sent.from }),
8837
- /* @__PURE__ */ jsxs26(
9393
+ /* @__PURE__ */ jsxs27(
8838
9394
  "div",
8839
9395
  {
8840
9396
  className: cn("flex max-w-72 flex-col gap-1", ARRIVES2, "delay-[400ms]"),
8841
9397
  children: [
8842
9398
  /* @__PURE__ */ jsx48("p", { className: "text-xl font-semibold tracking-tight", children: "Message sent" }),
8843
- /* @__PURE__ */ jsxs26("p", { className: "text-base text-balance text-muted-foreground", children: [
9399
+ /* @__PURE__ */ jsxs27("p", { className: "text-base text-balance text-muted-foreground", children: [
8844
9400
  "Replies go to ",
8845
9401
  sent.email,
8846
9402
  "."
@@ -8848,7 +9404,7 @@ function ContactForm({
8848
9404
  ]
8849
9405
  }
8850
9406
  ),
8851
- /* @__PURE__ */ jsxs26(
9407
+ /* @__PURE__ */ jsxs27(
8852
9408
  "div",
8853
9409
  {
8854
9410
  className: cn(
@@ -8882,10 +9438,10 @@ function ContactForm({
8882
9438
  }
8883
9439
 
8884
9440
  // ../../apps/web/src/components/support-widget/sdk/use-median-contact.ts
8885
- import { useCallback as useCallback16, useMemo as useMemo4, useState as useState30, useSyncExternalStore as useSyncExternalStore14 } from "react";
9441
+ import { useCallback as useCallback17, useMemo as useMemo4, useState as useState31, useSyncExternalStore as useSyncExternalStore17 } from "react";
8886
9442
 
8887
9443
  // ../../apps/web/src/components/support-widget/sdk/contact.ts
8888
- import { ConvexHttpClient as ConvexHttpClient2 } from "convex/browser";
9444
+ import { ConvexHttpClient as ConvexHttpClient3 } from "convex/browser";
8889
9445
  var client2 = null;
8890
9446
  async function deliverContact(options, message) {
8891
9447
  const { apiKey, sessionId, user, telemetry = true } = options;
@@ -8898,11 +9454,11 @@ async function deliverContact(options, message) {
8898
9454
  "The Median SDK was built without a deployment URL. Set NEXT_PUBLIC_CONVEX_URL."
8899
9455
  );
8900
9456
  }
8901
- client2 ??= new ConvexHttpClient2(MEDIAN_DEPLOYMENT_URL);
9457
+ client2 ??= new ConvexHttpClient3(MEDIAN_DEPLOYMENT_URL);
8902
9458
  const context = telemetry ? collectContext() : void 0;
8903
9459
  const page = telemetry ? collectPage() : void 0;
8904
9460
  const { facts, claim } = splitIdentity(user ?? null);
8905
- await client2.mutation(api.widget.send, {
9461
+ await client2.mutation(api.widget.sendV2, {
8906
9462
  apiKey,
8907
9463
  sessionId,
8908
9464
  body: composeBody(message),
@@ -8913,8 +9469,10 @@ async function deliverContact(options, message) {
8913
9469
  ...context ? { context } : {},
8914
9470
  ...page ? { page } : {},
8915
9471
  ...options.diagnostics ? { diagnostics: options.diagnostics } : {},
8916
- ...custom ? { customData: custom } : {}
8917
- });
9472
+ ...custom ? { customData: custom } : {},
9473
+ // Replies go to the address they typed: nothing on the page shows one.
9474
+ fromContactForm: true
9475
+ }).then(unwrapMutationResult);
8918
9476
  } catch (caught) {
8919
9477
  if (custom) restoreCustomData(custom);
8920
9478
  console.error("Median: sending a contact message failed", caught);
@@ -8933,24 +9491,29 @@ async function sendMedianContact(options, message) {
8933
9491
  }
8934
9492
 
8935
9493
  // ../../apps/web/src/components/support-widget/sdk/use-median-contact.ts
8936
- function useMedianContact(options) {
9494
+ function useMedianContact(options, caller = "useMedianContact()") {
8937
9495
  const { apiKey, identity, user, telemetry, diagnostics, appState } = options;
8938
9496
  const resolved = useMedianIdentity(identity);
8939
- useMissingCredentials("useMedianContact", apiKey, identity);
9497
+ useMissingCredentials(caller, apiKey, identity, "it cannot send");
8940
9498
  const key = apiKey ?? resolved.apiKey;
8941
- useApiKeyShapeWarning("useMedianContact", key);
8942
- const person = user ?? resolved.user;
9499
+ const showBranding = useMedianBranding(key);
9500
+ useApiKeyShapeWarning(caller, key);
9501
+ useCustomDataSender();
9502
+ const person = useSignedInUser(user ?? resolved.user);
8943
9503
  useRememberInstall(key, person);
8944
- const sessionId = useSyncExternalStore14(
9504
+ const sessionId = useSyncExternalStore17(
8945
9505
  subscribeSession,
8946
- useCallback16(() => key === void 0 ? null : getOrCreateSessionId(key), [key]),
9506
+ useCallback17(
9507
+ () => key === void 0 ? null : getOrCreateSessionId(key),
9508
+ [key]
9509
+ ),
8947
9510
  () => null
8948
9511
  );
8949
9512
  const takeDiagnostics = useDiagnostics(diagnostics, appState);
8950
9513
  const [knownEmail] = useVisitorEmail(key ?? "");
8951
- const [isSending, setSending] = useState30(false);
8952
- const [error, setError] = useState30(null);
8953
- const send = useCallback16(
9514
+ const [isSending, setSending] = useState31(false);
9515
+ const [error, setError] = useState31(null);
9516
+ const send = useCallback17(
8954
9517
  async (message) => {
8955
9518
  setError(null);
8956
9519
  if (key === void 0 || sessionId === null) {
@@ -8987,6 +9550,7 @@ function useMedianContact(options) {
8987
9550
  }, [person, knownEmail]);
8988
9551
  return {
8989
9552
  send,
9553
+ showBranding,
8990
9554
  isSending,
8991
9555
  error,
8992
9556
  isReady: key !== void 0 && sessionId !== null,
@@ -8997,14 +9561,16 @@ function useMedianContact(options) {
8997
9561
  // ../../apps/web/src/components/support-widget/sdk/median-contact-form.tsx
8998
9562
  import { jsx as jsx49 } from "react/jsx-runtime";
8999
9563
  function MedianContactForm(props) {
9000
- return /* @__PURE__ */ jsx49(MedianErrorBoundary, { children: /* @__PURE__ */ jsx49(ConnectedContactForm, { ...props }) });
9564
+ return /* @__PURE__ */ jsx49(MedianErrorBoundary, { component: "MedianContactForm", children: /* @__PURE__ */ jsx49(ConnectedContactForm, { ...props }) });
9001
9565
  }
9002
9566
  function useFieldComplaints(fields) {
9003
9567
  const key = fields === void 0 ? null : JSON.stringify(fields);
9004
- useEffect27(() => {
9568
+ useEffect31(() => {
9005
9569
  if (key === null) return;
9006
9570
  for (const complaint of fieldComplaints(JSON.parse(key))) {
9007
- console.error(`Median: in the fields passed to <MedianContactForm>, ${complaint}`);
9571
+ console.error(
9572
+ `Median: in the fields passed to <MedianContactForm>, ${complaint}`
9573
+ );
9008
9574
  }
9009
9575
  }, [key]);
9010
9576
  }
@@ -9015,7 +9581,7 @@ function ConnectedContactForm({
9015
9581
  onSent,
9016
9582
  ...options
9017
9583
  }) {
9018
- const contact = useMedianContact(options);
9584
+ const contact = useMedianContact(options, "<MedianContactForm>");
9019
9585
  useFieldComplaints(fields);
9020
9586
  const knownEmail = contact.user?.email || void 0;
9021
9587
  const knownName = contact.user?.name || void 0;
@@ -9029,10 +9595,18 @@ function ConnectedContactForm({
9029
9595
  return /* @__PURE__ */ jsx49(
9030
9596
  ContactForm,
9031
9597
  {
9598
+ showBadge: contact.showBranding,
9032
9599
  fields,
9033
9600
  onSubmit: async (message) => {
9034
9601
  await contact.send(message);
9035
- onSent?.();
9602
+ try {
9603
+ onSent?.();
9604
+ } catch (caught) {
9605
+ console.error(
9606
+ "Median: onSent threw after the message was sent.",
9607
+ caught
9608
+ );
9609
+ }
9036
9610
  },
9037
9611
  notice: contact.error,
9038
9612
  initialValues,