@mediansh/widget 0.7.0 → 0.8.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 useEffect24 } 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,16 +63,16 @@ 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: r38,
73
+ color: a29 = "currentColor",
74
+ size: t11 = "1em",
75
+ weight: o46 = "regular",
74
76
  mirrored: i3 = false,
75
77
  children: n,
76
78
  weights: c8,
@@ -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: t11,
87
+ height: t11,
88
+ fill: a29,
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),
93
+ !!r38 && /* @__PURE__ */ e.createElement("title", null, r38),
92
94
  n,
93
- c8.get(o43)
95
+ c8.get(o46)
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((e56, t11) => /* @__PURE__ */ o.createElement(w, { ref: t11, ...e56, 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((t11, e56) => /* @__PURE__ */ r2.createElement(w, { ref: e56, ...t11, 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((e56, t11) => /* @__PURE__ */ r3.createElement(w, { ref: t11, ...e56, 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((e56, s4) => /* @__PURE__ */ o4.createElement(w, { ref: s4, ...e56, 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((e56, s4) => /* @__PURE__ */ r5.createElement(w, { ref: s4, ...e56, 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((t11, e56) => /* @__PURE__ */ r6.createElement(w, { ref: e56, ...t11, 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((r38, t11) => /* @__PURE__ */ o7.createElement(w, { ref: t11, ...r38, 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((r38, t11) => /* @__PURE__ */ o8.createElement(w, { ref: t11, ...r38, 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((r38, o46) => /* @__PURE__ */ t3.createElement(w, { ref: o46, ...r38, 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((e56, r38) => /* @__PURE__ */ t4.createElement(w, { ref: r38, ...e56, 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((r38, t11) => /* @__PURE__ */ e16.createElement(w, { ref: t11, ...r38, 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((r38, t11) => /* @__PURE__ */ e20.createElement(w, { ref: t11, ...r38, 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((r38, o46) => /* @__PURE__ */ e22.createElement(w, { ref: o46, ...r38, 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((e56, r38) => /* @__PURE__ */ t5.createElement(w, { ref: r38, ...e56, 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, r38) => /* @__PURE__ */ o14.createElement(w, { ref: r38, ...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((e56, r38) => /* @__PURE__ */ o15.createElement(w, { ref: r38, ...e56, 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((r38, s4) => /* @__PURE__ */ o16.createElement(w, { ref: s4, ...r38, 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((o46, a29) => /* @__PURE__ */ e29.createElement(w, { ref: a29, ...o46, 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((o46, a29) => /* @__PURE__ */ r8.createElement(w, { ref: a29, ...o46, 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((o46, s4) => /* @__PURE__ */ e31.createElement(w, { ref: s4, ...o46, 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((o46, a29) => /* @__PURE__ */ r10.createElement(w, { ref: a29, ...o46, 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((r38, t11) => /* @__PURE__ */ o17.createElement(w, { ref: t11, ...r38, 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";
@@ -1144,57 +1153,57 @@ import * as React3 from "react";
1144
1153
 
1145
1154
  // ../../node_modules/.bun/@shadcn+react@0.3.0+03aa47d54245a271/node_modules/@shadcn/react/dist/chunk-HBS6WEDP.js
1146
1155
  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;
1156
+ function S({ defaultTagName: a29, props: r38, render: t11, state: n = {}, stateAttributesMapping: e56 }) {
1157
+ let o46 = d(R(n, e56), r38);
1158
+ if (!t11) return i2.createElement(a29, o46);
1159
+ if (typeof t11 == "function") return t11(o46, n);
1160
+ if (!i2.isValidElement(t11)) return null;
1161
+ let s4 = t11.props, u = { ...d(o46, s4), ref: f8(o46.ref, s4.ref) };
1162
+ return i2.cloneElement(t11, u);
1163
+ }
1164
+ function d(...a29) {
1165
+ let r38 = {};
1166
+ for (let t11 of a29) {
1167
+ if (!t11) continue;
1168
+ let n = t11;
1169
+ for (let e56 of Object.keys(n)) {
1170
+ let o46 = n[e56];
1171
+ if (o46 === void 0) continue;
1172
+ let s4 = r38[e56];
1173
+ e56 === "className" ? r38[e56] = [s4, o46].filter(Boolean).join(" ") : e56 === "style" ? r38[e56] = { ...s4, ...o46 } : e56 === "ref" ? r38[e56] = f8(s4, o46) : l4(e56) && typeof s4 == "function" && typeof o46 == "function" ? r38[e56] = p2(o46, s4) : r38[e56] = o46;
1165
1174
  }
1166
1175
  }
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);
1176
+ return r38;
1177
+ }
1178
+ function R(a29, r38) {
1179
+ let t11 = {};
1180
+ for (let n of Object.keys(a29)) {
1181
+ let e56 = a29[n], o46 = r38?.[n]?.(e56);
1182
+ if (o46) {
1183
+ Object.assign(t11, o46);
1175
1184
  continue;
1176
1185
  }
1177
1186
  if (n === "slot") {
1178
- t10["data-slot"] = e55;
1187
+ t11["data-slot"] = e56;
1179
1188
  continue;
1180
1189
  }
1181
1190
  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));
1191
+ typeof e56 == "boolean" ? t11[s4] = e56 ? "" : void 0 : e56 != null && (t11[s4] = String(e56));
1183
1192
  }
1184
- return t10;
1193
+ return t11;
1185
1194
  }
1186
- function p2(a28, r32) {
1195
+ function p2(a29, r38) {
1187
1196
  return function(n) {
1188
- a28(n), n.defaultPrevented || r32(n);
1197
+ a29(n), n.defaultPrevented || r38(n);
1189
1198
  };
1190
1199
  }
1191
- function l4(a28) {
1192
- return /^on[A-Z]/.test(a28);
1200
+ function l4(a29) {
1201
+ return /^on[A-Z]/.test(a29);
1193
1202
  }
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);
1203
+ function f8(...a29) {
1204
+ let r38 = a29.filter(Boolean);
1205
+ if (r38.length !== 0) return (t11) => {
1206
+ for (let n of r38) typeof n == "function" ? n(t11) : n && (n.current = t11);
1198
1207
  };
1199
1208
  }
1200
1209
 
@@ -1210,15 +1219,15 @@ var Be = /* @__PURE__ */ new Set(["ArrowDown", "ArrowUp", "End", "Home", "PageDo
1210
1219
  var ge = { start: false, end: false };
1211
1220
  var dt = [];
1212
1221
  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)) {
1222
+ function De({ content: e56, scrollEdgeThreshold: t11, spacer: r38, viewport: n }) {
1223
+ if (!n || !e56) return ge;
1224
+ let o46 = Se({ content: e56, spacer: r38, viewport: n });
1225
+ return { start: n.scrollTop > t11, end: o46 - n.scrollTop - n.clientHeight > t11 };
1226
+ }
1227
+ function Fe({ content: e56, scrollMargin: t11, scrollPreviousItemPeek: r38, spacer: n, viewport: o46, visibleMessageIds: i3 }) {
1228
+ if (!e56 || !o46) return Q;
1229
+ let S2 = o46.getBoundingClientRect(), l5 = S2.top + t11 + r38, d2 = typeof IntersectionObserver > "u", E = [], b2 = null;
1230
+ for (let C of ee(e56, n)) {
1222
1231
  let p3 = C.dataset.messageId;
1223
1232
  if (!p3) continue;
1224
1233
  let m5 = C.dataset.scrollAnchor === "true", L = m5 || d2 ? C.getBoundingClientRect() : null;
@@ -1226,100 +1235,100 @@ function Fe({ content: e55, scrollMargin: t10, scrollPreviousItemPeek: r32, spac
1226
1235
  }
1227
1236
  return E.length === 0 && b2 === null ? Q : { currentAnchorId: b2, visibleMessageIds: E };
1228
1237
  }
1229
- function ee(e55, t10) {
1230
- return Array.from(e55.children).filter((r32) => r32 instanceof HTMLElement && r32 !== t10);
1238
+ function ee(e56, t11) {
1239
+ return Array.from(e56.children).filter((r38) => r38 instanceof HTMLElement && r38 !== t11);
1231
1240
  }
1232
- function je(e55, t10) {
1233
- for (let r32 = t10; r32 < e55.length; r32++) {
1234
- let n = e55[r32];
1241
+ function je(e56, t11) {
1242
+ for (let r38 = t11; r38 < e56.length; r38++) {
1243
+ let n = e56[r38];
1235
1244
  if (n?.dataset.scrollAnchor === "true") return n;
1236
1245
  }
1237
1246
  return null;
1238
1247
  }
1239
- function Ge(e55, t10) {
1240
- for (let r32 of e55) if (r32.dataset.scrollAnchor === "true" && !t10.has(r32)) return r32;
1248
+ function Ge(e56, t11) {
1249
+ for (let r38 of e56) if (r38.dataset.scrollAnchor === "true" && !t11.has(r38)) return r38;
1241
1250
  return null;
1242
1251
  }
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;
1252
+ function Ne(e56, t11) {
1253
+ let r38 = 0;
1254
+ for (let n = t11; n < e56.length; n++) if (e56[n]?.dataset.scrollAnchor === "true" && (r38 += 1, r38 > 1)) return true;
1246
1255
  return false;
1247
1256
  }
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;
1257
+ function Ue(e56) {
1258
+ for (let t11 = e56.length - 1; t11 >= 0; t11--) {
1259
+ let r38 = e56[t11];
1260
+ if (r38?.dataset.scrollAnchor === "true") return r38;
1252
1261
  }
1253
1262
  return null;
1254
1263
  }
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;
1264
+ function Ye({ content: e56, spacer: t11, viewport: r38 }) {
1265
+ let n = r38.getBoundingClientRect();
1266
+ for (let o46 of ee(e56, t11)) {
1267
+ if (!o46.dataset.messageId) continue;
1268
+ let i3 = o46.getBoundingClientRect();
1269
+ if (i3.bottom > n.top && i3.top < n.bottom) return o46;
1261
1270
  }
1262
1271
  return null;
1263
1272
  }
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;
1273
+ function ze({ align: e56, element: t11, scrollMargin: r38, spacer: n, viewport: o46 }) {
1274
+ let i3 = pe(t11, o46), S2 = t11.getBoundingClientRect().height, l5 = bt(n);
1275
+ if (e56 === "center") {
1276
+ let d2 = Math.max(0, o46.clientHeight - l5.start - l5.end);
1277
+ return i3 - l5.start - (d2 - S2) / 2 - r38;
1269
1278
  }
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;
1279
+ if (e56 === "end") return i3 - o46.clientHeight + S2 + l5.end + r38;
1280
+ if (e56 === "nearest") {
1281
+ let d2 = i3 + S2, E = o46.scrollTop + l5.start, b2 = o46.scrollTop + o46.clientHeight - l5.end;
1282
+ return i3 >= E && d2 <= b2 ? o46.scrollTop : i3 < E ? i3 - l5.start - r38 : d2 - o46.clientHeight + l5.end + r38;
1274
1283
  }
1275
- return i3 - l5.start - r32;
1284
+ return i3 - l5.start - r38;
1276
1285
  }
1277
- function pe(e55, t10) {
1278
- let r32 = e55.getBoundingClientRect(), n = t10.getBoundingClientRect();
1279
- return r32.top - n.top + t10.scrollTop;
1286
+ function pe(e56, t11) {
1287
+ let r38 = e56.getBoundingClientRect(), n = t11.getBoundingClientRect();
1288
+ return r38.top - n.top + t11.scrollTop;
1280
1289
  }
1281
- function te(e55, t10) {
1282
- return e55.getBoundingClientRect().top - t10.getBoundingClientRect().top;
1290
+ function te(e56, t11) {
1291
+ return e56.getBoundingClientRect().top - t11.getBoundingClientRect().top;
1283
1292
  }
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;
1293
+ function qe({ content: e56, scrollTop: t11, spacer: r38, viewport: n }) {
1294
+ let o46 = Se({ content: e56, spacer: r38, viewport: n });
1295
+ return t11 + n.clientHeight - o46;
1287
1296
  }
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;
1297
+ function Se({ content: e56, spacer: t11, viewport: r38 }) {
1298
+ let n = ee(e56, t11), o46 = We(e56), i3 = r38.getBoundingClientRect(), S2 = r38.scrollTop, l5 = o46.start + o46.end;
1290
1299
  for (let d2 of n) {
1291
1300
  let E = d2.getBoundingClientRect();
1292
- l5 = Math.max(l5, E.bottom - i3.top + S2 + o43.end);
1301
+ l5 = Math.max(l5, E.bottom - i3.top + S2 + o46.end);
1293
1302
  }
1294
1303
  return l5;
1295
1304
  }
1296
- function Ke(e55) {
1297
- return Math.max(0, e55.scrollHeight - e55.clientHeight);
1305
+ function Ke(e56) {
1306
+ return Math.max(0, e56.scrollHeight - e56.clientHeight);
1298
1307
  }
1299
- function We(e55) {
1300
- let t10 = window.getComputedStyle(e55);
1301
- return { end: Re(t10.paddingBlockEnd || t10.paddingBottom), start: Re(t10.paddingBlockStart || t10.paddingTop) };
1308
+ function We(e56) {
1309
+ let t11 = window.getComputedStyle(e56);
1310
+ return { end: Re(t11.paddingBlockEnd || t11.paddingBottom), start: Re(t11.paddingBlockStart || t11.paddingTop) };
1302
1311
  }
1303
- function bt(e55) {
1304
- let t10 = e55?.parentElement;
1305
- return t10 ? We(t10) : { end: 0, start: 0 };
1312
+ function bt(e56) {
1313
+ let t11 = e56?.parentElement;
1314
+ return t11 ? We(t11) : { end: 0, start: 0 };
1306
1315
  }
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);
1316
+ function $e(e56) {
1317
+ if (!e56) return 0;
1318
+ let t11 = window.getComputedStyle(e56), r38 = t11.rowGap === "normal" ? t11.gap : t11.rowGap;
1319
+ return Re(r38);
1311
1320
  }
1312
- function Re(e55) {
1313
- if (!e55) return 0;
1314
- let t10 = Number.parseFloat(e55);
1315
- return Number.isFinite(t10) ? t10 : 0;
1321
+ function Re(e56) {
1322
+ if (!e56) return 0;
1323
+ let t11 = Number.parseFloat(e56);
1324
+ return Number.isFinite(t11) ? t11 : 0;
1316
1325
  }
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();
1326
+ function Je({ refs: e56, commitScrollState: t11, scheduleStateCommit: r38, scheduleVisibilitySync: n }) {
1327
+ let { streamingTurnRef: o46, 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: _ } = e56, G = g.useCallback((c8) => {
1328
+ l5.current !== null && (window.clearTimeout(l5.current), l5.current = null), S2.current !== c8 && (S2.current = c8, t11()), c8 && (l5.current = window.setTimeout(() => {
1329
+ l5.current = null, S2.current = false, t11();
1321
1330
  }, ke));
1322
- }, [t10]), v = g.useCallback((c8) => {
1331
+ }, [t11]), v = g.useCallback((c8) => {
1323
1332
  let f9 = k.current;
1324
1333
  if (!f9) return;
1325
1334
  let h = Math.max(0, Math.ceil(c8));
@@ -1329,20 +1338,20 @@ function Je({ refs: e55, commitScrollState: t10, scheduleStateCommit: r32, sched
1329
1338
  if (!P) return;
1330
1339
  let V = Math.max(0, c8);
1331
1340
  if (Math.abs(P.scrollTop - V) <= J) {
1332
- P.scrollTop = V, t10();
1341
+ P.scrollTop = V, t11();
1333
1342
  return;
1334
1343
  }
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" } = {}) => {
1344
+ h && G(true), P.scrollTo({ top: V, behavior: f9 }), r38();
1345
+ }, [t11, r38, G]), N = g.useCallback(({ behavior: c8 = "auto" } = {}) => _.current ? (v(0), o46.current = null, p3.current = "free-scrolling", H(0, { behavior: c8 }), n(), true) : false, [n, H, v]), ne = g.useCallback(({ behavior: c8 = "auto" } = {}) => {
1337
1346
  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;
1347
+ return f9 ? (v(0), o46.current = null, p3.current = i3.current ? "following-bottom" : "free-scrolling", H(Ke(f9), { autoscrolling: true, behavior: c8 }), n(), true) : false;
1339
1348
  }, [n, H, v]), A = g.useCallback((c8, { align: f9 = "start", behavior: h = "auto", scrollMargin: P = y.current } = {}, { keepPreviousPeek: V = false } = {}) => {
1340
1349
  let z = d2.current, q = _.current;
1341
1350
  if (!z || !q || !z.contains(c8)) return false;
1342
1351
  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;
1352
+ return v(se), L.current = { element: c8, viewportTop: te(c8, q) }, p3.current = V ? "anchored-to-message" : "settling-jump", o46.current = V ? c8 : null, H(K, { behavior: h }), n(), true;
1344
1353
  }, [n, H, v]), B = g.useCallback(() => {
1345
- let c8 = o43.current;
1354
+ let c8 = o46.current;
1346
1355
  return !c8 || !c8.isConnected || p3.current !== "anchored-to-message" ? false : A(c8, { align: "start" }, { keepPreviousPeek: true });
1347
1356
  }, [A]), D = g.useCallback((c8, f9) => {
1348
1357
  let h = C.current.get(c8);
@@ -1355,52 +1364,52 @@ function Je({ refs: e55, commitScrollState: t10, scheduleStateCommit: r32, sched
1355
1364
  return !f9 || !A(f9, c8.options) ? false : (m5.current = null, E.current = true, true);
1356
1365
  }, [A]), reanchorToAnchoredMessage: B, scrollToElement: A, scrollToEnd: ne, scrollToMessage: D, scrollToStart: N };
1357
1366
  }
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) => {
1367
+ function Qe(e56, t11) {
1368
+ let r38 = e56, n = /* @__PURE__ */ new Set();
1369
+ return { getSnapshot: () => r38, hasListeners: () => n.size > 0, setSnapshot: (o46) => {
1370
+ t11(r38, o46) || (r38 = o46, n.forEach((i3) => i3()));
1371
+ }, subscribe: (o46, i3, S2) => {
1363
1372
  let l5 = n.size === 0;
1364
- return n.add(o43), l5 && i3?.(), () => {
1365
- n.delete(o43), n.size === 0 && S2?.();
1373
+ return n.add(o46), l5 && i3?.(), () => {
1374
+ n.delete(o46), n.size === 0 && S2?.();
1366
1375
  };
1367
1376
  } };
1368
1377
  }
1369
- function Xe(e55, t10) {
1370
- return Qe(e55, t10);
1378
+ function Xe(e56, t11) {
1379
+ return Qe(e56, t11);
1371
1380
  }
1372
1381
  function Ze() {
1373
1382
  return Qe(Q, Tt);
1374
1383
  }
1375
- function et(e55, t10) {
1376
- return e55.start === t10.start && e55.end === t10.end;
1384
+ function et(e56, t11) {
1385
+ return e56.start === t11.start && e56.end === t11.end;
1377
1386
  }
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]);
1387
+ function Tt(e56, t11) {
1388
+ return e56.currentAnchorId !== t11.currentAnchorId || e56.visibleMessageIds.length !== t11.visibleMessageIds.length ? false : e56.visibleMessageIds.every((r38, n) => r38 === t11.visibleMessageIds[n]);
1380
1389
  }
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 };
1390
+ function tt({ autoScroll: e56, scrollEdgeThreshold: t11, scrollMargin: r38, scrollPreviousItemPeek: n }) {
1391
+ let o46 = g.useRef(e56), i3 = g.useRef(false), S2 = g.useRef(null), l5 = g.useRef(false), d2 = g.useRef(t11), E = g.useRef(0), b2 = g.useRef(null), C = g.useRef(0), p3 = g.useRef(e56 ? "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(r38), 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());
1392
+ return A.current === null && (A.current = Xe(ge, et)), f9.current === null && (f9.current = Ze()), o46.current = e56, d2.current = t11, _.current = r38, x.current = n, { autoScrollRef: o46, 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
1393
  }
1385
- function rt(e55, t10) {
1386
- return g.useCallback((r32) => {
1387
- e55.current = r32, r32 && t10();
1388
- }, [e55, t10]);
1394
+ function rt(e56, t11) {
1395
+ return g.useCallback((r38) => {
1396
+ e56.current = r38, r38 && t11();
1397
+ }, [e56, t11]);
1389
1398
  }
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);
1399
+ function nt({ autoScroll: e56 = false, defaultScrollPosition: t11 = "end", scrollEdgeThreshold: r38 = xe, scrollPreviousItemPeek: n = Ve, scrollMargin: o46 = _e }) {
1400
+ let i3 = tt({ autoScroll: e56, scrollEdgeThreshold: r38, scrollMargin: o46, 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(t11);
1401
+ q.current !== t11 && (q.current = t11, C.current = false);
1393
1402
  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));
1403
+ let a29 = G.current, T = c8.current, O = [s4.start && "start", s4.end && "end"].filter(Boolean).join(" "), ue = d2.current;
1404
+ for (let W of [a29, T]) W && (O ? W.setAttribute("data-scrollable", O) : W.removeAttribute("data-scrollable"), W.toggleAttribute("data-autoscrolling", ue));
1396
1405
  }, []), 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");
1406
+ let a29 = c8.current?.scrollTop ?? 0, T = a29 < L.current - J;
1407
+ L.current = a29, 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
1408
  }, []), F = g.useCallback(() => {
1400
1409
  let s4 = De({ content: b2.current, scrollEdgeThreshold: v.current, spacer: B.current, viewport: c8.current });
1401
1410
  se(s4);
1402
- let a28 = u.current === "following-bottom" ? { ...s4, end: false } : s4;
1403
- K(a28), U.setSnapshot(a28);
1411
+ let a29 = u.current === "following-bottom" ? { ...s4, end: false } : s4;
1412
+ K(a29), U.setSnapshot(a29);
1404
1413
  }, [se, U, K]), oe = g.useCallback(() => {
1405
1414
  D.current === null && (D.current = window.requestAnimationFrame(() => {
1406
1415
  D.current = null, F();
@@ -1410,54 +1419,54 @@ function nt({ autoScroll: e55 = false, defaultScrollPosition: t10 = "end", scrol
1410
1419
  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
1420
  }));
1412
1421
  }, [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);
1422
+ let s4 = k.current, a29 = c8.current;
1423
+ if (!s4 || !a29 || !s4.element.isConnected) return false;
1424
+ let O = te(s4.element, a29) - s4.viewportTop;
1425
+ return Math.abs(O) <= J ? false : (a29.scrollTop += O, s4.viewportTop = te(s4.element, a29), oe(), w2(), true);
1417
1426
  }, [oe, w2]), Z = g.useCallback(() => {
1418
- let s4 = b2.current, a28 = c8.current;
1419
- if (!s4 || !a28) {
1427
+ let s4 = b2.current, a29 = c8.current;
1428
+ if (!s4 || !a29) {
1420
1429
  k.current = null;
1421
1430
  return;
1422
1431
  }
1423
- let T = Ye({ content: s4, spacer: B.current, viewport: a28 });
1424
- k.current = T ? { element: T, viewportTop: te(T, a28) } : null;
1432
+ let T = Ye({ content: s4, spacer: B.current, viewport: a29 });
1433
+ k.current = T ? { element: T, viewportTop: te(T, a29) } : null;
1425
1434
  }, []), be = g.useCallback(() => {
1426
1435
  x.current === null && (x.current = window.requestAnimationFrame(() => {
1427
1436
  x.current = null, ce() && Z();
1428
1437
  }));
1429
1438
  }, [Z, ce]), ie = g.useCallback(() => {
1430
- if (!t10 || C.current || m5.current === 0) return false;
1439
+ if (!t11 || C.current || m5.current === 0) return false;
1431
1440
  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" });
1441
+ if (t11 === "last-anchor") {
1442
+ let a29 = b2.current, T = c8.current, O = a29 && T ? Ue(ee(a29, B.current)) : null;
1443
+ if (!a29 || !T || !O) s4 = I2({ behavior: "auto" });
1435
1444
  else {
1436
1445
  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 });
1446
+ s4 = Se({ content: a29, spacer: B.current, viewport: T }) - ue <= T.clientHeight ? I2({ behavior: "auto" }) : le(O, { align: "start" }, { keepPreviousPeek: true });
1438
1447
  }
1439
- } else s4 = t10 === "end" ? I2({ behavior: "auto" }) : ae({ behavior: "auto" });
1448
+ } else s4 = t11 === "end" ? I2({ behavior: "auto" }) : ae({ behavior: "auto" });
1440
1449
  return s4 ? (C.current = true, true) : false;
1441
- }, [t10, le, I2, ae]), Te = g.useCallback(() => {
1450
+ }, [t11, le, I2, ae]), Te = g.useCallback(() => {
1442
1451
  let s4 = b2.current;
1443
1452
  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, (() => {
1453
+ let a29 = ee(s4, B.current), T = m5.current, O = p3.current;
1454
+ m5.current = a29.length, p3.current = a29[0] ?? null, (() => {
1446
1455
  if (ce()) return;
1447
1456
  if (T === 0) {
1448
- if (ie() || a28.length > 0 && l5.current && I2({ behavior: "auto" })) return;
1457
+ if (ie() || a29.length > 0 && l5.current && I2({ behavior: "auto" })) return;
1449
1458
  F(), w2();
1450
1459
  return;
1451
1460
  }
1452
- let W = O ? a28.indexOf(O) : -1;
1461
+ let W = O ? a29.indexOf(O) : -1;
1453
1462
  if (_.current && W > 0) {
1454
1463
  de();
1455
1464
  return;
1456
1465
  }
1457
- if (a28.length > T) {
1458
- let $ = je(a28, T);
1466
+ if (a29.length > T) {
1467
+ let $ = je(a29, T);
1459
1468
  if ($) {
1460
- if (l5.current && u.current === "following-bottom" && Ne(a28, T)) {
1469
+ if (l5.current && u.current === "following-bottom" && Ne(a29, T)) {
1461
1470
  I2({ behavior: "auto" });
1462
1471
  return;
1463
1472
  }
@@ -1465,8 +1474,8 @@ function nt({ autoScroll: e55 = false, defaultScrollPosition: t10 = "end", scrol
1465
1474
  return;
1466
1475
  }
1467
1476
  }
1468
- if (a28.length === T) {
1469
- let $ = Ge(a28, z.current);
1477
+ if (a29.length === T) {
1478
+ let $ = Ge(a29, z.current);
1470
1479
  if ($) {
1471
1480
  le($, { align: "start" }, { keepPreviousPeek: true }), z.current.add($);
1472
1481
  return;
@@ -1492,21 +1501,21 @@ function nt({ autoScroll: e55 = false, defaultScrollPosition: t10 = "end", scrol
1492
1501
  w2();
1493
1502
  return;
1494
1503
  }
1495
- h.current || (h.current = new IntersectionObserver((a28) => {
1496
- for (let T of a28) {
1504
+ h.current || (h.current = new IntersectionObserver((a29) => {
1505
+ for (let T of a29) {
1497
1506
  let O = T.target.dataset.messageId;
1498
1507
  O && (T.isIntersecting ? V.current.add(O) : V.current.delete(O));
1499
1508
  }
1500
1509
  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);
1510
+ }, { root: s4, rootMargin: `${-(H.current + N.current)}px 0px 0px 0px`, threshold: [0, 0.01, 0.5, 1] })), y.current.forEach((a29) => {
1511
+ h.current?.observe(a29);
1503
1512
  }), w2();
1504
1513
  }
1505
1514
  }, [w2, P]), he = g.useCallback(() => {
1506
1515
  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();
1516
+ }, [P]), pt = g.useCallback((s4, a29, T) => {
1517
+ if (a29) {
1518
+ y.current.set(s4, a29), h.current?.observe(a29), w2(), Y.current?.messageId === s4 && be();
1510
1519
  return;
1511
1520
  }
1512
1521
  T && y.current.get(s4) === T && (y.current.delete(s4), V.current.delete(s4), h.current?.unobserve(T), w2());
@@ -1524,38 +1533,38 @@ function nt({ autoScroll: e55 = false, defaultScrollPosition: t10 = "end", scrol
1524
1533
  }, [ie]), g.useEffect(() => () => {
1525
1534
  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
1535
  }, []), g.useLayoutEffect(() => {
1527
- if (e55 && u.current === "following-bottom" && m5.current > 0) {
1536
+ if (e56 && u.current === "following-bottom" && m5.current > 0) {
1528
1537
  I2({ behavior: "auto" });
1529
1538
  return;
1530
1539
  }
1531
1540
  F();
1532
- }, [e55, F, I2]), { context: mt, registerMessage: pt };
1541
+ }, [e56, F, I2]), { context: mt, registerMessage: pt };
1533
1542
  }
1534
- function lt(e55) {
1535
- let t10 = g.useRef(e55);
1536
- return t10.current = e55, t10;
1543
+ function lt(e56) {
1544
+ let t11 = g.useRef(e56);
1545
+ return t11.current = e56, t11;
1537
1546
  }
1538
1547
  var st = g.createContext(null);
1539
1548
  var ct = g.createContext(null);
1540
1549
  function X2() {
1541
- let e55 = g.useContext(st);
1542
- if (!e55) throw new Error("useMessageScroller must be used within a MessageScroller.");
1543
- return e55;
1550
+ let e56 = g.useContext(st);
1551
+ if (!e56) throw new Error("useMessageScroller must be used within a MessageScroller.");
1552
+ return e56;
1544
1553
  }
1545
1554
  function Et() {
1546
- let e55 = g.useContext(ct);
1547
- if (!e55) throw new Error("MessageScrollerItem must be used within a MessageScroller.");
1548
- return e55;
1555
+ let e56 = g.useContext(ct);
1556
+ if (!e56) throw new Error("MessageScrollerItem must be used within a MessageScroller.");
1557
+ return e56;
1549
1558
  }
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 }) });
1559
+ function at({ autoScroll: e56 = false, children: t11, defaultScrollPosition: r38 = "end", scrollEdgeThreshold: n, scrollPreviousItemPeek: o46, scrollMargin: i3 }) {
1560
+ let { context: S2, registerMessage: l5 } = nt({ autoScroll: e56, defaultScrollPosition: r38, scrollEdgeThreshold: n, scrollPreviousItemPeek: o46, scrollMargin: i3 });
1561
+ return jsx3(st.Provider, { value: S2, children: jsx3(ct.Provider, { value: l5, children: t11 }) });
1553
1562
  }
1554
- function it({ children: e55, ...t10 }) {
1555
- let { setRootElement: r32 } = X2();
1556
- return jsx3("div", { ref: r32, ...t10, children: e55 });
1563
+ function it({ children: e56, ...t11 }) {
1564
+ let { setRootElement: r38 } = X2();
1565
+ return jsx3("div", { ref: r38, ...t11, children: e56 });
1557
1566
  }
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 }) {
1567
+ function ut({ "aria-label": e56, children: t11, onKeyDown: r38, onScroll: n, onTouchMove: o46, onWheel: i3, preserveScrollOnPrepend: S2 = true, ref: l5, role: d2, tabIndex: E, ...b2 }) {
1559
1568
  let { handleResize: C, preserveScrollOnPrependRef: p3, setViewportElement: m5, syncAfterScroll: L, userScrollIntent: y, viewportRef: u } = X2();
1560
1569
  p3.current = S2;
1561
1570
  let x = g.useCallback((v) => {
@@ -1568,10 +1577,10 @@ function ut({ "aria-label": e55, children: t10, onKeyDown: r32, onScroll: n, onT
1568
1577
  y(), i3?.(v);
1569
1578
  }
1570
1579
  function _(v) {
1571
- y(), o43?.(v);
1580
+ y(), o46?.(v);
1572
1581
  }
1573
1582
  function G(v) {
1574
- Be.has(v.key) && y(), r32?.(v);
1583
+ Be.has(v.key) && y(), r38?.(v);
1575
1584
  }
1576
1585
  return g.useEffect(() => {
1577
1586
  let v = u.current;
@@ -1582,12 +1591,12 @@ function ut({ "aria-label": e55, children: t10, onKeyDown: r32, onScroll: n, onT
1582
1591
  return N.observe(v), () => {
1583
1592
  window.cancelAnimationFrame(H), N.disconnect();
1584
1593
  };
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 });
1594
+ }, [C, u]), jsx3("div", { ref: x, role: d2 ?? "region", "aria-label": e56 ?? "Messages", tabIndex: E ?? 0, onKeyDown: G, onScroll: Y, onTouchMove: _, onWheel: k, ...b2, children: t11 });
1586
1595
  }
1587
- function ft({ "aria-relevant": e55, children: t10, ref: r32, role: n, spacerClassName: o43, ...i3 }) {
1596
+ function ft({ "aria-relevant": e56, children: t11, ref: r38, role: n, spacerClassName: o46, ...i3 }) {
1588
1597
  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]);
1598
+ b2.current = p3, d2(p3), f8(r38)?.(p3);
1599
+ }, [r38, d2]);
1591
1600
  return g.useLayoutEffect(() => {
1592
1601
  let p3 = b2.current;
1593
1602
  if (!p3 || (S2(), typeof MutationObserver > "u")) return;
@@ -1604,23 +1613,23 @@ function ft({ "aria-relevant": e55, children: t10, ref: r32, role: n, spacerClas
1604
1613
  return L.observe(p3), () => {
1605
1614
  window.cancelAnimationFrame(m5), L.disconnect();
1606
1615
  };
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 })] });
1616
+ }, [l5]), jsxs("div", { ref: C, role: n ?? "log", "aria-relevant": e56 ?? "additions", ...i3, children: [t11, jsx3("div", { ref: E, "aria-hidden": "true", "data-message-scroller-spacer": "", hidden: true, className: o46 })] });
1608
1617
  }
1609
- function gt({ messageId: e55, ref: t10, scrollAnchor: r32 = false, ...n }) {
1610
- let o43 = Et(), i3 = g.useRef(null), S2 = g.useCallback((l5) => {
1618
+ function gt({ messageId: e56, ref: t11, scrollAnchor: r38 = false, ...n }) {
1619
+ let o46 = Et(), i3 = g.useRef(null), S2 = g.useCallback((l5) => {
1611
1620
  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 });
1621
+ i3.current = l5, e56 && o46(e56, l5, d2), f8(t11)?.(l5);
1622
+ }, [e56, t11, o46]);
1623
+ return jsx3("div", { ref: S2, "data-message-id": e56, "data-scroll-anchor": r38 ? "true" : "false", ...n });
1615
1624
  }
1616
- function St({ behavior: e55 = "smooth", children: t10, direction: r32 = "end", onClick: n, render: o43, tabIndex: i3, type: S2 = "button", ...l5 }) {
1625
+ function St({ behavior: e56 = "smooth", children: t11, direction: r38 = "end", onClick: n, render: o46, tabIndex: i3, type: S2 = "button", ...l5 }) {
1617
1626
  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(() => {
1618
1627
  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" }) } });
1628
+ return r38 === "start" ? u.start : u.end;
1629
+ }, [r38, b$1]), L = g.useSyncExternalStore(p3, m5, m5), y = g.useCallback((u) => {
1630
+ L && (C.current?.(u), u.defaultPrevented || (u.currentTarget.blur(), r38 === "start" ? E({ behavior: e56 }) : d2({ behavior: e56 })));
1631
+ }, [e56, r38, L, C, d2, E]);
1632
+ return S({ defaultTagName: "button", props: d({ type: S2, inert: !L, tabIndex: L ? i3 : -1, children: t11 ?? jsxs("span", { children: ["Scroll to ", r38] }), onClick: y }, l5), render: o46, state: { active: L, direction: r38 }, stateAttributesMapping: { active: (u) => ({ "data-active": u ? "true" : "false" }) } });
1624
1633
  }
1625
1634
  var $t = { Provider: at, Root: it, Viewport: ut, Content: ft, Item: gt, Button: St };
1626
1635
 
@@ -1859,14 +1868,14 @@ var use = React2[" use ".trim().toString()];
1859
1868
  // ../../apps/web/src/components/ui/button.tsx
1860
1869
  import { jsx as jsx4 } from "react/jsx-runtime";
1861
1870
  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",
1871
+ "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
1872
  {
1864
1873
  variants: {
1865
1874
  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",
1875
+ default: "surface-primary bg-primary bg-clip-border text-primary-foreground hover:bg-primary/90 active:shadow-field",
1876
+ 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",
1877
+ 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",
1878
+ ghost: "text-muted-foreground hover:bg-muted hover:text-foreground aria-expanded:bg-muted aria-expanded:text-foreground",
1870
1879
  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
1880
  link: "text-primary underline-offset-4 hover:underline"
1872
1881
  },
@@ -1907,34 +1916,6 @@ function Button({
1907
1916
  );
1908
1917
  }
1909
1918
 
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
1919
  // ../../apps/web/src/components/ui/message-scroller.tsx
1939
1920
  import { Fragment as Fragment25, jsx as jsx5, jsxs as jsxs2 } from "react/jsx-runtime";
1940
1921
  function MessageScrollerProvider({
@@ -1969,9 +1950,12 @@ function usePinnedToEnd(viewport) {
1969
1950
  const remember = () => {
1970
1951
  wasAtEnd = node.scrollHeight - node.scrollTop - node.clientHeight <= AT_END_PX;
1971
1952
  };
1953
+ let height = node.clientHeight;
1972
1954
  const observer = new ResizeObserver(() => {
1973
1955
  const drift = node.scrollHeight - node.scrollTop - node.clientHeight;
1974
- if (wasAtEnd && drift > DRIFT_PX) {
1956
+ const shrank = node.clientHeight < height;
1957
+ height = node.clientHeight;
1958
+ if (wasAtEnd && shrank && drift > DRIFT_PX) {
1975
1959
  node.scrollTo({
1976
1960
  top: node.scrollHeight - node.clientHeight,
1977
1961
  behavior: "instant"
@@ -1992,11 +1976,11 @@ var MAX_STEP_MS = 64;
1992
1976
  function prefersReducedMotion() {
1993
1977
  return window.matchMedia("(prefers-reduced-motion: reduce)").matches;
1994
1978
  }
1995
- function useGlidingScroll(viewport, isSettled) {
1996
- const settled = React3.useRef(isSettled);
1979
+ function useGlidingScroll(viewport, hasLanded) {
1980
+ const landed = React3.useRef(hasLanded);
1997
1981
  React3.useLayoutEffect(() => {
1998
- settled.current = isSettled;
1999
- }, [isSettled]);
1982
+ landed.current = hasLanded;
1983
+ }, [hasLanded]);
2000
1984
  React3.useEffect(() => {
2001
1985
  const node = viewport.current;
2002
1986
  if (!node) return;
@@ -2004,6 +1988,7 @@ function useGlidingScroll(viewport, isSettled) {
2004
1988
  let target = null;
2005
1989
  let frame = 0;
2006
1990
  let last = 0;
1991
+ let box = { width: node.clientWidth, height: node.clientHeight };
2007
1992
  const stop = () => {
2008
1993
  if (frame !== 0) cancelAnimationFrame(frame);
2009
1994
  frame = 0;
@@ -2012,11 +1997,12 @@ function useGlidingScroll(viewport, isSettled) {
2012
1997
  const step = (now) => {
2013
1998
  frame = 0;
2014
1999
  if (target === null) return;
2015
- const elapsed = Math.min(now - last, MAX_STEP_MS);
2000
+ const elapsed = Math.max(0, Math.min(now - last, MAX_STEP_MS));
2016
2001
  last = now;
2017
- const left = target - node.scrollTop;
2002
+ const goal = Math.min(target, node.scrollHeight - node.clientHeight);
2003
+ const left = goal - node.scrollTop;
2018
2004
  if (Math.abs(left) < 0.5) {
2019
- node.scrollTop = target;
2005
+ node.scrollTop = goal;
2020
2006
  target = null;
2021
2007
  return;
2022
2008
  }
@@ -2025,7 +2011,10 @@ function useGlidingScroll(viewport, isSettled) {
2025
2011
  };
2026
2012
  const glide = (options, y) => {
2027
2013
  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();
2014
+ const size = { width: node.clientWidth, height: node.clientHeight };
2015
+ const shrank = size.width !== box.width || size.height < box.height;
2016
+ box = size;
2017
+ const glides = asked !== void 0 && typeof asked.top === "number" && asked.behavior !== "instant" && landed.current && !shrank && !prefersReducedMotion();
2029
2018
  if (!glides) {
2030
2019
  stop();
2031
2020
  scrollNatively.call(node, asked);
@@ -2070,10 +2059,8 @@ function MessageScrollerViewport({
2070
2059
  }) {
2071
2060
  const viewport = React3.useRef(null);
2072
2061
  const hasLanded = useHasLanded();
2073
- const isResizing = useIsResizing(viewport);
2074
- const isSettled = hasLanded && !isResizing;
2075
2062
  usePinnedToEnd(viewport);
2076
- useGlidingScroll(viewport, isSettled);
2063
+ useGlidingScroll(viewport, hasLanded);
2077
2064
  const holdViewport = React3.useCallback(
2078
2065
  (node) => {
2079
2066
  viewport.current = node;
@@ -2087,7 +2074,6 @@ function MessageScrollerViewport({
2087
2074
  {
2088
2075
  ref: holdViewport,
2089
2076
  "data-slot": "message-scroller-viewport",
2090
- "data-settled": isSettled ? "" : void 0,
2091
2077
  className: cn(
2092
2078
  "size-full min-h-0 min-w-0 overflow-y-auto overscroll-contain contain-content [scrollbar-gutter:stable] [scrollbar-width:thin]",
2093
2079
  // The fade says "there is more below", so it only belongs to a
@@ -2266,9 +2252,9 @@ var SETTLE_BODY = cn(
2266
2252
 
2267
2253
  // ../../apps/web/src/components/support-widget/widget-composer.tsx
2268
2254
  import {
2269
- useEffect as useEffect6,
2270
- useRef as useRef7,
2271
- useState as useState5
2255
+ useEffect as useEffect5,
2256
+ useRef as useRef8,
2257
+ useState as useState4
2272
2258
  } from "react";
2273
2259
 
2274
2260
  // src/next-image-shim.tsx
@@ -2296,7 +2282,7 @@ import { jsx as jsx8, jsxs as jsxs3 } from "react/jsx-runtime";
2296
2282
  var KIND_ICONS = {
2297
2283
  image: ImageSquare,
2298
2284
  video: Play,
2299
- file: File
2285
+ file: File2
2300
2286
  };
2301
2287
  function AttachmentDraftChip({
2302
2288
  draft,
@@ -2369,7 +2355,7 @@ function AttachmentDraftChip({
2369
2355
  }
2370
2356
 
2371
2357
  // ../../apps/web/src/components/attachments/use-attachment-drafts.ts
2372
- import { useEffect as useEffect4, useRef as useRef4, useState as useState3 } from "react";
2358
+ import { useEffect as useEffect3, useRef as useRef4, useState as useState2 } from "react";
2373
2359
  var MAX_BYTES = 25 * 1024 * 1024;
2374
2360
  var UNSAFE_TYPES = [
2375
2361
  "text/html",
@@ -2410,12 +2396,12 @@ function useAttachmentDrafts({
2410
2396
  accept,
2411
2397
  max
2412
2398
  } = {}) {
2413
- const [drafts, setDrafts] = useState3([]);
2399
+ const [drafts, setDrafts] = useState2([]);
2414
2400
  const draftsRef = useRef4(drafts);
2415
- useEffect4(() => {
2401
+ useEffect3(() => {
2416
2402
  draftsRef.current = drafts;
2417
2403
  }, [drafts]);
2418
- useEffect4(() => () => draftsRef.current.forEach(release), []);
2404
+ useEffect3(() => () => draftsRef.current.forEach(release), []);
2419
2405
  function add(files) {
2420
2406
  const picked = Array.from(files).map((file, index) => {
2421
2407
  const isTooBig = file.size > MAX_BYTES;
@@ -2460,12 +2446,12 @@ function useAttachmentDrafts({
2460
2446
  }
2461
2447
 
2462
2448
  // ../../apps/web/src/components/attachments/use-attachment-target.ts
2463
- import { useRef as useRef5, useState as useState4 } from "react";
2449
+ import { useRef as useRef5, useState as useState3 } from "react";
2464
2450
  function useAttachmentTarget({
2465
2451
  onFiles,
2466
2452
  disabled = false
2467
2453
  }) {
2468
- const [isDropTarget, setIsDropTarget] = useState4(false);
2454
+ const [isDropTarget, setIsDropTarget] = useState3(false);
2469
2455
  const depth = useRef5(0);
2470
2456
  function handleDragEnter(event) {
2471
2457
  if (disabled || !event.dataTransfer.types.includes("Files")) return;
@@ -2561,12 +2547,10 @@ function WidgetButton({
2561
2547
  );
2562
2548
  }
2563
2549
 
2564
- // ../../apps/web/src/components/ui/text-field.ts
2565
- var TEXT_FIELD_SHELL = [
2550
+ // ../../apps/web/src/components/support-widget/widget-field.ts
2551
+ var WIDGET_TEXT_FIELD_SHELL = [
2566
2552
  "rounded-lg border border-input bg-transparent text-sm max-sm:text-base transition-[color,box-shadow] outline-none dark:bg-input/30",
2567
2553
  "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
2554
  "focus-within:border-ring",
2571
2555
  "aria-invalid:border-destructive",
2572
2556
  "disabled:pointer-events-none disabled:opacity-50"
@@ -2591,12 +2575,12 @@ function Textarea({ className, ...props }) {
2591
2575
  }
2592
2576
 
2593
2577
  // ../../apps/web/src/hooks/use-typing-signal.ts
2594
- import { useCallback as useCallback5, useEffect as useEffect5, useRef as useRef6 } from "react";
2578
+ import { useCallback as useCallback5, useEffect as useEffect4, useRef as useRef6 } from "react";
2595
2579
  var REFRESH_MS = 2500;
2596
2580
  var IDLE_MS = 8e3;
2597
2581
  function useTypingSignal(onChange) {
2598
2582
  const onChangeRef = useRef6(onChange);
2599
- useEffect5(() => {
2583
+ useEffect4(() => {
2600
2584
  onChangeRef.current = onChange;
2601
2585
  }, [onChange]);
2602
2586
  const isTypingRef = useRef6(false);
@@ -2630,10 +2614,33 @@ function useTypingSignal(onChange) {
2630
2614
  send(true);
2631
2615
  }
2632
2616
  }, [send, stop]);
2633
- useEffect5(() => () => stop(), [stop]);
2617
+ useEffect4(() => () => stop(), [stop]);
2634
2618
  return { ping, stop, settle };
2635
2619
  }
2636
2620
 
2621
+ // ../../apps/web/src/components/support-widget/use-eased-height.ts
2622
+ import { useLayoutEffect as useLayoutEffect4, useRef as useRef7 } from "react";
2623
+ function useEasedHeight(field) {
2624
+ const box = useRef7(null);
2625
+ useLayoutEffect4(() => {
2626
+ const outer = box.current;
2627
+ const inner = field.current;
2628
+ if (!outer || !inner) return;
2629
+ const follow = () => {
2630
+ outer.style.height = `${inner.offsetHeight}px`;
2631
+ };
2632
+ follow();
2633
+ if (typeof ResizeObserver === "undefined") return;
2634
+ const observer = new ResizeObserver(follow);
2635
+ observer.observe(inner);
2636
+ return () => {
2637
+ observer.disconnect();
2638
+ outer.style.height = "";
2639
+ };
2640
+ }, [field]);
2641
+ return box;
2642
+ }
2643
+
2637
2644
  // ../../apps/web/src/components/brand-art.ts
2638
2645
  var BRAND_INK = "#191919";
2639
2646
  var LOGO = {
@@ -2661,7 +2668,7 @@ var LOGO_MASK_URL = `url("data:image/svg+xml,${encodeURIComponent(
2661
2668
  )}")`;
2662
2669
 
2663
2670
  // ../../apps/web/src/components/brand.tsx
2664
- import { jsx as jsx11 } from "react/jsx-runtime";
2671
+ import { jsx as jsx11, jsxs as jsxs4 } from "react/jsx-runtime";
2665
2672
  var INVERT_IN_DARK = "dark:invert";
2666
2673
  function Wordmark({
2667
2674
  width = 132,
@@ -2726,10 +2733,10 @@ function ShimmerLogo({
2726
2733
  }
2727
2734
 
2728
2735
  // ../../apps/web/src/components/support-widget/widget-badge.tsx
2729
- import { jsx as jsx12, jsxs as jsxs4 } from "react/jsx-runtime";
2736
+ import { jsx as jsx12, jsxs as jsxs5 } from "react/jsx-runtime";
2730
2737
  var MEDIAN_URL = "https://median.sh";
2731
2738
  function PoweredByMedian({ className }) {
2732
- return /* @__PURE__ */ jsxs4(
2739
+ return /* @__PURE__ */ jsxs5(
2733
2740
  "a",
2734
2741
  {
2735
2742
  href: MEDIAN_URL,
@@ -2754,12 +2761,13 @@ function PoweredByMedian({ className }) {
2754
2761
  }
2755
2762
 
2756
2763
  // ../../apps/web/src/components/support-widget/widget-composer.tsx
2757
- import { Fragment as Fragment26, jsx as jsx13, jsxs as jsxs5 } from "react/jsx-runtime";
2764
+ import { Fragment as Fragment26, jsx as jsx13, jsxs as jsxs6 } from "react/jsx-runtime";
2758
2765
  var EMAIL_PATTERN = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
2759
2766
  var SWAP = "transition-[opacity,translate] duration-300 ease-out motion-reduce:transition-none";
2760
2767
  var RISE = "transition-transform duration-450 ease-spring motion-reduce:transition-none";
2761
2768
  var SINK = "transition-transform duration-250 ease-in motion-reduce:transition-none";
2762
2769
  var ROLL = "transition-transform duration-350 ease-out-quint motion-reduce:transition-none";
2770
+ var GROW = "transition-[height] duration-200 ease-out-quint motion-reduce:transition-none";
2763
2771
  var SAVED_MS = 1200;
2764
2772
  function WidgetComposer({
2765
2773
  onSend,
@@ -2773,11 +2781,12 @@ function WidgetComposer({
2773
2781
  compact = false
2774
2782
  }) {
2775
2783
  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);
2784
+ const [draft, setDraft] = useState4("");
2785
+ const [email, setEmail] = useState4("");
2786
+ const [answer, setAnswer] = useState4("none");
2787
+ const fileInputRef = useRef8(null);
2788
+ const draftRef = useRef8(null);
2789
+ const draftBoxRef = useEasedHeight(draftRef);
2781
2790
  const attachments = useAttachmentDrafts();
2782
2791
  const target = useAttachmentTarget({ onFiles: attachments.add });
2783
2792
  const typing = useTypingSignal(onTypingChange);
@@ -2793,12 +2802,12 @@ function WidgetComposer({
2793
2802
  setAnswer("saved");
2794
2803
  onSubmitEmail(email.trim());
2795
2804
  }
2796
- useEffect6(() => {
2805
+ useEffect5(() => {
2797
2806
  if (answer !== "saved") return;
2798
2807
  const timer = setTimeout(() => setAnswer("settled"), SAVED_MS);
2799
2808
  return () => clearTimeout(timer);
2800
2809
  }, [answer]);
2801
- useEffect6(() => {
2810
+ useEffect5(() => {
2802
2811
  if (answer !== "saved" || askForEmail) return;
2803
2812
  draftRef.current?.focus({ preventScroll: true });
2804
2813
  }, [answer, askForEmail]);
@@ -2848,24 +2857,42 @@ function WidgetComposer({
2848
2857
  children: /* @__PURE__ */ jsx13(Plus, { className: isStacked ? "size-5" : void 0 })
2849
2858
  }
2850
2859
  );
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
- }
2860
+ const draftBox = (
2861
+ // The field sizes itself to its words and sits at the foot of the box,
2862
+ // so a line that wraps stays where it was typed and the box's top edge
2863
+ // rises to take the first line in: the composer grows upward, and the
2864
+ // words hold still under the caret. The box follows the field on a
2865
+ // curve and clips whatever it is behind by.
2866
+ /* @__PURE__ */ jsx13(
2867
+ "div",
2868
+ {
2869
+ ref: draftBoxRef,
2870
+ className: cn(
2871
+ "flex flex-col justify-end overflow-clip",
2872
+ isStacked ? "w-full" : "min-w-0 flex-1",
2873
+ draft.length > 0 && GROW
2874
+ ),
2875
+ children: /* @__PURE__ */ jsx13(
2876
+ Textarea,
2877
+ {
2878
+ ref: draftRef,
2879
+ rows: 1,
2880
+ value: draft,
2881
+ onChange: (event) => {
2882
+ setDraft(event.target.value);
2883
+ typing.ping();
2884
+ },
2885
+ onKeyDown: handleKeyDown,
2886
+ placeholder: target.isDropTarget ? "Drop to attach" : placeholder,
2887
+ "aria-label": "Message",
2888
+ className: cn(
2889
+ "max-h-40 shrink-0 max-sm:text-[16px]",
2890
+ isStacked ? "min-h-9 px-2 pt-1.5 pb-1 text-sm" : "min-h-10 px-2 py-2 text-base"
2891
+ )
2892
+ }
2893
+ )
2894
+ }
2895
+ )
2869
2896
  );
2870
2897
  const sendButton = /* @__PURE__ */ jsx13(
2871
2898
  WidgetButton,
@@ -2925,7 +2952,7 @@ function WidgetComposer({
2925
2952
  // anyway. Paste rides on the same element, so it is caught wherever the
2926
2953
  // caret happens to be inside the composer. None of that while the email
2927
2954
  // is asked for: there is no message yet to attach anything to.
2928
- /* @__PURE__ */ jsxs5(
2955
+ /* @__PURE__ */ jsxs6(
2929
2956
  "div",
2930
2957
  {
2931
2958
  className: cn(
@@ -2941,7 +2968,7 @@ function WidgetComposer({
2941
2968
  onRemove: () => attachments.remove(item.id)
2942
2969
  }
2943
2970
  ) }, item.id)) }),
2944
- /* @__PURE__ */ jsxs5("div", { className: "relative", children: [
2971
+ /* @__PURE__ */ jsxs6("div", { className: "relative", children: [
2945
2972
  /* @__PURE__ */ jsx13(
2946
2973
  "div",
2947
2974
  {
@@ -2958,8 +2985,8 @@ function WidgetComposer({
2958
2985
  showTab ? RISE : SINK,
2959
2986
  !showTab && "translate-y-full"
2960
2987
  ),
2961
- children: /* @__PURE__ */ jsxs5("span", { className: "grid overflow-hidden", children: [
2962
- /* @__PURE__ */ jsxs5(
2988
+ children: /* @__PURE__ */ jsxs6("span", { className: "grid overflow-hidden", children: [
2989
+ /* @__PURE__ */ jsxs6(
2963
2990
  "span",
2964
2991
  {
2965
2992
  "aria-hidden": isSaved,
@@ -2974,7 +3001,7 @@ function WidgetComposer({
2974
3001
  ]
2975
3002
  }
2976
3003
  ),
2977
- /* @__PURE__ */ jsxs5(
3004
+ /* @__PURE__ */ jsxs6(
2978
3005
  "span",
2979
3006
  {
2980
3007
  "aria-hidden": !isSaved,
@@ -2994,11 +3021,11 @@ function WidgetComposer({
2994
3021
  )
2995
3022
  }
2996
3023
  ),
2997
- /* @__PURE__ */ jsxs5(
3024
+ /* @__PURE__ */ jsxs6(
2998
3025
  "div",
2999
3026
  {
3000
3027
  className: cn(
3001
- TEXT_FIELD_SHELL,
3028
+ WIDGET_TEXT_FIELD_SHELL,
3002
3029
  "relative z-10 grid bg-card p-2 transition-colors dark:bg-card",
3003
3030
  // Half the pill's one line height, so it is a full pill at one
3004
3031
  // line and keeps those corners as it grows. The box keeps a
@@ -3020,17 +3047,17 @@ function WidgetComposer({
3020
3047
  SWAP,
3021
3048
  isAsking && "-translate-y-1 opacity-0 pointer-events-none"
3022
3049
  ),
3023
- children: isStacked ? /* @__PURE__ */ jsxs5(Fragment26, { children: [
3050
+ children: isStacked ? /* @__PURE__ */ jsxs6(Fragment26, { children: [
3024
3051
  draftBox,
3025
- /* @__PURE__ */ jsxs5("div", { className: "flex items-center gap-1.5", children: [
3052
+ /* @__PURE__ */ jsxs6("div", { className: "flex items-center gap-1.5", children: [
3026
3053
  fileInput,
3027
3054
  addButton,
3028
- /* @__PURE__ */ jsxs5("div", { className: "ml-auto flex items-center gap-2", children: [
3055
+ /* @__PURE__ */ jsxs6("div", { className: "ml-auto flex items-center gap-2", children: [
3029
3056
  badge,
3030
3057
  sendButton
3031
3058
  ] })
3032
3059
  ] })
3033
- ] }) : /* @__PURE__ */ jsxs5(Fragment26, { children: [
3060
+ ] }) : /* @__PURE__ */ jsxs6(Fragment26, { children: [
3034
3061
  fileInput,
3035
3062
  addButton,
3036
3063
  draftBox,
@@ -3049,13 +3076,13 @@ function WidgetComposer({
3049
3076
  SWAP,
3050
3077
  !isAsking && "pointer-events-none translate-y-1 opacity-0"
3051
3078
  ),
3052
- children: isStacked ? /* @__PURE__ */ jsxs5(Fragment26, { children: [
3079
+ children: isStacked ? /* @__PURE__ */ jsxs6(Fragment26, { children: [
3053
3080
  emailBox,
3054
- /* @__PURE__ */ jsxs5("div", { className: "flex items-center justify-end gap-2", children: [
3081
+ /* @__PURE__ */ jsxs6("div", { className: "flex items-center justify-end gap-2", children: [
3055
3082
  badge,
3056
3083
  emailButton
3057
3084
  ] })
3058
- ] }) : /* @__PURE__ */ jsxs5(Fragment26, { children: [
3085
+ ] }) : /* @__PURE__ */ jsxs6(Fragment26, { children: [
3059
3086
  emailBox,
3060
3087
  emailButton
3061
3088
  ] })
@@ -3072,13 +3099,17 @@ function WidgetComposer({
3072
3099
  );
3073
3100
  }
3074
3101
 
3102
+ // ../../apps/web/src/components/support-widget/widget-corner.ts
3103
+ var WIDGET_RADIUS = "0.875rem";
3104
+ var WIDGET_CORNER = "rounded-[0.875rem]";
3105
+
3075
3106
  // ../../apps/web/src/components/support-widget/widget-destination.tsx
3076
- import { jsx as jsx14, jsxs as jsxs6 } from "react/jsx-runtime";
3107
+ import { jsx as jsx14, jsxs as jsxs7 } from "react/jsx-runtime";
3077
3108
  function WidgetDestination({
3078
3109
  destination,
3079
3110
  onFollow
3080
3111
  }) {
3081
- return /* @__PURE__ */ jsxs6(
3112
+ return /* @__PURE__ */ jsxs7(
3082
3113
  WidgetButton,
3083
3114
  {
3084
3115
  type: "button",
@@ -3100,10 +3131,24 @@ function WidgetDestination({
3100
3131
  }
3101
3132
 
3102
3133
  // ../../apps/web/src/components/avatar.tsx
3103
- import { useState as useState6 } from "react";
3134
+ import { useState as useState5 } from "react";
3104
3135
 
3105
3136
  // ../../apps/web/src/components/initial-badge.tsx
3106
3137
  import { jsx as jsx15 } from "react/jsx-runtime";
3138
+ var TONES = [
3139
+ "bg-[var(--avatar-blue,#e0edfa)] text-[var(--avatar-blue-foreground,#37628c)]",
3140
+ "bg-[var(--avatar-violet,#eae4f7)] text-[var(--avatar-violet-foreground,#705894)]",
3141
+ "bg-[var(--avatar-amber,#f8ead6)] text-[var(--avatar-amber-foreground,#8b6434)]",
3142
+ "bg-[var(--avatar-teal,#dbefea)] text-[var(--avatar-teal-foreground,#3c766d)]",
3143
+ "bg-[var(--avatar-rose,#f5e2e8)] text-[var(--avatar-rose-foreground,#92586c)]"
3144
+ ];
3145
+ function toneFor(value) {
3146
+ let hash = 0;
3147
+ for (const character of value.trim().toLowerCase()) {
3148
+ hash = hash * 31 + character.charCodeAt(0) >>> 0;
3149
+ }
3150
+ return TONES[hash % TONES.length];
3151
+ }
3107
3152
  function InitialBadge({
3108
3153
  value,
3109
3154
  className
@@ -3114,7 +3159,8 @@ function InitialBadge({
3114
3159
  "aria-hidden": true,
3115
3160
  className: cn(
3116
3161
  // 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",
3162
+ "surface-control grid size-4.5 shrink-0 place-items-center rounded border border-foreground/5 text-[10px] font-medium",
3163
+ toneFor(value),
3118
3164
  className
3119
3165
  ),
3120
3166
  children: value.trim().charAt(0).toUpperCase() || "?"
@@ -3131,7 +3177,7 @@ function Avatar({
3131
3177
  unoptimized = false,
3132
3178
  className
3133
3179
  }) {
3134
- const [failedSrc, setFailedSrc] = useState6(null);
3180
+ const [failedSrc, setFailedSrc] = useState5(null);
3135
3181
  const hasFailed = failedSrc !== null && failedSrc === src;
3136
3182
  const shape2 = "size-4.5 shrink-0 rounded-full";
3137
3183
  if (!src || hasFailed) {
@@ -3139,7 +3185,7 @@ function Avatar({
3139
3185
  InitialBadge,
3140
3186
  {
3141
3187
  value: name,
3142
- className: cn(shape2, "border-none bg-muted text-[10px]", className)
3188
+ className: cn(shape2, "border-none text-[10px]", className)
3143
3189
  }
3144
3190
  );
3145
3191
  }
@@ -3153,7 +3199,7 @@ function Avatar({
3153
3199
  unoptimized,
3154
3200
  onError: () => setFailedSrc(src),
3155
3201
  draggable: false,
3156
- className: cn(shape2, "bg-muted object-cover", className)
3202
+ className: cn(shape2, "bg-muted object-cover ring-1 ring-foreground/5", className)
3157
3203
  }
3158
3204
  );
3159
3205
  }
@@ -3192,7 +3238,7 @@ function WidgetAvatar({
3192
3238
  {
3193
3239
  src: agent.avatarUrl,
3194
3240
  name: agent.name,
3195
- className: "size-full rounded-[inherit]"
3241
+ className: "size-full rounded-[inherit] bg-muted bg-none font-semibold text-muted-foreground shadow-none ring-0"
3196
3242
  }
3197
3243
  )
3198
3244
  }
@@ -3215,7 +3261,7 @@ function WidgetAvatarStack({
3215
3261
  }
3216
3262
 
3217
3263
  // ../../apps/web/src/components/support-widget/widget-empty.tsx
3218
- import { jsx as jsx18, jsxs as jsxs7 } from "react/jsx-runtime";
3264
+ import { jsx as jsx18, jsxs as jsxs8 } from "react/jsx-runtime";
3219
3265
  var STARTERS = [
3220
3266
  {
3221
3267
  label: "Ask a question",
@@ -3242,7 +3288,7 @@ function WidgetEmpty({
3242
3288
  compact = false
3243
3289
  }) {
3244
3290
  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: [
3291
+ 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
3292
  agent && /* @__PURE__ */ jsx18(
3247
3293
  WidgetAvatar,
3248
3294
  {
@@ -3253,7 +3299,7 @@ function WidgetEmpty({
3253
3299
  )
3254
3300
  }
3255
3301
  ),
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: [
3302
+ /* @__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
3303
  agent && /* @__PURE__ */ jsx18(
3258
3304
  "p",
3259
3305
  {
@@ -3275,7 +3321,7 @@ function WidgetEmpty({
3275
3321
  }
3276
3322
  )
3277
3323
  ] }),
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(
3324
+ 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
3325
  WidgetButton,
3280
3326
  {
3281
3327
  type: "button",
@@ -3316,7 +3362,7 @@ function WidgetEmpty({
3316
3362
  }
3317
3363
 
3318
3364
  // ../../apps/web/src/components/support-widget/widget-header.tsx
3319
- import { jsx as jsx19, jsxs as jsxs8 } from "react/jsx-runtime";
3365
+ import { jsx as jsx19, jsxs as jsxs9 } from "react/jsx-runtime";
3320
3366
  function WidgetHeader({
3321
3367
  presence,
3322
3368
  onClose,
@@ -3327,7 +3373,7 @@ function WidgetHeader({
3327
3373
  compact = false,
3328
3374
  className
3329
3375
  }) {
3330
- return /* @__PURE__ */ jsxs8(
3376
+ return /* @__PURE__ */ jsxs9(
3331
3377
  "header",
3332
3378
  {
3333
3379
  ...grabHandlers,
@@ -3344,7 +3390,7 @@ function WidgetHeader({
3344
3390
  ]
3345
3391
  ),
3346
3392
  children: [
3347
- /* @__PURE__ */ jsxs8("div", { className: "flex min-w-0 flex-1 flex-col pt-0.5", children: [
3393
+ /* @__PURE__ */ jsxs9("div", { className: "flex min-w-0 flex-1 flex-col pt-0.5", children: [
3348
3394
  /* @__PURE__ */ jsx19(
3349
3395
  "h2",
3350
3396
  {
@@ -3366,7 +3412,7 @@ function WidgetHeader({
3366
3412
  }
3367
3413
  )
3368
3414
  ] }),
3369
- /* @__PURE__ */ jsxs8("div", { className: "flex shrink-0 items-center gap-3", children: [
3415
+ /* @__PURE__ */ jsxs9("div", { className: "flex shrink-0 items-center gap-3", children: [
3370
3416
  /* @__PURE__ */ jsx19(
3371
3417
  WidgetAvatarStack,
3372
3418
  {
@@ -3374,7 +3420,7 @@ function WidgetHeader({
3374
3420
  avatarClassName: compact ? "size-8" : void 0
3375
3421
  }
3376
3422
  ),
3377
- /* @__PURE__ */ jsxs8("div", { className: "flex items-center gap-0.5", children: [
3423
+ /* @__PURE__ */ jsxs9("div", { className: "flex items-center gap-0.5", children: [
3378
3424
  onToggleExpanded && /* @__PURE__ */ jsx19(
3379
3425
  WidgetButton,
3380
3426
  {
@@ -3412,16 +3458,16 @@ function WidgetLoading() {
3412
3458
  }
3413
3459
 
3414
3460
  // ../../apps/web/src/components/support-widget/widget-locked.tsx
3415
- import { jsx as jsx21, jsxs as jsxs9 } from "react/jsx-runtime";
3461
+ import { jsx as jsx21, jsxs as jsxs10 } from "react/jsx-runtime";
3416
3462
  function WidgetLocked({
3417
3463
  orgName,
3418
3464
  onSignIn,
3419
3465
  showBadge = true
3420
3466
  }) {
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: [
3467
+ return /* @__PURE__ */ jsxs10("div", { className: "flex min-h-0 flex-1 flex-col", children: [
3468
+ /* @__PURE__ */ jsxs10("div", { className: "flex min-h-0 flex-1 flex-col items-center justify-center gap-5 px-8 text-center", children: [
3423
3469
  /* @__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: [
3470
+ /* @__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
3471
  /* @__PURE__ */ jsx21("p", { className: "text-xl font-semibold tracking-tight", children: "Sign in to chat" }),
3426
3472
  /* @__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
3473
  ] }),
@@ -3440,7 +3486,7 @@ function WidgetLocked({
3440
3486
  }
3441
3487
 
3442
3488
  // ../../apps/web/src/components/support-widget/widget-message.tsx
3443
- import { useState as useState9 } from "react";
3489
+ import { useState as useState8 } from "react";
3444
3490
 
3445
3491
  // ../../apps/web/src/components/ui/badge.tsx
3446
3492
  import { jsx as jsx22 } from "react/jsx-runtime";
@@ -3449,10 +3495,10 @@ var badgeVariants = cva(
3449
3495
  {
3450
3496
  variants: {
3451
3497
  variant: {
3452
- default: "bg-secondary text-secondary-foreground",
3453
- muted: "bg-muted text-muted-foreground",
3498
+ default: "bg-secondary text-secondary-foreground inset-ring-1 inset-ring-foreground/5",
3499
+ muted: "bg-muted/70 text-muted-foreground inset-ring-1 inset-ring-foreground/5",
3454
3500
  outline: "border border-border text-muted-foreground",
3455
- solid: "bg-primary text-primary-foreground"
3501
+ solid: "surface-primary bg-primary text-primary-foreground"
3456
3502
  }
3457
3503
  },
3458
3504
  defaultVariants: {
@@ -3479,7 +3525,7 @@ function Badge({
3479
3525
  }
3480
3526
 
3481
3527
  // ../../apps/web/src/components/ui/morph-bubble.tsx
3482
- import { useLayoutEffect as useLayoutEffect4, useRef as useRef8 } from "react";
3528
+ import { useLayoutEffect as useLayoutEffect5, useRef as useRef9 } from "react";
3483
3529
  import { jsx as jsx23 } from "react/jsx-runtime";
3484
3530
  var MORPH_MS = 350;
3485
3531
  var MORPH_EASING = "ease-in-out";
@@ -3507,11 +3553,11 @@ function MorphBubble({
3507
3553
  contentClassName,
3508
3554
  children
3509
3555
  }) {
3510
- const box = useRef8(null);
3511
- const content = useRef8(null);
3512
- const previous = useRef8(null);
3513
- const running = useRef8([]);
3514
- useLayoutEffect4(() => {
3556
+ const box = useRef9(null);
3557
+ const content = useRef9(null);
3558
+ const previous = useRef9(null);
3559
+ const running = useRef9([]);
3560
+ useLayoutEffect5(() => {
3515
3561
  const outer = box.current;
3516
3562
  const inner = content.current;
3517
3563
  if (!outer || !inner) return;
@@ -3564,8 +3610,8 @@ function MorphBubble({
3564
3610
  }
3565
3611
 
3566
3612
  // ../../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";
3613
+ import { useState as useState6 } from "react";
3614
+ import { Fragment as Fragment27, jsx as jsx24, jsxs as jsxs11 } from "react/jsx-runtime";
3569
3615
  var SURFACE = "rounded-xl border border-foreground/10 bg-card/70";
3570
3616
  function OpenAttachment({
3571
3617
  attachment,
@@ -3593,9 +3639,9 @@ function MediaTile({
3593
3639
  attachment,
3594
3640
  className
3595
3641
  }) {
3596
- const [hasFailed, setHasFailed] = useState7(false);
3642
+ const [hasFailed, setHasFailed] = useState6(false);
3597
3643
  if (!attachment.previewUrl || hasFailed) {
3598
- return /* @__PURE__ */ jsxs10(
3644
+ return /* @__PURE__ */ jsxs11(
3599
3645
  "div",
3600
3646
  {
3601
3647
  className: cn(
@@ -3610,7 +3656,7 @@ function MediaTile({
3610
3656
  }
3611
3657
  );
3612
3658
  }
3613
- return /* @__PURE__ */ jsxs10("figure", { className: cn(SURFACE, "relative overflow-hidden", className), children: [
3659
+ return /* @__PURE__ */ jsxs11("figure", { className: cn(SURFACE, "relative overflow-hidden", className), children: [
3614
3660
  /* @__PURE__ */ jsx24(
3615
3661
  "img",
3616
3662
  {
@@ -3634,9 +3680,9 @@ function MediaTile({
3634
3680
  ] });
3635
3681
  }
3636
3682
  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: [
3683
+ return /* @__PURE__ */ jsx24(OpenAttachment, { attachment, children: /* @__PURE__ */ jsxs11("div", { className: cn(SURFACE, "flex items-center gap-2.5 px-2.5 py-2"), children: [
3684
+ /* @__PURE__ */ jsx24(File2, { className: "size-5 shrink-0 text-muted-foreground" }),
3685
+ /* @__PURE__ */ jsxs11("div", { className: "flex min-w-0 flex-1 flex-col text-left", children: [
3640
3686
  /* @__PURE__ */ jsx24("p", { className: "truncate text-sm font-medium", children: attachment.name }),
3641
3687
  /* @__PURE__ */ jsx24("p", { className: "truncate text-sm text-muted-foreground", children: attachment.size })
3642
3688
  ] })
@@ -3647,7 +3693,7 @@ function WidgetAttachments({
3647
3693
  }) {
3648
3694
  const media = attachments.filter((one) => one.kind !== "file");
3649
3695
  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: [
3696
+ return /* @__PURE__ */ jsxs11("div", { className: "flex w-64 max-w-full flex-col gap-2", children: [
3651
3697
  media.length > 0 && /* @__PURE__ */ jsx24("div", { className: cn("grid gap-2", media.length > 1 && "grid-cols-2"), children: media.map((attachment) => /* @__PURE__ */ jsx24(
3652
3698
  OpenAttachment,
3653
3699
  {
@@ -3685,7 +3731,7 @@ function WidgetBubble({
3685
3731
  import { Suspense } from "react";
3686
3732
 
3687
3733
  // ../../apps/web/src/components/ui/line-reveal.tsx
3688
- import { useLayoutEffect as useLayoutEffect5, useRef as useRef9, useState as useState8 } from "react";
3734
+ import { useLayoutEffect as useLayoutEffect6, useRef as useRef10, useState as useState7 } from "react";
3689
3735
 
3690
3736
  // ../../apps/web/src/hooks/use-media-query.ts
3691
3737
  import { useSyncExternalStore as useSyncExternalStore3 } from "react";
@@ -3712,7 +3758,7 @@ function durationMs(token, fallback, from = document.documentElement) {
3712
3758
  }
3713
3759
 
3714
3760
  // ../../apps/web/src/components/ui/line-reveal.tsx
3715
- import { jsx as jsx26, jsxs as jsxs11 } from "react/jsx-runtime";
3761
+ import { jsx as jsx26, jsxs as jsxs12 } from "react/jsx-runtime";
3716
3762
  var LAST_BEAT = 11;
3717
3763
  function lineSlices(root) {
3718
3764
  const host = root.getBoundingClientRect();
@@ -3748,7 +3794,7 @@ function lineSlices(root) {
3748
3794
  return [{ top: 0, height: host.height }];
3749
3795
  }
3750
3796
  const unique = [...new Set(tops.map((top) => Math.round(top)))].sort(
3751
- (a28, b2) => a28 - b2
3797
+ (a29, b2) => a29 - b2
3752
3798
  );
3753
3799
  return unique.map((top, index) => {
3754
3800
  const next = unique[index + 1] ?? Math.ceil(host.height);
@@ -3798,10 +3844,10 @@ function LineReveal({
3798
3844
  children,
3799
3845
  className
3800
3846
  }) {
3801
- const sourceRef = useRef9(null);
3802
- const stageRef = useRef9(null);
3847
+ const sourceRef = useRef10(null);
3848
+ const stageRef = useRef10(null);
3803
3849
  const reduce = useMediaQuery("(prefers-reduced-motion: reduce)");
3804
- const [play2, setPlay] = useState8({
3850
+ const [play2, setPlay] = useState7({
3805
3851
  key: contentKey,
3806
3852
  delay,
3807
3853
  isPlaying: !reduce
@@ -3810,19 +3856,20 @@ function LineReveal({
3810
3856
  setPlay({ key: contentKey, delay, isPlaying: !reduce });
3811
3857
  }
3812
3858
  const { key: playKey, delay: hold, isPlaying } = play2;
3813
- useLayoutEffect5(() => {
3859
+ useLayoutEffect6(() => {
3814
3860
  if (reduce || !isPlaying) return;
3815
3861
  const source = sourceRef.current;
3816
3862
  const stage = stageRef.current;
3817
3863
  if (source === null || stage === null) return;
3864
+ const wait = typeof hold === "number" ? hold : durationMs(hold, 0, stage);
3818
3865
  let painted = false;
3819
3866
  let timer = 0;
3820
3867
  const end = () => setPlay((now) => ({ ...now, isPlaying: false }));
3821
3868
  const paint = () => {
3822
3869
  if (painted) return;
3823
- if (!paintSlices(source, stage, hold)) return;
3870
+ if (!paintSlices(source, stage, wait)) return;
3824
3871
  painted = true;
3825
- timer = window.setTimeout(end, playMs(stage, hold));
3872
+ timer = window.setTimeout(end, playMs(stage, wait));
3826
3873
  observer.disconnect();
3827
3874
  };
3828
3875
  const observer = new ResizeObserver(paint);
@@ -3839,7 +3886,7 @@ function LineReveal({
3839
3886
  };
3840
3887
  }, [playKey, hold, isPlaying, reduce]);
3841
3888
  const showSlices = isPlaying && !reduce;
3842
- return /* @__PURE__ */ jsxs11("div", { className: cn("relative", className), children: [
3889
+ return /* @__PURE__ */ jsxs12("div", { className: cn("relative", className), children: [
3843
3890
  /* @__PURE__ */ jsx26("div", { className: showSlices ? "pointer-events-none opacity-0" : void 0, children: /* @__PURE__ */ jsx26("div", { ref: sourceRef, children }) }),
3844
3891
  showSlices && /* @__PURE__ */ jsx26(
3845
3892
  "div",
@@ -3868,10 +3915,10 @@ function WidgetMessageBody({
3868
3915
  }
3869
3916
 
3870
3917
  // ../../apps/web/src/components/support-widget/widget-typing.tsx
3871
- import { jsx as jsx28, jsxs as jsxs12 } from "react/jsx-runtime";
3918
+ import { jsx as jsx28, jsxs as jsxs13 } from "react/jsx-runtime";
3872
3919
  var DOTS = [0, 160, 320];
3873
3920
  function WidgetTyping({ label }) {
3874
- return /* @__PURE__ */ jsxs12("span", { className: "flex items-center gap-1.5 py-2", children: [
3921
+ return /* @__PURE__ */ jsxs13("span", { className: "flex items-center gap-1.5 py-2", children: [
3875
3922
  /* @__PURE__ */ jsx28("span", { className: "sr-only", children: label }),
3876
3923
  DOTS.map((delay) => /* @__PURE__ */ jsx28(
3877
3924
  "span",
@@ -3886,16 +3933,17 @@ function WidgetTyping({ label }) {
3886
3933
  }
3887
3934
 
3888
3935
  // ../../apps/web/src/components/support-widget/widget-message.tsx
3889
- import { Fragment as Fragment28, jsx as jsx29, jsxs as jsxs13 } from "react/jsx-runtime";
3936
+ import { Fragment as Fragment28, jsx as jsx29, jsxs as jsxs14 } from "react/jsx-runtime";
3890
3937
  var BUBBLE = "max-w-[min(80%,36rem)] rounded-2xl px-4 py-2.5 text-base";
3891
3938
  var COMPACT_BUBBLE = "max-w-[min(80%,36rem)] rounded-2xl px-3.5 py-2 text-sm";
3892
3939
  var VISITOR_BUBBLE = "bg-[color-mix(in_oklch,var(--muted),var(--foreground)_12%)]";
3893
3940
  var ARRIVES = "animate-in fade-in-0 slide-in-from-bottom-1 duration-300 ease-out-quint motion-reduce:animate-none";
3941
+ 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
3942
  var LADDER_DELAY_MS = Math.round(MORPH_MS * 0.7);
3895
3943
  function WidgetDivider({ message }) {
3896
3944
  const label = message.startsConversation ? "New conversation" : message.dayLabel;
3897
3945
  if (!label) return null;
3898
- return /* @__PURE__ */ jsxs13("div", { className: "flex items-center gap-3 py-1", children: [
3946
+ return /* @__PURE__ */ jsxs14("div", { className: "flex items-center gap-3 py-1", children: [
3899
3947
  /* @__PURE__ */ jsx29("span", { className: "h-px flex-1 bg-border" }),
3900
3948
  /* @__PURE__ */ jsx29("span", { className: "text-sm text-muted-foreground", children: label }),
3901
3949
  /* @__PURE__ */ jsx29("span", { className: "h-px flex-1 bg-border" })
@@ -3908,7 +3956,7 @@ function WidgetMessage({
3908
3956
  compact = false,
3909
3957
  isLive = false
3910
3958
  }) {
3911
- return message.sender === "visitor" ? /* @__PURE__ */ jsx29(VisitorMessage, { message, compact }) : /* @__PURE__ */ jsx29(
3959
+ return message.sender === "visitor" ? /* @__PURE__ */ jsx29(VisitorMessage, { message, compact, isLive }) : /* @__PURE__ */ jsx29(
3912
3960
  AgentMessage,
3913
3961
  {
3914
3962
  message,
@@ -3932,7 +3980,7 @@ function BubbleContent({
3932
3980
  }
3933
3981
  );
3934
3982
  }
3935
- return /* @__PURE__ */ jsxs13(Fragment28, { children: [
3983
+ return /* @__PURE__ */ jsxs14(Fragment28, { children: [
3936
3984
  hasAttachments && /* @__PURE__ */ jsx29(WidgetAttachments, { attachments: message.attachments ?? [] }),
3937
3985
  message.body && /* @__PURE__ */ jsx29(
3938
3986
  WidgetMessageBody,
@@ -3946,9 +3994,10 @@ function BubbleContent({
3946
3994
  }
3947
3995
  function VisitorMessage({
3948
3996
  message,
3949
- compact
3997
+ compact,
3998
+ isLive
3950
3999
  }) {
3951
- return /* @__PURE__ */ jsx29("div", { className: cn("flex justify-end", ARRIVES), children: /* @__PURE__ */ jsx29(
4000
+ return /* @__PURE__ */ jsx29("div", { className: cn("flex justify-end", isLive ? SENT : ARRIVES), children: /* @__PURE__ */ jsx29(
3952
4001
  WidgetBubble,
3953
4002
  {
3954
4003
  morphKey: "settled",
@@ -3965,7 +4014,7 @@ function AgentMessage({
3965
4014
  isLive
3966
4015
  }) {
3967
4016
  const { agent } = message;
3968
- const [sawDots, setSawDots] = useState9(message.pending === true);
4017
+ const [sawDots, setSawDots] = useState8(message.pending === true);
3969
4018
  if (message.pending && !sawDots) setSawDots(true);
3970
4019
  const arrival = isLive && !message.pending && message.body.length > 0 ? { key: message.id, delay: sawDots ? LADDER_DELAY_MS : 0 } : null;
3971
4020
  const destination = message.navigateTo === void 0 ? null : {
@@ -3979,8 +4028,8 @@ function AgentMessage({
3979
4028
  // below, which spread four small things across three rows and pushed every
3980
4029
  // bubble in off the edge. All of it is one line now, and the bubble gets
3981
4030
  // the full width back.
3982
- /* @__PURE__ */ jsxs13("div", { className: cn("flex flex-col items-start gap-1", ARRIVES), children: [
3983
- group.isFirst && /* @__PURE__ */ jsxs13(
4031
+ /* @__PURE__ */ jsxs14("div", { className: cn("flex flex-col items-start gap-1", ARRIVES), children: [
4032
+ group.isFirst && /* @__PURE__ */ jsxs14(
3984
4033
  "div",
3985
4034
  {
3986
4035
  className: cn(
@@ -3988,7 +4037,7 @@ function AgentMessage({
3988
4037
  compact ? "text-xs" : "text-sm"
3989
4038
  ),
3990
4039
  children: [
3991
- agent && /* @__PURE__ */ jsxs13(Fragment28, { children: [
4040
+ agent && /* @__PURE__ */ jsxs14(Fragment28, { children: [
3992
4041
  /* @__PURE__ */ jsx29(
3993
4042
  WidgetAvatar,
3994
4043
  {
@@ -3997,7 +4046,7 @@ function AgentMessage({
3997
4046
  }
3998
4047
  ),
3999
4048
  /* @__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" })
4049
+ agent.kind === "ai" && /* @__PURE__ */ jsx29(Badge, { variant: "muted", className: "shrink-0 rounded-md bg-muted px-1 inset-ring-0", children: "AI" })
4001
4050
  ] }),
4002
4051
  !message.pending && /* @__PURE__ */ jsx29("span", { className: "shrink-0 text-muted-foreground tabular-nums", children: message.time })
4003
4052
  ]
@@ -4035,21 +4084,21 @@ var TYPING_TTL_MS = 12e3;
4035
4084
  var SNOOZE_MAX_MS = 365 * 24 * 60 * 6e4;
4036
4085
 
4037
4086
  // ../../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";
4087
+ import { useEffect as useEffect6, useRef as useRef11, useState as useState9 } from "react";
4088
+ import { jsx as jsx30, jsxs as jsxs15 } from "react/jsx-runtime";
4040
4089
  var STARS = Array.from({ length: RATING_MAX_STARS }, (_, index) => index + 1);
4041
4090
  var THANKS_MS = 1400;
4042
4091
  var STAR_STAGGER_MS = 40;
4043
4092
  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(() => {
4093
+ const [isOpen, setIsOpen] = useState9(false);
4094
+ const [picked, setPicked] = useState9(rating.stars);
4095
+ const [hovered, setHovered] = useState9(null);
4096
+ const [feedback, setFeedback] = useState9("");
4097
+ const [isSending, setIsSending] = useState9(false);
4098
+ const [error, setError] = useState9(null);
4099
+ const [isDone, setIsDone] = useState9(false);
4100
+ const timers = useRef11([]);
4101
+ useEffect6(() => {
4053
4102
  const frame = requestAnimationFrame(() => setIsOpen(true));
4054
4103
  const pending = timers.current;
4055
4104
  return () => {
@@ -4090,9 +4139,9 @@ function WidgetRating({ rating }) {
4090
4139
  setIsDone(true);
4091
4140
  later(THANKS_MS, leave);
4092
4141
  }
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: [
4142
+ 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: [
4143
+ /* @__PURE__ */ jsx30(Collapse, { isOpen: !isDone, children: /* @__PURE__ */ jsxs15("div", { className: "py-3", children: [
4144
+ /* @__PURE__ */ jsxs15("div", { className: "flex items-start justify-between gap-2", children: [
4096
4145
  /* @__PURE__ */ jsx30("p", { className: "text-base font-medium", children: picked === null ? "How did we do?" : "Anything we could improve?" }),
4097
4146
  /* @__PURE__ */ jsx30(
4098
4147
  WidgetButton,
@@ -4149,7 +4198,7 @@ function WidgetRating({ rating }) {
4149
4198
  ))
4150
4199
  }
4151
4200
  ),
4152
- /* @__PURE__ */ jsx30(Collapse, { isOpen: picked !== null, children: /* @__PURE__ */ jsxs14("div", { className: "flex flex-col gap-2 pt-2", children: [
4201
+ /* @__PURE__ */ jsx30(Collapse, { isOpen: picked !== null, children: /* @__PURE__ */ jsxs15("div", { className: "flex flex-col gap-2 pt-2", children: [
4153
4202
  /* @__PURE__ */ jsx30(
4154
4203
  Textarea,
4155
4204
  {
@@ -4161,7 +4210,7 @@ function WidgetRating({ rating }) {
4161
4210
  maxLength: RATING_FEEDBACK_MAX_LENGTH,
4162
4211
  disabled: isSending,
4163
4212
  className: cn(
4164
- TEXT_FIELD_SHELL,
4213
+ WIDGET_TEXT_FIELD_SHELL,
4165
4214
  "max-h-28 min-h-20 bg-card px-3 py-2.5 text-base"
4166
4215
  )
4167
4216
  }
@@ -4184,16 +4233,16 @@ function WidgetRating({ rating }) {
4184
4233
  }
4185
4234
 
4186
4235
  // ../../apps/web/src/components/support-widget/widget-status.tsx
4187
- import { jsx as jsx31, jsxs as jsxs15 } from "react/jsx-runtime";
4236
+ import { jsx as jsx31, jsxs as jsxs16 } from "react/jsx-runtime";
4188
4237
  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: [
4238
+ 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
4239
  /* @__PURE__ */ jsx31(UserCircle, { "aria-hidden": true, className: "size-4" }),
4191
4240
  "Waiting for the support team"
4192
4241
  ] });
4193
4242
  }
4194
4243
 
4195
4244
  // ../../apps/web/src/components/support-widget/support-widget.tsx
4196
- import { jsx as jsx32, jsxs as jsxs16 } from "react/jsx-runtime";
4245
+ import { jsx as jsx32, jsxs as jsxs17 } from "react/jsx-runtime";
4197
4246
  function joinsPreviousTurn(message, previous) {
4198
4247
  return !message.dayLabel && !message.startsConversation && message.sender === previous.sender && message.agent?.id === previous.agent?.id && withinRun(previous.sentAt, message.sentAt);
4199
4248
  }
@@ -4223,7 +4272,7 @@ function SupportWidget({
4223
4272
  className
4224
4273
  }) {
4225
4274
  const stack = useStackShift(messages.length);
4226
- return /* @__PURE__ */ jsxs16(
4275
+ return /* @__PURE__ */ jsxs17(
4227
4276
  "section",
4228
4277
  {
4229
4278
  "aria-label": `${presence.name} support`,
@@ -4231,7 +4280,7 @@ function SupportWidget({
4231
4280
  // The widget keeps its own radius scale. The app's token can climb as
4232
4281
  // the chrome gets rounder without the widget changing shape under the
4233
4282
  // hosts embedding it.
4234
- "flex h-full w-full flex-col overflow-hidden rounded-xl border bg-card text-card-foreground [--radius:0.625rem]",
4283
+ `flex h-full w-full flex-col overflow-hidden ${WIDGET_CORNER} border bg-card text-card-foreground [--radius:0.625rem]`,
4235
4284
  // And its own pace for a reply arriving a line at a time: a step
4236
4285
  // quicker than the palette's ladder, since a support answer is a
4237
4286
  // few short lines in a bubble rather than an essay in a column.
@@ -4305,14 +4354,14 @@ function Conversation({
4305
4354
  stackRef
4306
4355
  }) {
4307
4356
  const groups = groupMessages(messages, joinsPreviousTurn);
4308
- const [openedWith] = useState11(
4357
+ const [openedWith] = useState10(
4309
4358
  () => new Set(messages.map((message) => message.id))
4310
4359
  );
4311
- const [heldOffer, setHeldOffer] = useState11(offer);
4360
+ const [heldOffer, setHeldOffer] = useState10(offer);
4312
4361
  if (offer !== null && offer.key !== heldOffer?.key) setHeldOffer(offer);
4313
- const [heldNotice, setHeldNotice] = useState11(notice ?? null);
4362
+ const [heldNotice, setHeldNotice] = useState10(notice ?? null);
4314
4363
  if (notice && notice !== heldNotice) setHeldNotice(notice);
4315
- return /* @__PURE__ */ jsxs16(
4364
+ return /* @__PURE__ */ jsxs17(
4316
4365
  "div",
4317
4366
  {
4318
4367
  className: cn(
@@ -4340,7 +4389,7 @@ function Conversation({
4340
4389
  so a scroller left alone slides the newest message under the
4341
4390
  composer and has to catch up afterwards. Following it is what the
4342
4391
  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: [
4392
+ /* @__PURE__ */ jsx32(MessageScrollerProvider, { children: /* @__PURE__ */ jsxs17(MessageScroller, { className: "h-auto min-h-0 flex-1", children: [
4344
4393
  /* @__PURE__ */ jsx32(
4345
4394
  MessageScrollerViewport,
4346
4395
  {
@@ -4354,7 +4403,7 @@ function Conversation({
4354
4403
  {
4355
4404
  ref: stackRef,
4356
4405
  className: "justify-end gap-0",
4357
- children: messages.map((message, index) => /* @__PURE__ */ jsxs16(
4406
+ children: messages.map((message, index) => /* @__PURE__ */ jsxs17(
4358
4407
  MessageScrollerItem,
4359
4408
  {
4360
4409
  messageId: message.id,
@@ -4417,10 +4466,10 @@ function Conversation({
4417
4466
  // ../../apps/web/src/components/support-widget/use-launcher-drag.ts
4418
4467
  import {
4419
4468
  useCallback as useCallback6,
4420
- useEffect as useEffect8,
4421
- useLayoutEffect as useLayoutEffect6,
4422
- useRef as useRef11,
4423
- useState as useState12,
4469
+ useEffect as useEffect7,
4470
+ useLayoutEffect as useLayoutEffect7,
4471
+ useRef as useRef12,
4472
+ useState as useState11,
4424
4473
  useSyncExternalStore as useSyncExternalStore4
4425
4474
  } from "react";
4426
4475
  import { flushSync } from "react-dom";
@@ -4439,9 +4488,9 @@ function useLauncherDrag({
4439
4488
  subject
4440
4489
  }) {
4441
4490
  const spot = useSyncExternalStore4(subscribeSpot, getSpot, getDefaultSpot);
4442
- const [isMoving, setIsMoving] = useState12(false);
4443
- const grip = useRef11(null);
4444
- const justDragged = useRef11(false);
4491
+ const [isMoving, setIsMoving] = useState11(false);
4492
+ const grip = useRef12(null);
4493
+ const justDragged = useRef12(false);
4445
4494
  const measure = useCallback6(() => {
4446
4495
  const box = boxRef.current;
4447
4496
  const button = buttonRef.current;
@@ -4492,12 +4541,12 @@ function useLauncherDrag({
4492
4541
  box.style.top = `${metrics.boxTop - taken.bounds.top}px`;
4493
4542
  box.style.setProperty(BUTTON_Y, `${metrics.buttonInBox}px`);
4494
4543
  }, [boxRef, measure, spot]);
4495
- useLayoutEffect6(place, [place]);
4496
- useEffect8(() => {
4544
+ useLayoutEffect7(place, [place]);
4545
+ useEffect7(() => {
4497
4546
  window.addEventListener("resize", place);
4498
4547
  return () => window.removeEventListener("resize", place);
4499
4548
  }, [place]);
4500
- useEffect8(() => {
4549
+ useEffect7(() => {
4501
4550
  const box = boxRef.current;
4502
4551
  if (!box || typeof ResizeObserver === "undefined") return;
4503
4552
  const observer = new ResizeObserver(place);
@@ -4625,7 +4674,7 @@ function useLauncherDrag({
4625
4674
  },
4626
4675
  [finishDrag]
4627
4676
  );
4628
- useEffect8(() => {
4677
+ useEffect7(() => {
4629
4678
  if (!isMoving) return;
4630
4679
  const end = () => finishDrag();
4631
4680
  window.addEventListener("pointerup", end);
@@ -4643,7 +4692,7 @@ function useLauncherDrag({
4643
4692
  onPointerUp: handlePointerUp,
4644
4693
  onPointerCancel: handlePointerUp
4645
4694
  };
4646
- useEffect8(() => {
4695
+ useEffect7(() => {
4647
4696
  function abandon() {
4648
4697
  if (grip.current === null) return;
4649
4698
  grip.current = null;
@@ -4682,15 +4731,15 @@ function clamp2(value, low, high) {
4682
4731
  }
4683
4732
 
4684
4733
  // ../../apps/web/src/components/support-widget/use-teaser-placement.ts
4685
- import { useLayoutEffect as useLayoutEffect7, useState as useState13 } from "react";
4734
+ import { useLayoutEffect as useLayoutEffect8, useState as useState12 } from "react";
4686
4735
  var ROOM_ABOVE_PX = 128;
4687
4736
  function useTeaserPlacement({
4688
4737
  buttonRef,
4689
4738
  enabled: enabled2,
4690
4739
  isMoving
4691
4740
  }) {
4692
- const [placement, setPlacement] = useState13("above");
4693
- useLayoutEffect7(() => {
4741
+ const [placement, setPlacement] = useState12("above");
4742
+ useLayoutEffect8(() => {
4694
4743
  if (!enabled2 || isMoving) return;
4695
4744
  const measure = () => {
4696
4745
  const button = buttonRef.current;
@@ -4706,15 +4755,15 @@ function useTeaserPlacement({
4706
4755
 
4707
4756
  // ../../apps/web/src/components/support-widget/use-widget-sheet.ts
4708
4757
  import {
4709
- useLayoutEffect as useLayoutEffect8,
4710
- useState as useState14,
4758
+ useLayoutEffect as useLayoutEffect9,
4759
+ useState as useState13,
4711
4760
  useSyncExternalStore as useSyncExternalStore5
4712
4761
  } from "react";
4713
4762
 
4714
4763
  // ../../apps/web/src/components/support-widget/use-page-lock.ts
4715
- import { useEffect as useEffect9 } from "react";
4764
+ import { useEffect as useEffect8 } from "react";
4716
4765
  function usePageLock(isLocked) {
4717
- useEffect9(() => {
4766
+ useEffect8(() => {
4718
4767
  if (!isLocked) return;
4719
4768
  const { style } = document.body;
4720
4769
  const held = {
@@ -4743,7 +4792,7 @@ function usePageLock(isLocked) {
4743
4792
 
4744
4793
  // ../../apps/web/src/components/support-widget/use-widget-sheet.ts
4745
4794
  var PHONE = "(max-width: 40rem)";
4746
- var SETTLE_MS2 = 400;
4795
+ var SETTLE_MS = 400;
4747
4796
  var KEYBOARD_PX = 96;
4748
4797
  function subscribePhone(onChange) {
4749
4798
  const media = window.matchMedia(PHONE);
@@ -4762,13 +4811,13 @@ function useWidgetSheet({
4762
4811
  isPhoneNow,
4763
4812
  isPhoneOnServer
4764
4813
  );
4765
- const [isFramed, setIsFramed] = useState14(false);
4766
- useLayoutEffect8(() => {
4814
+ const [isFramed, setIsFramed] = useState13(false);
4815
+ useLayoutEffect9(() => {
4767
4816
  setIsFramed(boxRef.current?.offsetParent != null);
4768
4817
  }, [boxRef, isPhone]);
4769
4818
  const isSheet = isPhone && !isFramed;
4770
4819
  const isUp = isSheet && isOpen;
4771
- useLayoutEffect8(() => {
4820
+ useLayoutEffect9(() => {
4772
4821
  if (!isUp) return;
4773
4822
  const panel = panelRef.current;
4774
4823
  const viewport = window.visualViewport;
@@ -4797,7 +4846,7 @@ function useWidgetSheet({
4797
4846
  frame = performance.now() < until ? requestAnimationFrame(follow) : 0;
4798
4847
  };
4799
4848
  const track = () => {
4800
- until = performance.now() + SETTLE_MS2;
4849
+ until = performance.now() + SETTLE_MS;
4801
4850
  if (frame === 0) frame = requestAnimationFrame(follow);
4802
4851
  };
4803
4852
  fit();
@@ -4820,7 +4869,7 @@ function useWidgetSheet({
4820
4869
  }
4821
4870
 
4822
4871
  // ../../apps/web/src/components/support-widget/widget-modal.tsx
4823
- import { useRef as useRef12 } from "react";
4872
+ import { useRef as useRef13 } from "react";
4824
4873
  import { createPortal } from "react-dom";
4825
4874
 
4826
4875
  // ../../apps/web/src/components/support-widget/use-hydrated.ts
@@ -4834,9 +4883,9 @@ function useHydrated() {
4834
4883
  }
4835
4884
 
4836
4885
  // ../../apps/web/src/components/support-widget/use-scroll-lock.ts
4837
- import { useEffect as useEffect10 } from "react";
4886
+ import { useEffect as useEffect9 } from "react";
4838
4887
  function useScrollLock(isLocked) {
4839
- useEffect10(() => {
4888
+ useEffect9(() => {
4840
4889
  if (!isLocked) return;
4841
4890
  const { body } = document;
4842
4891
  const held = {
@@ -4857,7 +4906,7 @@ function useScrollLock(isLocked) {
4857
4906
  }
4858
4907
 
4859
4908
  // ../../apps/web/src/components/support-widget/widget-modal.tsx
4860
- import { jsx as jsx33, jsxs as jsxs17 } from "react/jsx-runtime";
4909
+ import { jsx as jsx33, jsxs as jsxs18 } from "react/jsx-runtime";
4861
4910
  var LAYER = 2147483e3;
4862
4911
  var WIDTH = "50rem";
4863
4912
  var HEIGHT = "40rem";
@@ -4873,13 +4922,13 @@ function SupportModal({
4873
4922
  label,
4874
4923
  children
4875
4924
  }) {
4876
- const layerRef = useRef12(null);
4925
+ const layerRef = useRef13(null);
4877
4926
  const isHydrated = useHydrated();
4878
4927
  const isSheet = useWidgetSheet({ boxRef: layerRef, panelRef, isOpen });
4879
4928
  useScrollLock(isOpen && !isSheet);
4880
4929
  if (!isHydrated) return null;
4881
4930
  return createPortal(
4882
- /* @__PURE__ */ jsxs17(
4931
+ /* @__PURE__ */ jsxs18(
4883
4932
  "div",
4884
4933
  {
4885
4934
  ref: layerRef,
@@ -4924,7 +4973,7 @@ function SupportModal({
4924
4973
  isOpen ? "translate-y-0 opacity-100" : "translate-y-8 opacity-0"
4925
4974
  ] : [
4926
4975
  // Never past the layer's padding, whatever the size says.
4927
- "relative max-h-full max-w-full rounded-xl shadow-2xl",
4976
+ `relative max-h-full max-w-full ${WIDGET_CORNER} shadow-2xl`,
4928
4977
  MOTION,
4929
4978
  isOpen ? [OPEN, "scale-100 opacity-100 translate-y-0"] : [SHUT, "scale-[0.96] opacity-0 translate-y-2"]
4930
4979
  ]
@@ -4947,7 +4996,7 @@ var SHEET_PANEL = cn(
4947
4996
  );
4948
4997
 
4949
4998
  // ../../apps/web/src/components/support-widget/widget-teaser.tsx
4950
- import { useState as useState15 } from "react";
4999
+ import { useState as useState14 } from "react";
4951
5000
 
4952
5001
  // ../../apps/web/src/components/support-widget/plain-text.ts
4953
5002
  function plainTextOf(markdown) {
@@ -4955,7 +5004,7 @@ function plainTextOf(markdown) {
4955
5004
  }
4956
5005
 
4957
5006
  // ../../apps/web/src/components/support-widget/widget-teaser.tsx
4958
- import { Fragment as Fragment29, jsx as jsx34, jsxs as jsxs18 } from "react/jsx-runtime";
5007
+ import { Fragment as Fragment29, jsx as jsx34, jsxs as jsxs19 } from "react/jsx-runtime";
4959
5008
  var WIDTH2 = "min(20rem, 100%)";
4960
5009
  var GAP = "0.5rem";
4961
5010
  function WidgetTeaser({
@@ -4968,13 +5017,13 @@ function WidgetTeaser({
4968
5017
  onOpen,
4969
5018
  onDismiss
4970
5019
  }) {
4971
- const [held, setHeld] = useState15(message);
5020
+ const [held, setHeld] = useState14(message);
4972
5021
  if (message !== null && message !== held) setHeld(message);
4973
5022
  const shown = message ?? held;
4974
5023
  if (shown === null) return null;
4975
5024
  const agent = shown.agent;
4976
5025
  const text = plainTextOf(shown.body);
4977
- return /* @__PURE__ */ jsxs18(
5026
+ return /* @__PURE__ */ jsxs19(
4978
5027
  "div",
4979
5028
  {
4980
5029
  inert: !isShown,
@@ -4999,7 +5048,7 @@ function WidgetTeaser({
4999
5048
  ] : "pointer-events-none scale-95 opacity-0 duration-150 ease-out-quint"
5000
5049
  ),
5001
5050
  children: [
5002
- /* @__PURE__ */ jsxs18(
5051
+ /* @__PURE__ */ jsxs19(
5003
5052
  "button",
5004
5053
  {
5005
5054
  type: "button",
@@ -5008,17 +5057,17 @@ function WidgetTeaser({
5008
5057
  className: cn(
5009
5058
  // Its own radius scale, like the panel, so the app's token cannot
5010
5059
  // 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]",
5060
+ `block w-full ${WIDGET_CORNER} border bg-card p-3.5 pr-10 text-left text-card-foreground shadow-lg [--radius:0.625rem]`,
5012
5061
  "transition-[background-color,box-shadow] outline-none",
5013
5062
  "hover:bg-muted/50 focus-visible:ring-3 focus-visible:ring-ring/50",
5014
5063
  "cursor-pointer select-none"
5015
5064
  ),
5016
5065
  children: [
5017
- /* @__PURE__ */ jsxs18("span", { className: "flex items-center gap-2", children: [
5018
- agent && /* @__PURE__ */ jsxs18(Fragment29, { children: [
5066
+ /* @__PURE__ */ jsxs19("span", { className: "flex items-center gap-2", children: [
5067
+ agent && /* @__PURE__ */ jsxs19(Fragment29, { children: [
5019
5068
  /* @__PURE__ */ jsx34(WidgetAvatar, { agent, className: "size-6 rounded-md" }),
5020
5069
  /* @__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" })
5070
+ agent.kind === "ai" && /* @__PURE__ */ jsx34(Badge, { variant: "muted", className: "shrink-0 rounded-md bg-muted px-1 inset-ring-0", children: "AI" })
5022
5071
  ] }),
5023
5072
  /* @__PURE__ */ jsx34("span", { className: "ml-auto shrink-0 text-sm text-muted-foreground tabular-nums", children: shown.time })
5024
5073
  ] }),
@@ -5048,14 +5097,13 @@ function WidgetTeaser({
5048
5097
  }
5049
5098
 
5050
5099
  // ../../apps/web/src/components/support-widget/widget-launcher.tsx
5051
- import { Fragment as Fragment30, jsx as jsx35, jsxs as jsxs19 } from "react/jsx-runtime";
5100
+ import { Fragment as Fragment30, jsx as jsx35, jsxs as jsxs20 } from "react/jsx-runtime";
5052
5101
  var DEFAULT_WIDTH = "28rem";
5053
5102
  var DEFAULT_HEIGHT = "40rem";
5054
5103
  var DEFAULT_BUTTON_SIZE = "3rem";
5055
5104
  var EXPANDED_WIDTH = "34rem";
5056
5105
  var EXPANDED_HEIGHT = "52rem";
5057
- var PANEL_RADIUS = "0.75rem";
5058
- var OPEN_CLIP = `inset(0px 0px 0px 0px round ${PANEL_RADIUS})`;
5106
+ var OPEN_CLIP = `inset(0px 0px 0px 0px round ${WIDGET_RADIUS})`;
5059
5107
  var buttonY = (buttonSize) => `var(${BUTTON_Y}, calc(100% - ${buttonSize}))`;
5060
5108
  var MARGIN = "1rem";
5061
5109
  var CLOSED_SCALE = "scale(0.94)";
@@ -5105,14 +5153,14 @@ function SupportLauncher({
5105
5153
  onOpenChange,
5106
5154
  className
5107
5155
  }) {
5108
- const [uncontrolledOpen, setUncontrolledOpen] = useState16(defaultOpen);
5156
+ const [uncontrolledOpen, setUncontrolledOpen] = useState15(defaultOpen);
5109
5157
  const isOpen = open ?? uncontrolledOpen;
5110
5158
  const hasBubble = launcher === "bubble";
5111
5159
  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);
5160
+ const [isExpanded, setExpanded] = useState15(false);
5161
+ const launcherRef = useRef14(null);
5162
+ const panelRef = useRef14(null);
5163
+ const boxRef = useRef14(null);
5116
5164
  const drag = useLauncherDrag({
5117
5165
  boxRef,
5118
5166
  buttonRef: launcherRef,
@@ -5132,8 +5180,8 @@ function SupportLauncher({
5132
5180
  enabled: hasTeaser,
5133
5181
  isMoving: drag.isMoving
5134
5182
  });
5135
- const opener = useRef13(null);
5136
- const wasOpen = useRef13(isOpen);
5183
+ const opener = useRef14(null);
5184
+ const wasOpen = useRef14(isOpen);
5137
5185
  const setOpen = useCallback7(
5138
5186
  (next) => {
5139
5187
  if (open === void 0) setUncontrolledOpen(next);
@@ -5149,10 +5197,10 @@ function SupportLauncher({
5149
5197
  },
5150
5198
  [onFollow, isSheet, isModal, close]
5151
5199
  );
5152
- useEffect11(() => {
5200
+ useEffect10(() => {
5153
5201
  if (isOpen) warmMarkdown();
5154
5202
  }, [isOpen]);
5155
- useEffect11(() => {
5203
+ useEffect10(() => {
5156
5204
  if (!isOpen) return;
5157
5205
  function handleKeyDown(event) {
5158
5206
  if (event.key === "Escape") close();
@@ -5160,7 +5208,7 @@ function SupportLauncher({
5160
5208
  document.addEventListener("keydown", handleKeyDown);
5161
5209
  return () => document.removeEventListener("keydown", handleKeyDown);
5162
5210
  }, [isOpen, close]);
5163
- useEffect11(() => {
5211
+ useEffect10(() => {
5164
5212
  if (wasOpen.current === isOpen) return;
5165
5213
  wasOpen.current = isOpen;
5166
5214
  if (isOpen) {
@@ -5201,8 +5249,8 @@ function SupportLauncher({
5201
5249
  compact: isModal
5202
5250
  }
5203
5251
  );
5204
- return /* @__PURE__ */ jsxs19(Fragment30, { children: [
5205
- /* @__PURE__ */ jsxs19(
5252
+ return /* @__PURE__ */ jsxs20(Fragment30, { children: [
5253
+ /* @__PURE__ */ jsxs20(
5206
5254
  "div",
5207
5255
  {
5208
5256
  ref: boxRef,
@@ -5271,7 +5319,7 @@ function SupportLauncher({
5271
5319
  children: widget(isSheet)
5272
5320
  }
5273
5321
  ),
5274
- hasBubble && /* @__PURE__ */ jsxs19(
5322
+ hasBubble && /* @__PURE__ */ jsxs20(
5275
5323
  "button",
5276
5324
  {
5277
5325
  ref: launcherRef,
@@ -5386,9 +5434,9 @@ function SupportLauncher({
5386
5434
  }
5387
5435
 
5388
5436
  // ../../apps/web/src/components/support-widget/widget-spotlight.tsx
5389
- import { useEffect as useEffect12, useRef as useRef14, useState as useState17 } from "react";
5437
+ import { useEffect as useEffect11, useRef as useRef15, useState as useState16 } from "react";
5390
5438
  import { createPortal as createPortal2 } from "react-dom";
5391
- import { jsx as jsx36, jsxs as jsxs20 } from "react/jsx-runtime";
5439
+ import { jsx as jsx36, jsxs as jsxs21 } from "react/jsx-runtime";
5392
5440
  var FIND_TIMEOUT_MS = 4e3;
5393
5441
  var HOLD_MS = 2600;
5394
5442
  var FADE_MS = 300;
@@ -5397,10 +5445,10 @@ function prefersReducedMotion4() {
5397
5445
  return window.matchMedia("(prefers-reduced-motion: reduce)").matches;
5398
5446
  }
5399
5447
  function WidgetSpotlight({ selector }) {
5400
- const [isShown, setIsShown] = useState17(false);
5401
- const [isLeaving, setIsLeaving] = useState17(false);
5402
- const ring = useRef14(null);
5403
- useEffect12(() => {
5448
+ const [isShown, setIsShown] = useState16(false);
5449
+ const [isLeaving, setIsLeaving] = useState16(false);
5450
+ const ring = useRef15(null);
5451
+ useEffect11(() => {
5404
5452
  let target = null;
5405
5453
  let frame = 0;
5406
5454
  let startedAt = 0;
@@ -5459,7 +5507,7 @@ function WidgetSpotlight({ selector }) {
5459
5507
  }, [selector]);
5460
5508
  if (!isShown || typeof document === "undefined") return null;
5461
5509
  return createPortal2(
5462
- /* @__PURE__ */ jsxs20(
5510
+ /* @__PURE__ */ jsxs21(
5463
5511
  "div",
5464
5512
  {
5465
5513
  ref: ring,
@@ -5497,7 +5545,7 @@ function WidgetSpotlight({ selector }) {
5497
5545
  }
5498
5546
 
5499
5547
  // ../../apps/web/src/components/support-widget/sdk/control.ts
5500
- import { useSyncExternalStore as useSyncExternalStore11 } from "react";
5548
+ import { useSyncExternalStore as useSyncExternalStore12 } from "react";
5501
5549
 
5502
5550
  // ../backend/convex/shared/customData.ts
5503
5551
  var MAX_CUSTOM_ENTRIES = 8;
@@ -5562,23 +5610,23 @@ function restoreCustomData(entries) {
5562
5610
  }
5563
5611
 
5564
5612
  // ../../apps/web/src/components/support-widget/sdk/open-store.ts
5565
- import { useCallback as useCallback8, useEffect as useEffect13, useSyncExternalStore as useSyncExternalStore7 } from "react";
5613
+ import { useCallback as useCallback8, useEffect as useEffect12, useSyncExternalStore as useSyncExternalStore7 } from "react";
5566
5614
  var isDevelopment = typeof process !== "undefined" && process.env.NODE_ENV === "development";
5567
5615
  function createOpenStore(component) {
5568
5616
  let requested = null;
5569
5617
  let effective = false;
5570
5618
  let mounted = 0;
5571
- const listeners6 = /* @__PURE__ */ new Set();
5619
+ const listeners7 = /* @__PURE__ */ new Set();
5572
5620
  function emit() {
5573
- for (const listener of listeners6) listener();
5621
+ for (const listener of listeners7) listener();
5574
5622
  }
5575
5623
  return {
5576
5624
  /** The component a warning should name, so it reads like the host's code. */
5577
5625
  component,
5578
5626
  subscribe(listener) {
5579
- listeners6.add(listener);
5627
+ listeners7.add(listener);
5580
5628
  return () => {
5581
- listeners6.delete(listener);
5629
+ listeners7.delete(listener);
5582
5630
  };
5583
5631
  },
5584
5632
  readRequested: () => requested,
@@ -5642,7 +5690,7 @@ function useOpenState(store3, {
5642
5690
  store3.readServerRequested
5643
5691
  );
5644
5692
  const isOpen = open ?? asked ?? defaultOpen;
5645
- useEffect13(() => {
5693
+ useEffect12(() => {
5646
5694
  store3.settle(isOpen);
5647
5695
  }, [store3, isOpen]);
5648
5696
  const setOpen = useCallback8(
@@ -5655,7 +5703,7 @@ function useOpenState(store3, {
5655
5703
  return [isOpen, setOpen];
5656
5704
  }
5657
5705
  function useRegister(store3) {
5658
- useEffect13(() => {
5706
+ useEffect12(() => {
5659
5707
  if (store3.countMounted() > 1 && isDevelopment) {
5660
5708
  console.warn(
5661
5709
  `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.`
@@ -5665,6 +5713,30 @@ function useRegister(store3) {
5665
5713
  }, [store3]);
5666
5714
  }
5667
5715
 
5716
+ // ../backend/convex/shared/auditResult.ts
5717
+ import { ConvexError } from "convex/values";
5718
+ function unwrapAuditResult(result) {
5719
+ if (result.auditResult === "error") throw new ConvexError(result.data);
5720
+ return result.value;
5721
+ }
5722
+
5723
+ // ../backend/client/auditedMutation.ts
5724
+ function unwrapMutationResult(result) {
5725
+ if (result && typeof result === "object" && "auditResult" in result && (result.auditResult === "ok" && "value" in result || result.auditResult === "error" && "data" in result)) {
5726
+ return unwrapAuditResult(
5727
+ result
5728
+ );
5729
+ }
5730
+ return result;
5731
+ }
5732
+ function auditedClientMutation(mutation) {
5733
+ const invoke = async (...args) => {
5734
+ return unwrapMutationResult(await mutation(...args));
5735
+ };
5736
+ invoke.withOptimisticUpdate = (update) => auditedClientMutation(mutation.withOptimisticUpdate(update));
5737
+ return invoke;
5738
+ }
5739
+
5668
5740
  // ../backend/convex/_generated/api.js
5669
5741
  import { anyApi, componentsGeneric } from "convex/server";
5670
5742
  var api = anyApi;
@@ -5702,6 +5774,33 @@ function cleanCrash(input) {
5702
5774
  import { ConvexHttpClient } from "convex/browser";
5703
5775
  import { useSyncExternalStore as useSyncExternalStore10 } from "react";
5704
5776
 
5777
+ // ../../apps/web/src/lib/upload.ts
5778
+ async function uploadFile(getUploadUrl, file) {
5779
+ const url = await getUploadUrl();
5780
+ const response = await fetch(url, {
5781
+ method: "POST",
5782
+ headers: { "Content-Type": file.type || "application/octet-stream" },
5783
+ body: file
5784
+ });
5785
+ if (!response.ok) {
5786
+ throw new Error(`Uploading ${file.name} failed with ${response.status}`);
5787
+ }
5788
+ const { storageId } = await response.json();
5789
+ return storageId;
5790
+ }
5791
+ async function uploadAttachments(getUploadUrl, files) {
5792
+ const attachments = [];
5793
+ for (const file of files) {
5794
+ attachments.push({
5795
+ storageId: await uploadFile(getUploadUrl, file),
5796
+ name: file.name,
5797
+ size: file.size,
5798
+ type: file.type || "application/octet-stream"
5799
+ });
5800
+ }
5801
+ return attachments;
5802
+ }
5803
+
5705
5804
  // ../../apps/web/src/components/support-widget/sdk/context.ts
5706
5805
  var BROWSERS = [
5707
5806
  { name: "Edge", token: /Edg(?:e|A|iOS)?\/(\d+)/ },
@@ -5867,7 +5966,7 @@ function cleanClientDiagnostics(input) {
5867
5966
  }
5868
5967
 
5869
5968
  // ../../apps/web/src/components/support-widget/sdk/diagnostics.ts
5870
- import { useCallback as useCallback9, useEffect as useEffect14, useRef as useRef15 } from "react";
5969
+ import { useCallback as useCallback9, useEffect as useEffect13, useRef as useRef16 } from "react";
5871
5970
  function settingsOf(option) {
5872
5971
  if (option === void 0 || option === false) return null;
5873
5972
  if (option === true) return { errors: true, network: false, console: false };
@@ -6003,13 +6102,13 @@ function snapshotNow(extraState) {
6003
6102
  function useDiagnostics(option, appState) {
6004
6103
  const settings = settingsOf(option);
6005
6104
  const key = settings === null ? null : JSON.stringify(settings);
6006
- const stateRef = useRef15(appState);
6007
- useEffect14(() => {
6105
+ const stateRef = useRef16(appState);
6106
+ useEffect13(() => {
6008
6107
  stateRef.current = appState;
6009
6108
  latestState = appState;
6010
6109
  });
6011
- const sentAt = useRef15(null);
6012
- useEffect14(() => {
6110
+ const sentAt = useRef16(null);
6111
+ useEffect13(() => {
6013
6112
  if (key === null) return;
6014
6113
  return collect(JSON.parse(key));
6015
6114
  }, [key]);
@@ -6023,7 +6122,7 @@ function useDiagnostics(option, appState) {
6023
6122
  }
6024
6123
 
6025
6124
  // ../../apps/web/src/components/support-widget/sdk/install.ts
6026
- import { useEffect as useEffect15 } from "react";
6125
+ import { useEffect as useEffect14 } from "react";
6027
6126
  var install = null;
6028
6127
  var listeners3 = /* @__PURE__ */ new Set();
6029
6128
  function rememberInstall(next) {
@@ -6045,12 +6144,115 @@ function hasInstall() {
6045
6144
  return install !== null;
6046
6145
  }
6047
6146
  function useRememberInstall(apiKey, user) {
6048
- useEffect15(() => {
6147
+ useEffect14(() => {
6049
6148
  if (apiKey === void 0) return;
6050
6149
  rememberInstall({ apiKey, ...user ? { user } : {} });
6051
6150
  });
6052
6151
  }
6053
6152
 
6153
+ // ../../apps/web/src/components/support-widget/sdk/screenshot.ts
6154
+ var MAX_WIDTH = 1440;
6155
+ var TIMEOUT_MS = 6e3;
6156
+ var TYPE = "image/jpeg";
6157
+ var QUALITY = 0.85;
6158
+ var SCREENSHOT_NAME = "screenshot.jpg";
6159
+ var PIN_ATTRIBUTE = "data-median-pin";
6160
+ async function capturePage() {
6161
+ if (typeof window === "undefined" || typeof document === "undefined") {
6162
+ return null;
6163
+ }
6164
+ const page = document.documentElement;
6165
+ if (page === null) return null;
6166
+ try {
6167
+ const blob = await Promise.race([
6168
+ draw(page),
6169
+ new Promise((resolve) => setTimeout(() => resolve(null), TIMEOUT_MS))
6170
+ ]);
6171
+ if (blob === null) return null;
6172
+ return new File([blob], SCREENSHOT_NAME, { type: TYPE });
6173
+ } catch (caught) {
6174
+ console.warn("Median: taking a screenshot for this report failed", caught);
6175
+ return null;
6176
+ }
6177
+ }
6178
+ async function draw(page) {
6179
+ const { domToBlob } = await import("./dist-VC6ZVTA2.js");
6180
+ const width = page.clientWidth || window.innerWidth;
6181
+ const height = page.clientHeight || window.innerHeight;
6182
+ const unpin = pinFixed();
6183
+ try {
6184
+ return await domToBlob(page, {
6185
+ width,
6186
+ height,
6187
+ // Their view, not the whole document: the page is drawn in full and
6188
+ // the part they are looking at is what the frame is put around. A page
6189
+ // held still by `usePageLock` has already been scrolled to nothing,
6190
+ // and is shifted by nothing here.
6191
+ style: {
6192
+ transform: `translate(${-window.scrollX}px, ${-window.scrollY}px)`,
6193
+ transformOrigin: "top left"
6194
+ },
6195
+ scale: Math.min(1, MAX_WIDTH / Math.max(width, 1)),
6196
+ type: TYPE,
6197
+ quality: QUALITY,
6198
+ // JPEG has no transparency, so anything clear would come out black.
6199
+ backgroundColor: pageBackground(),
6200
+ filter: notOurs,
6201
+ onCloneNode: placePinned,
6202
+ timeout: TIMEOUT_MS,
6203
+ features: { copyScrollbar: false }
6204
+ });
6205
+ } finally {
6206
+ unpin();
6207
+ }
6208
+ }
6209
+ function pinFixed() {
6210
+ const pinned = [];
6211
+ for (const element of document.body.querySelectorAll("*")) {
6212
+ if (window.getComputedStyle(element).position !== "fixed") continue;
6213
+ const box = element.getBoundingClientRect();
6214
+ if (box.width === 0 || box.height === 0) continue;
6215
+ element.setAttribute(
6216
+ PIN_ATTRIBUTE,
6217
+ [box.left, box.top, box.width, box.height].join(",")
6218
+ );
6219
+ pinned.push(element);
6220
+ }
6221
+ return () => {
6222
+ for (const element of pinned) element.removeAttribute(PIN_ATTRIBUTE);
6223
+ };
6224
+ }
6225
+ function placePinned(clone) {
6226
+ if (!(clone instanceof Element)) return;
6227
+ for (const element of clone.querySelectorAll(`[${PIN_ATTRIBUTE}]`)) {
6228
+ const [left, top, width, height] = (element.getAttribute(PIN_ATTRIBUTE) ?? "").split(",").map(Number);
6229
+ element.removeAttribute(PIN_ATTRIBUTE);
6230
+ if (!(element instanceof HTMLElement) || Number.isNaN(top)) continue;
6231
+ element.style.setProperty("left", `${left + window.scrollX}px`, "important");
6232
+ element.style.setProperty("top", `${top + window.scrollY}px`, "important");
6233
+ element.style.setProperty("right", "auto", "important");
6234
+ element.style.setProperty("bottom", "auto", "important");
6235
+ element.style.setProperty("width", `${width}px`, "important");
6236
+ element.style.setProperty("height", `${height}px`, "important");
6237
+ element.style.setProperty("margin", "0", "important");
6238
+ }
6239
+ }
6240
+ function notOurs(node) {
6241
+ return !(node instanceof Element && node.hasAttribute("data-median-widget"));
6242
+ }
6243
+ function pageBackground() {
6244
+ for (const element of [document.body, document.documentElement]) {
6245
+ if (element === null) continue;
6246
+ const color = window.getComputedStyle(element).backgroundColor;
6247
+ if (color && !isClear(color)) return color;
6248
+ }
6249
+ return "#ffffff";
6250
+ }
6251
+ function isClear(color) {
6252
+ const clear = color.replace(/\s/g, "");
6253
+ return clear === "transparent" || clear === "rgba(0,0,0,0)";
6254
+ }
6255
+
6054
6256
  // ../../apps/web/src/components/support-widget/sdk/session.ts
6055
6257
  var STORAGE_PREFIX = "median:session:";
6056
6258
  var inMemorySessions = /* @__PURE__ */ new Map();
@@ -6093,20 +6295,30 @@ function resetSessions() {
6093
6295
  }
6094
6296
 
6095
6297
  // ../../apps/web/src/components/support-widget/sdk/use-median-thread.ts
6096
- import { useMutation, useQuery } from "convex/react";
6298
+ import { useQuery } from "convex/react";
6299
+
6300
+ // ../../apps/web/src/hooks/use-audited-mutation.ts
6301
+ import { useMemo as useMemo2 } from "react";
6302
+ import { useMutation } from "convex/react";
6303
+ function useAuditedMutation(reference) {
6304
+ const mutation = useMutation(reference);
6305
+ return useMemo2(() => auditedClientMutation(mutation), [mutation]);
6306
+ }
6307
+
6308
+ // ../../apps/web/src/components/support-widget/sdk/use-median-thread.ts
6097
6309
  import {
6098
6310
  useCallback as useCallback11,
6099
6311
  useEffect as useEffect17,
6100
- useMemo as useMemo2,
6101
- useState as useState19,
6312
+ useMemo as useMemo3,
6313
+ useState as useState18,
6102
6314
  useSyncExternalStore as useSyncExternalStore9
6103
6315
  } from "react";
6104
6316
 
6105
6317
  // ../../apps/web/src/hooks/use-now.ts
6106
- import { useEffect as useEffect16, useState as useState18 } from "react";
6318
+ import { useEffect as useEffect15, useState as useState17 } from "react";
6107
6319
  function useNow(intervalMs = 3e4) {
6108
- const [now, setNow] = useState18(() => Date.now());
6109
- useEffect16(() => {
6320
+ const [now, setNow] = useState17(() => Date.now());
6321
+ useEffect15(() => {
6110
6322
  const timer = setInterval(() => setNow(Date.now()), intervalMs);
6111
6323
  return () => clearInterval(timer);
6112
6324
  }, [intervalMs]);
@@ -6114,13 +6326,13 @@ function useNow(intervalMs = 3e4) {
6114
6326
  }
6115
6327
 
6116
6328
  // ../../apps/web/src/lib/errors.ts
6117
- import { ConvexError } from "convex/values";
6329
+ import { ConvexError as ConvexError2 } from "convex/values";
6118
6330
  var FALLBACK = "Something went wrong on our end. Please try again.";
6119
6331
  function isErrorPayload(data) {
6120
6332
  return typeof data === "object" && data !== null && "message" in data && typeof data.message === "string";
6121
6333
  }
6122
6334
  function errorMessage(error) {
6123
- if (error instanceof ConvexError) {
6335
+ if (error instanceof ConvexError2) {
6124
6336
  if (typeof error.data === "string") return error.data;
6125
6337
  if (isErrorPayload(error.data)) return error.data.message;
6126
6338
  }
@@ -6161,33 +6373,6 @@ function isNewDay(previous, next) {
6161
6373
  return startOfDay(previous) !== startOfDay(next);
6162
6374
  }
6163
6375
 
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
6376
  // ../../apps/web/src/components/support-widget/sdk/rating-skips.ts
6192
6377
  var STORAGE_PREFIX2 = "median:rating-skips:";
6193
6378
  var MAX_REMEMBERED = 20;
@@ -6222,45 +6407,56 @@ function writeSkippedRating(apiKey, conversationId) {
6222
6407
  }
6223
6408
  }
6224
6409
 
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
- }
6410
+ // ../../apps/web/src/components/support-widget/sdk/read-state.ts
6411
+ function isUnreadReply(message) {
6412
+ return message.sender === "agent" && !message.pending && !message.isRead;
6240
6413
  }
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 {
6414
+ function widgetReadReceipts(messages, now) {
6415
+ const receipts = /* @__PURE__ */ new Map();
6416
+ const blocked = /* @__PURE__ */ new Set();
6417
+ for (const message of messages) {
6418
+ const { conversationId } = message;
6419
+ if (!receipts.has(conversationId)) {
6420
+ receipts.set(conversationId, { conversationId });
6421
+ }
6422
+ if (blocked.has(conversationId)) continue;
6423
+ if (message.pending) {
6424
+ if (message.typingAt === null || now - message.typingAt <= TYPING_TTL_MS) {
6425
+ blocked.add(conversationId);
6426
+ }
6427
+ continue;
6428
+ }
6429
+ if (message.sender === "agent" && (message.body.trim() || message.attachments.length > 0)) {
6430
+ receipts.set(conversationId, { conversationId, messageId: message._id });
6431
+ }
6248
6432
  }
6433
+ return [...receipts.values()];
6249
6434
  }
6250
6435
 
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);
6436
+ // ../../apps/web/src/components/support-widget/sdk/use-screenshot-ask.ts
6437
+ import { useEffect as useEffect16, useRef as useRef17 } from "react";
6438
+ function useScreenshotAsk({
6439
+ ask,
6440
+ enabled: enabled2,
6441
+ answer
6442
+ }) {
6443
+ const answered = useRef17(/* @__PURE__ */ new Set());
6444
+ useEffect16(() => {
6445
+ if (ask === null || answered.current.has(ask.key)) return;
6446
+ answered.current.add(ask.key);
6447
+ void (async () => {
6448
+ const shot = enabled2 ? await capturePage() : null;
6449
+ await answer(ask.key, shot);
6450
+ })().catch((caught) => {
6451
+ console.warn("Median: answering the agent's request for a picture failed", caught);
6452
+ });
6453
+ }, [ask, enabled2, answer]);
6258
6454
  }
6259
6455
 
6260
6456
  // ../../apps/web/src/components/support-widget/sdk/visitor-email.ts
6261
6457
  import { useCallback as useCallback10, useSyncExternalStore as useSyncExternalStore8 } from "react";
6262
6458
  var STORAGE_PREFIX3 = "median:email:";
6263
- var inMemory3 = /* @__PURE__ */ new Map();
6459
+ var inMemory2 = /* @__PURE__ */ new Map();
6264
6460
  var listeners5 = /* @__PURE__ */ new Set();
6265
6461
  function subscribeVisitorEmail(listener) {
6266
6462
  listeners5.add(listener);
@@ -6274,11 +6470,11 @@ function readVisitorEmail(apiKey) {
6274
6470
  const stored = window.localStorage.getItem(`${STORAGE_PREFIX3}${apiKey}`);
6275
6471
  return stored && stored.length > 0 ? stored : null;
6276
6472
  } catch {
6277
- return inMemory3.get(apiKey) ?? null;
6473
+ return inMemory2.get(apiKey) ?? null;
6278
6474
  }
6279
6475
  }
6280
6476
  function writeVisitorEmail(apiKey, email) {
6281
- inMemory3.set(apiKey, email);
6477
+ inMemory2.set(apiKey, email);
6282
6478
  try {
6283
6479
  window.localStorage.setItem(`${STORAGE_PREFIX3}${apiKey}`, email);
6284
6480
  } catch {
@@ -6286,7 +6482,7 @@ function writeVisitorEmail(apiKey, email) {
6286
6482
  for (const listener of listeners5) listener();
6287
6483
  }
6288
6484
  function resetVisitorEmails() {
6289
- inMemory3.clear();
6485
+ inMemory2.clear();
6290
6486
  if (typeof window !== "undefined") {
6291
6487
  try {
6292
6488
  const stale = [];
@@ -6333,7 +6529,17 @@ var IDENTITY_COMPLAINTS = {
6333
6529
  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
6530
  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
6531
  };
6336
- function useMedianThread(apiKey, user, telemetry = true, isLocked = false, diagnostics, appState, requireEmail = true, messagePreview = true) {
6532
+ function useMedianThread({
6533
+ apiKey,
6534
+ user,
6535
+ telemetry = true,
6536
+ screenshot = true,
6537
+ isLocked = false,
6538
+ diagnostics,
6539
+ appState,
6540
+ requireEmail = true,
6541
+ messagePreview = true
6542
+ }) {
6337
6543
  const sessionId = useSyncExternalStore9(
6338
6544
  subscribeSession,
6339
6545
  useCallback11(() => getOrCreateSessionId(apiKey), [apiKey]),
@@ -6346,14 +6552,41 @@ function useMedianThread(apiKey, user, telemetry = true, isLocked = false, diagn
6346
6552
  api.widget.thread,
6347
6553
  sessionId === null || isLocked ? "skip" : { apiKey, sessionId }
6348
6554
  );
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);
6555
+ const send = useAuditedMutation(api.widget.sendV2);
6556
+ const sendTyping = useAuditedMutation(api.widget.setTyping);
6557
+ const sendPresence = useAuditedMutation(api.widget.presence);
6558
+ const rateConversation = useAuditedMutation(api.widget.rateV2);
6559
+ const identify = useAuditedMutation(api.widget.identifyV2);
6560
+ const generateUploadUrl = useAuditedMutation(api.widget.generateUploadUrlV2);
6561
+ const answerScreenshot = useAuditedMutation(api.widget.answerScreenshotV2);
6562
+ const answerAsk = useCallback11(
6563
+ async (key, shot) => {
6564
+ if (sessionId === null) return;
6565
+ let attachment;
6566
+ if (shot !== null) {
6567
+ try {
6568
+ [attachment] = await uploadAttachments(
6569
+ () => generateUploadUrl({ apiKey, sessionId }),
6570
+ [shot]
6571
+ );
6572
+ } catch (caught) {
6573
+ console.warn(
6574
+ "Median: the picture the agent asked for could not be uploaded",
6575
+ caught
6576
+ );
6577
+ }
6578
+ }
6579
+ await answerScreenshot({
6580
+ apiKey,
6581
+ sessionId,
6582
+ key,
6583
+ ...attachment === void 0 ? {} : { attachment }
6584
+ });
6585
+ },
6586
+ [apiKey, sessionId, generateUploadUrl, answerScreenshot]
6587
+ );
6355
6588
  const identityKey = user ? JSON.stringify(user) : null;
6356
- const identity = useMemo2(
6589
+ const identity = useMemo3(
6357
6590
  () => identityKey === null ? null : JSON.parse(identityKey),
6358
6591
  [identityKey]
6359
6592
  );
@@ -6376,9 +6609,9 @@ function useMedianThread(apiKey, user, telemetry = true, isLocked = false, diagn
6376
6609
  ) ?? false;
6377
6610
  const now = useNow(hasLiveDots ? 1e3 : 6e4);
6378
6611
  const takeDiagnostics = useDiagnostics(diagnostics, appState);
6379
- const [isSending, setIsSending] = useState19(false);
6380
- const [sendError, setSendError] = useState19(null);
6381
- const agent = useMemo2(
6612
+ const [isSending, setIsSending] = useState18(false);
6613
+ const [sendError, setSendError] = useState18(null);
6614
+ const agent = useMemo3(
6382
6615
  () => config === void 0 ? null : {
6383
6616
  id: "median-agent",
6384
6617
  name: config.agent.name,
@@ -6387,7 +6620,7 @@ function useMedianThread(apiKey, user, telemetry = true, isLocked = false, diagn
6387
6620
  },
6388
6621
  [config]
6389
6622
  );
6390
- const messages = useMemo2(() => {
6623
+ const messages = useMemo3(() => {
6391
6624
  if (!thread || agent === null) return [];
6392
6625
  const out = [];
6393
6626
  let previousAt;
@@ -6436,7 +6669,7 @@ function useMedianThread(apiKey, user, telemetry = true, isLocked = false, diagn
6436
6669
  }
6437
6670
  return out;
6438
6671
  }, [thread, agent, now]);
6439
- const presence = useMemo2(() => {
6672
+ const presence = useMemo3(() => {
6440
6673
  if (config === void 0 || agent === null) return null;
6441
6674
  const humans = /* @__PURE__ */ new Map();
6442
6675
  for (const message of messages) {
@@ -6450,17 +6683,16 @@ function useMedianThread(apiKey, user, telemetry = true, isLocked = false, diagn
6450
6683
  team: [...[...humans.values()].slice(-2), agent]
6451
6684
  };
6452
6685
  }, [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
- );
6686
+ const unread = useMemo3(() => {
6687
+ const unreadIds = new Set(
6688
+ thread?.messages.filter(isUnreadReply).map((message) => message._id)
6689
+ );
6690
+ return messages.filter((message) => unreadIds.has(message.id));
6691
+ }, [messages, thread]);
6460
6692
  const unreadCount = unread.length;
6461
6693
  const latestUnread = messagePreview && (config?.messagePreview ?? true) ? unread[unread.length - 1] ?? null : null;
6462
- const [picked, setPicked] = useState19(null);
6463
- const [skipped, setSkipped] = useState19(
6694
+ const [picked, setPicked] = useState18(null);
6695
+ const [skipped, setSkipped] = useState18(
6464
6696
  () => readSkippedRatings(apiKey)
6465
6697
  );
6466
6698
  const conversationId = thread?.conversationId ?? null;
@@ -6484,15 +6716,6 @@ function useMedianThread(apiKey, user, telemetry = true, isLocked = false, diagn
6484
6716
  setSkipped((current2) => new Set(current2).add(conversationId));
6485
6717
  }
6486
6718
  } : 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
6719
  async function handleSend(body, files) {
6497
6720
  if (sessionId === null || isSending) return;
6498
6721
  setSendError(null);
@@ -6534,12 +6757,29 @@ function useMedianThread(apiKey, user, telemetry = true, isLocked = false, diagn
6534
6757
  sendTyping({ apiKey, sessionId, isTyping }).catch(() => {
6535
6758
  });
6536
6759
  }
6760
+ const readsKey = JSON.stringify(
6761
+ widgetReadReceipts(thread?.messages ?? [], now)
6762
+ );
6763
+ const reads = useMemo3(
6764
+ () => JSON.parse(readsKey),
6765
+ [readsKey]
6766
+ );
6537
6767
  const reportPresence = useCallback11(() => {
6538
6768
  if (sessionId === null || isLocked) return;
6539
- sendPresence({ apiKey, sessionId }).catch(() => {
6769
+ sendPresence({
6770
+ apiKey,
6771
+ sessionId,
6772
+ reads
6773
+ }).catch((caught) => {
6774
+ console.warn("Median: support read status could not be synced", caught);
6540
6775
  });
6541
- }, [apiKey, sessionId, isLocked, sendPresence]);
6776
+ }, [apiKey, sessionId, isLocked, sendPresence, reads]);
6542
6777
  const ask = thread?.direction ?? null;
6778
+ useScreenshotAsk({
6779
+ ask: thread?.screenshotAsk ?? null,
6780
+ enabled: screenshot,
6781
+ answer: answerAsk
6782
+ });
6543
6783
  return {
6544
6784
  presence,
6545
6785
  messages,
@@ -6574,7 +6814,6 @@ function useMedianThread(apiKey, user, telemetry = true, isLocked = false, diagn
6574
6814
  unreadCount,
6575
6815
  /** The newest of those, for the card on the closed button. */
6576
6816
  latestUnread,
6577
- markSeen,
6578
6817
  /** Say the panel is on screen, so replies are not also emailed. */
6579
6818
  reportPresence,
6580
6819
  sendError,
@@ -6649,17 +6888,19 @@ async function reportError(error, options = {}) {
6649
6888
  const diagnostics = snapshotNow({ ...factsOf(crash), ...options.state });
6650
6889
  try {
6651
6890
  client ??= new ConvexHttpClient(MEDIAN_DEPLOYMENT_URL);
6652
- const outcome = await client.mutation(api.feedback.reportError, {
6891
+ const images = options.screenshot === false ? [] : await screenshotOf(client, apiKey, sessionId);
6892
+ const outcome = await client.mutation(api.feedback.reportErrorV2, {
6653
6893
  apiKey,
6654
6894
  sessionId,
6655
6895
  error: crash,
6896
+ ...images.length > 0 ? { images } : {},
6656
6897
  ...user ? { user: facts } : {},
6657
6898
  ...claim ? { identity: claim } : {},
6658
6899
  ...context ? { context } : {},
6659
6900
  ...page ? { page } : {},
6660
6901
  ...diagnostics ? { diagnostics } : {},
6661
6902
  ...custom ? { customData: custom } : {}
6662
- });
6903
+ }).then(unwrapMutationResult);
6663
6904
  if (outcome === "off") {
6664
6905
  if (custom) restoreCustomData(custom);
6665
6906
  return { reported: false, reason: REASONS.off };
@@ -6671,6 +6912,22 @@ async function reportError(error, options = {}) {
6671
6912
  return { reported: false, reason: REASONS.failed };
6672
6913
  }
6673
6914
  }
6915
+ async function screenshotOf(client3, apiKey, sessionId) {
6916
+ try {
6917
+ const shot = await capturePage();
6918
+ if (shot === null) return [];
6919
+ return await uploadAttachments(
6920
+ () => client3.mutation(api.widget.generateUploadUrlV2, { apiKey, sessionId }).then(unwrapMutationResult),
6921
+ [shot]
6922
+ );
6923
+ } catch (caught) {
6924
+ console.warn(
6925
+ "Median: the crash report's screenshot could not be sent",
6926
+ caught
6927
+ );
6928
+ return [];
6929
+ }
6930
+ }
6674
6931
  function useCanReportError() {
6675
6932
  const isInstalled = useSyncExternalStore10(
6676
6933
  subscribeInstall,
@@ -6683,6 +6940,34 @@ function useCanReportError() {
6683
6940
  return isInstalled && Boolean(MEDIAN_DEPLOYMENT_URL);
6684
6941
  }
6685
6942
 
6943
+ // ../../apps/web/src/components/support-widget/sdk/unread-store.ts
6944
+ import { useSyncExternalStore as useSyncExternalStore11 } from "react";
6945
+ var count = 0;
6946
+ var listeners6 = /* @__PURE__ */ new Set();
6947
+ var unreadStore = {
6948
+ subscribe(listener) {
6949
+ listeners6.add(listener);
6950
+ return () => {
6951
+ listeners6.delete(listener);
6952
+ };
6953
+ },
6954
+ read: () => count,
6955
+ /** The server has no thread to count, and neither does the first paint. */
6956
+ readServer: () => 0,
6957
+ set(next) {
6958
+ if (count === next) return;
6959
+ count = next;
6960
+ for (const listener of listeners6) listener();
6961
+ }
6962
+ };
6963
+ function useUnreadCount() {
6964
+ return useSyncExternalStore11(
6965
+ unreadStore.subscribe,
6966
+ unreadStore.read,
6967
+ unreadStore.readServer
6968
+ );
6969
+ }
6970
+
6686
6971
  // ../../apps/web/src/components/support-widget/sdk/control.ts
6687
6972
  var store = createOpenStore("MedianSupport");
6688
6973
  var medianSupport = {
@@ -6690,6 +6975,10 @@ var medianSupport = {
6690
6975
  get isOpen() {
6691
6976
  return store.readEffective();
6692
6977
  },
6978
+ /** Replies the visitor has not read. Zero while nothing is mounted. */
6979
+ get unreadCount() {
6980
+ return unreadStore.read();
6981
+ },
6693
6982
  open() {
6694
6983
  store.request(true);
6695
6984
  store.warnIfNothingIsMounted("the widget was asked to open");
@@ -6743,13 +7032,15 @@ var medianSupport = {
6743
7032
  }
6744
7033
  };
6745
7034
  function useMedianSupport() {
6746
- const isOpen = useSyncExternalStore11(
7035
+ const isOpen = useSyncExternalStore12(
6747
7036
  store.subscribe,
6748
7037
  store.readEffective,
6749
7038
  store.readServerEffective
6750
7039
  );
7040
+ const unreadCount = useUnreadCount();
6751
7041
  return {
6752
7042
  isOpen,
7043
+ unreadCount,
6753
7044
  open: medianSupport.open,
6754
7045
  close: medianSupport.close,
6755
7046
  toggle: medianSupport.toggle,
@@ -6766,10 +7057,10 @@ function useRegisterMedianSupport() {
6766
7057
  }
6767
7058
 
6768
7059
  // ../../apps/web/src/components/support-widget/sdk/error-boundary.tsx
6769
- import { ConvexError as ConvexError2 } from "convex/values";
7060
+ import { ConvexError as ConvexError3 } from "convex/values";
6770
7061
  import { Component } from "react";
6771
7062
  function adviceFor(error) {
6772
- if (error instanceof ConvexError2 && typeof error.data === "object" && error.data !== null && error.data.code === "invalid_api_key") {
7063
+ if (error instanceof ConvexError3 && typeof error.data === "object" && error.data !== null && error.data.code === "invalid_api_key") {
6773
7064
  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.";
6774
7065
  }
6775
7066
  return "The Median support widget hit an error and has been hidden. Your page is unaffected.";
@@ -6788,7 +7079,7 @@ var MedianErrorBoundary = class extends Component {
6788
7079
  };
6789
7080
 
6790
7081
  // ../../apps/web/src/components/support-widget/sdk/use-identity.ts
6791
- import { useEffect as useEffect18, useState as useState20 } from "react";
7082
+ import { useEffect as useEffect18, useState as useState19 } from "react";
6792
7083
  var NOTHING = {
6793
7084
  apiKey: void 0,
6794
7085
  user: void 0,
@@ -6825,7 +7116,7 @@ function useMissingCredentials(component, apiKey, endpoint) {
6825
7116
  }, [component, apiKey, endpoint]);
6826
7117
  }
6827
7118
  function useMedianIdentity(endpoint) {
6828
- const [answered, setAnswered] = useState20(null);
7119
+ const [answered, setAnswered] = useState19(null);
6829
7120
  useEffect18(() => {
6830
7121
  if (endpoint === void 0) return;
6831
7122
  let listening = true;
@@ -6861,6 +7152,45 @@ function useMedianIdentity(endpoint) {
6861
7152
  return WAITING;
6862
7153
  }
6863
7154
 
7155
+ // ../../apps/web/src/components/support-widget/sdk/use-median-branding.ts
7156
+ import { ConvexHttpClient as ConvexHttpClient2 } from "convex/browser";
7157
+ import { useEffect as useEffect19, useState as useState20 } from "react";
7158
+ function useMedianBranding(apiKey) {
7159
+ const [state, setState] = useState20({ key: apiKey, show: true });
7160
+ useEffect19(() => {
7161
+ if (!apiKey || !MEDIAN_DEPLOYMENT_URL) return;
7162
+ const client3 = new ConvexHttpClient2(MEDIAN_DEPLOYMENT_URL);
7163
+ let active = true;
7164
+ let expiry;
7165
+ async function refresh() {
7166
+ try {
7167
+ const result = await client3.action(api.billingAccess.branding, {
7168
+ apiKey
7169
+ });
7170
+ if (!active) return;
7171
+ clearTimeout(expiry);
7172
+ const expired = result.expiresAt !== null && result.expiresAt <= Date.now();
7173
+ setState({ key: apiKey, show: result.showBranding || expired });
7174
+ if (!result.showBranding && result.expiresAt !== null)
7175
+ expiry = setTimeout(
7176
+ () => setState({ key: apiKey, show: true }),
7177
+ Math.min(2147483647, Math.max(0, result.expiresAt - Date.now()))
7178
+ );
7179
+ } catch {
7180
+ if (active) setState({ key: apiKey, show: true });
7181
+ }
7182
+ }
7183
+ void refresh();
7184
+ const timer = setInterval(() => void refresh(), 6e4);
7185
+ return () => {
7186
+ active = false;
7187
+ clearInterval(timer);
7188
+ clearTimeout(expiry);
7189
+ };
7190
+ }, [apiKey]);
7191
+ return state.key === apiKey ? state.show : true;
7192
+ }
7193
+
6864
7194
  // ../../apps/web/src/components/support-widget/sdk/median-connection.tsx
6865
7195
  import { ConvexProvider, ConvexReactClient } from "convex/react";
6866
7196
  import { useState as useState21 } from "react";
@@ -6881,9 +7211,9 @@ function MedianConnection({
6881
7211
  }
6882
7212
 
6883
7213
  // ../../apps/web/src/components/support-widget/sdk/use-api-key-warning.ts
6884
- import { useEffect as useEffect19 } from "react";
7214
+ import { useEffect as useEffect20 } from "react";
6885
7215
  function useApiKeyShapeWarning(component, apiKey) {
6886
- useEffect19(() => {
7216
+ useEffect20(() => {
6887
7217
  if (apiKey === void 0 || hasPrefix(apiKey, API_KEY_PREFIX)) return;
6888
7218
  const shown = apiKey === "" ? "an empty string" : `"${apiKey.length > 16 ? `${apiKey.slice(0, 16)}\u2026` : apiKey}"`;
6889
7219
  console.error(
@@ -7210,16 +7540,16 @@ function play(sound = "chime", options) {
7210
7540
  }
7211
7541
 
7212
7542
  // ../../apps/web/src/components/support-widget/sdk/use-message-sound.ts
7213
- import { useEffect as useEffect20, useRef as useRef16 } from "react";
7543
+ import { useEffect as useEffect21, useRef as useRef18 } from "react";
7214
7544
  var ARRIVAL_VOLUME = 0.45;
7215
7545
  function useMessageSound({
7216
7546
  messages,
7217
7547
  conversationId = null,
7218
7548
  isLoading
7219
7549
  }) {
7220
- const heard = useRef16(null);
7221
- const watching = useRef16(null);
7222
- useEffect20(() => {
7550
+ const heard = useRef18(null);
7551
+ const watching = useRef18(null);
7552
+ useEffect21(() => {
7223
7553
  if (isLoading) {
7224
7554
  heard.current = null;
7225
7555
  return;
@@ -7243,7 +7573,7 @@ function useMessageSound({
7243
7573
  }
7244
7574
 
7245
7575
  // ../../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";
7576
+ import { useCallback as useCallback12, useEffect as useEffect22, useLayoutEffect as useLayoutEffect10, useRef as useRef19, useState as useState22 } from "react";
7247
7577
  var isDevelopment2 = typeof process !== "undefined" && process.env.NODE_ENV === "development";
7248
7578
  var STALE_AFTER_MS = 5 * 60 * 1e3;
7249
7579
  function isFresh(at2) {
@@ -7287,10 +7617,10 @@ function useSupportNavigation({
7287
7617
  isLoading,
7288
7618
  onNavigate
7289
7619
  }) {
7290
- const seen = useRef17(null);
7291
- const watching = useRef17(null);
7620
+ const seen = useRef19(null);
7621
+ const watching = useRef19(null);
7292
7622
  const [spotlight, setSpotlight] = useState22(null);
7293
- const drawn = useRef17(0);
7623
+ const drawn = useRef19(0);
7294
7624
  const [taken, setTaken] = useState22(null);
7295
7625
  const follow = useCallback12(
7296
7626
  (destination) => {
@@ -7314,7 +7644,7 @@ function useSupportNavigation({
7314
7644
  },
7315
7645
  [onNavigate]
7316
7646
  );
7317
- useLayoutEffect9(() => {
7647
+ useLayoutEffect10(() => {
7318
7648
  if (isLoading) return;
7319
7649
  const asked = ringing(messages, ask);
7320
7650
  if (seen.current === null || watching.current !== conversationId) {
@@ -7332,8 +7662,8 @@ function useSupportNavigation({
7332
7662
  }
7333
7663
  if (latest !== null) setSpotlight(latest);
7334
7664
  }, [messages, ask, conversationId, isLoading]);
7335
- const warned = useRef17(false);
7336
- useEffect21(() => {
7665
+ const warned = useRef19(false);
7666
+ useEffect22(() => {
7337
7667
  if (!isDevelopment2 || onNavigate !== void 0 || warned.current) return;
7338
7668
  const offered = messages.some((message) => message.navigateTo !== void 0) || ask?.navigateTo != null;
7339
7669
  if (!offered) return;
@@ -7343,11 +7673,11 @@ function useSupportNavigation({
7343
7673
  );
7344
7674
  }, [messages, ask, onNavigate]);
7345
7675
  const [, expire] = useState22(0);
7346
- useEffect21(() => {
7676
+ useEffect22(() => {
7347
7677
  if (ask === null || ask.navigateTo === null) return;
7348
7678
  const left = ask.at + STALE_AFTER_MS - Date.now();
7349
7679
  if (left <= 0) return;
7350
- const timer = setTimeout(() => expire((count) => count + 1), left);
7680
+ const timer = setTimeout(() => expire((count2) => count2 + 1), left);
7351
7681
  return () => clearTimeout(timer);
7352
7682
  }, [ask]);
7353
7683
  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;
@@ -7357,6 +7687,32 @@ function useSupportNavigation({
7357
7687
  // ../../apps/web/src/components/support-widget/sdk/use-teaser.ts
7358
7688
  import { useCallback as useCallback13, useState as useState23 } from "react";
7359
7689
 
7690
+ // ../../apps/web/src/components/support-widget/sdk/stamps.ts
7691
+ var inMemory3 = /* @__PURE__ */ new Map();
7692
+ function keyOf(name, apiKey) {
7693
+ return `median:${name}:${apiKey}`;
7694
+ }
7695
+ function readStamp(name, apiKey) {
7696
+ if (typeof window === "undefined") return 0;
7697
+ const key = keyOf(name, apiKey);
7698
+ try {
7699
+ const stored = window.localStorage.getItem(key);
7700
+ const at2 = stored === null ? Number.NaN : Number(stored);
7701
+ return Number.isFinite(at2) ? at2 : 0;
7702
+ } catch {
7703
+ return inMemory3.get(key) ?? 0;
7704
+ }
7705
+ }
7706
+ function writeStamp(name, apiKey, at2) {
7707
+ if (typeof window === "undefined") return;
7708
+ const key = keyOf(name, apiKey);
7709
+ inMemory3.set(key, at2);
7710
+ try {
7711
+ window.localStorage.setItem(key, String(at2));
7712
+ } catch {
7713
+ }
7714
+ }
7715
+
7360
7716
  // ../../apps/web/src/components/support-widget/sdk/teaser-state.ts
7361
7717
  var TEASER = "teaser";
7362
7718
  function readTeaserDismissedAt(apiKey) {
@@ -7381,8 +7737,38 @@ function useTeaser(apiKey, latestUnread) {
7381
7737
  return { teaser, dismiss };
7382
7738
  }
7383
7739
 
7740
+ // ../../apps/web/src/components/support-widget/sdk/use-unread-title.ts
7741
+ import { useEffect as useEffect23 } from "react";
7742
+ function badged(count2, title) {
7743
+ return `(${count2}) ${title}`.trim();
7744
+ }
7745
+ function useUnreadTitle(unreadCount) {
7746
+ useEffect23(() => {
7747
+ if (unreadCount === 0 || typeof document === "undefined") return;
7748
+ let plain = document.title;
7749
+ let shown = badged(unreadCount, plain);
7750
+ document.title = shown;
7751
+ const observer = new MutationObserver(() => {
7752
+ const current2 = document.title;
7753
+ if (current2 === shown) return;
7754
+ plain = current2;
7755
+ shown = badged(unreadCount, plain);
7756
+ document.title = shown;
7757
+ });
7758
+ observer.observe(document.head, {
7759
+ childList: true,
7760
+ subtree: true,
7761
+ characterData: true
7762
+ });
7763
+ return () => {
7764
+ observer.disconnect();
7765
+ if (document.title === shown) document.title = plain;
7766
+ };
7767
+ }, [unreadCount]);
7768
+ }
7769
+
7384
7770
  // ../../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";
7771
+ import { Fragment as Fragment31, jsx as jsx38, jsxs as jsxs22 } from "react/jsx-runtime";
7386
7772
  var PRESENCE_BEAT_MS = 45e3;
7387
7773
  function MedianSupport(props) {
7388
7774
  return /* @__PURE__ */ jsx38(MedianSupportPreview, { ...props });
@@ -7422,12 +7808,14 @@ function MedianLauncher({
7422
7808
  apiKey,
7423
7809
  user,
7424
7810
  telemetry,
7811
+ screenshot,
7425
7812
  diagnostics,
7426
7813
  appState,
7427
7814
  requireSignIn = false,
7428
7815
  onSignIn,
7429
7816
  requireEmail = true,
7430
7817
  messagePreview = true,
7818
+ titleCount = true,
7431
7819
  launcher,
7432
7820
  presentation,
7433
7821
  defaultOpen,
@@ -7443,6 +7831,7 @@ function MedianLauncher({
7443
7831
  apiKey
7444
7832
  );
7445
7833
  useRememberInstall(apiKey, user);
7834
+ const showBranding = useMedianBranding(apiKey);
7446
7835
  const [isOpen, setOpen] = useMedianOpenState({
7447
7836
  open,
7448
7837
  defaultOpen,
@@ -7462,31 +7851,28 @@ function MedianLauncher({
7462
7851
  rating,
7463
7852
  unreadCount,
7464
7853
  latestUnread,
7465
- markSeen,
7466
7854
  reportPresence,
7467
7855
  sendError,
7468
7856
  send,
7469
7857
  setTyping
7470
- } = useMedianThread(
7858
+ } = useMedianThread({
7471
7859
  apiKey,
7472
7860
  user,
7473
7861
  telemetry,
7862
+ screenshot,
7474
7863
  isLocked,
7475
7864
  diagnostics,
7476
7865
  appState,
7477
7866
  requireEmail,
7478
7867
  messagePreview
7479
- );
7868
+ });
7480
7869
  const { teaser, dismiss: dismissTeaser } = useTeaser(apiKey, latestUnread);
7481
7870
  const isSettling = isLoading || isResolvingIdentity;
7482
- useEffect22(() => {
7483
- if (isOpen) markSeen();
7484
- }, [isOpen, markSeen]);
7485
- useEffect22(() => {
7871
+ useEffect24(() => {
7486
7872
  if (!threadRendersMarkdown(messages)) return;
7487
7873
  return warmMarkdownWhenIdle();
7488
7874
  }, [messages]);
7489
- useEffect22(() => {
7875
+ useEffect24(() => {
7490
7876
  if (!isOpen) return;
7491
7877
  const beat = () => {
7492
7878
  if (document.visibilityState === "visible") reportPresence();
@@ -7519,16 +7905,22 @@ function MedianLauncher({
7519
7905
  conversationId,
7520
7906
  isLoading: isSettling || isLocked
7521
7907
  });
7908
+ useUnreadTitle(titleCount ? unreadCount : 0);
7909
+ useEffect24(() => {
7910
+ unreadStore.set(unreadCount);
7911
+ }, [unreadCount]);
7912
+ useEffect24(() => () => unreadStore.set(0), []);
7522
7913
  const resolvedPresence = presence ?? {
7523
7914
  name: "Support",
7524
7915
  status: "Connecting",
7525
7916
  team: []
7526
7917
  };
7527
- return /* @__PURE__ */ jsxs21(Fragment31, { children: [
7918
+ return /* @__PURE__ */ jsxs22(Fragment31, { children: [
7528
7919
  spotlight !== null && /* @__PURE__ */ jsx38(WidgetSpotlight, { selector: spotlight.selector }, spotlight.key),
7529
7920
  /* @__PURE__ */ jsx38(
7530
7921
  SupportLauncher,
7531
7922
  {
7923
+ showBadge: showBranding,
7532
7924
  presence: resolvedPresence,
7533
7925
  messages,
7534
7926
  onSend: send,
@@ -7644,7 +8036,12 @@ function mountMedianSupportModal(options) {
7644
8036
  }
7645
8037
 
7646
8038
  // ../../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";
8039
+ import {
8040
+ useCallback as useCallback15,
8041
+ useEffect as useEffect27,
8042
+ useRef as useRef22,
8043
+ useState as useState27
8044
+ } from "react";
7648
8045
 
7649
8046
  // ../backend/convex/shared/feedback.ts
7650
8047
  var NOTE_MAX_LENGTH = 2e3;
@@ -7654,17 +8051,17 @@ var MAX_REPORT_IMAGES = 6;
7654
8051
 
7655
8052
  // ../../apps/web/src/components/support-widget/feedback/feedback-panel.tsx
7656
8053
  import {
7657
- useEffect as useEffect24,
7658
- useRef as useRef19,
8054
+ useEffect as useEffect26,
8055
+ useRef as useRef21,
7659
8056
  useState as useState25
7660
8057
  } from "react";
7661
8058
 
7662
8059
  // ../../apps/web/src/components/support-widget/feedback/feedback-popover.tsx
7663
- import { useEffect as useEffect23, useRef as useRef18 } from "react";
8060
+ import { useEffect as useEffect25, useRef as useRef20 } from "react";
7664
8061
  import { createPortal as createPortal3 } from "react-dom";
7665
8062
 
7666
8063
  // ../../apps/web/src/components/support-widget/feedback/use-anchor.ts
7667
- import { useLayoutEffect as useLayoutEffect10, useState as useState24 } from "react";
8064
+ import { useLayoutEffect as useLayoutEffect11, useState as useState24 } from "react";
7668
8065
  var GAP2 = 8;
7669
8066
  var MARGIN2 = 16;
7670
8067
  function findAnchor(selector) {
@@ -7685,7 +8082,7 @@ function useAnchor({
7685
8082
  isOpen
7686
8083
  }) {
7687
8084
  const [side, setSide] = useState24("bottom");
7688
- useLayoutEffect10(() => {
8085
+ useLayoutEffect11(() => {
7689
8086
  if (!isOpen) return;
7690
8087
  let frame = 0;
7691
8088
  function place() {
@@ -7753,12 +8150,12 @@ function FeedbackPopover({
7753
8150
  width,
7754
8151
  children
7755
8152
  }) {
7756
- const panelRef = useRef18(null);
8153
+ const panelRef = useRef20(null);
7757
8154
  const side = useAnchor({ triggerRef, anchor, panelRef, isOpen });
7758
8155
  const isHydrated = useHydrated();
7759
- const opener = useRef18(null);
7760
- const wasOpen = useRef18(isOpen);
7761
- useEffect23(() => {
8156
+ const opener = useRef20(null);
8157
+ const wasOpen = useRef20(isOpen);
8158
+ useEffect25(() => {
7762
8159
  if (!isOpen) return;
7763
8160
  function handleKeyDown(event) {
7764
8161
  if (event.key === "Escape") onClose();
@@ -7766,7 +8163,7 @@ function FeedbackPopover({
7766
8163
  document.addEventListener("keydown", handleKeyDown);
7767
8164
  return () => document.removeEventListener("keydown", handleKeyDown);
7768
8165
  }, [isOpen, onClose]);
7769
- useEffect23(() => {
8166
+ useEffect25(() => {
7770
8167
  if (!isOpen) return;
7771
8168
  function handlePointerDown(event) {
7772
8169
  const target = event.target;
@@ -7778,7 +8175,7 @@ function FeedbackPopover({
7778
8175
  document.addEventListener("pointerdown", handlePointerDown);
7779
8176
  return () => document.removeEventListener("pointerdown", handlePointerDown);
7780
8177
  }, [isOpen, onClose, triggerRef]);
7781
- useEffect23(() => {
8178
+ useEffect25(() => {
7782
8179
  if (wasOpen.current === isOpen) return;
7783
8180
  wasOpen.current = isOpen;
7784
8181
  if (isOpen) {
@@ -7834,7 +8231,7 @@ function FeedbackPopover({
7834
8231
  }
7835
8232
 
7836
8233
  // ../../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";
8234
+ import { Fragment as Fragment32, jsx as jsx43, jsxs as jsxs23 } from "react/jsx-runtime";
7838
8235
  var THANKS_MS2 = 1600;
7839
8236
  function FeedbackPanel({
7840
8237
  onSubmit,
@@ -7843,21 +8240,21 @@ function FeedbackPanel({
7843
8240
  notice,
7844
8241
  showBadge = true,
7845
8242
  title = "Share feedback",
7846
- description = "The current page URL is included.",
8243
+ description = "Bugs, ideas, anything.",
7847
8244
  placeholder = "Tell us what could be better.",
7848
8245
  className
7849
8246
  }) {
7850
8247
  const [draft, setDraft] = useState25("");
7851
8248
  const [isSending, setSending] = useState25(false);
7852
8249
  const [isSent, setSent] = useState25(false);
7853
- const fileInputRef = useRef19(null);
8250
+ const fileInputRef = useRef21(null);
7854
8251
  const images = useAttachmentDrafts({
7855
8252
  accept: "image",
7856
8253
  max: MAX_REPORT_IMAGES
7857
8254
  });
7858
8255
  const target = useAttachmentTarget({ onFiles: images.add });
7859
8256
  const canSend = draft.trim().length > 0 && !images.hasBlocker && !isSending && !isSent;
7860
- useEffect24(() => {
8257
+ useEffect26(() => {
7861
8258
  if (!isSent) return;
7862
8259
  const timer = setTimeout(() => onSent?.(), THANKS_MS2);
7863
8260
  return () => clearTimeout(timer);
@@ -7885,7 +8282,7 @@ function FeedbackPanel({
7885
8282
  event.preventDefault();
7886
8283
  void send();
7887
8284
  }
7888
- return /* @__PURE__ */ jsxs22(
8285
+ return /* @__PURE__ */ jsxs23(
7889
8286
  "section",
7890
8287
  {
7891
8288
  "aria-label": title,
@@ -7893,12 +8290,12 @@ function FeedbackPanel({
7893
8290
  className: cn(
7894
8291
  // The widget's own radius scale, so the app's token can climb as the
7895
8292
  // 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]",
8293
+ `flex w-full flex-col gap-2 overflow-hidden ${WIDGET_CORNER} border bg-card p-3 text-card-foreground [--radius:0.625rem]`,
7897
8294
  className
7898
8295
  ),
7899
8296
  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: [
8297
+ /* @__PURE__ */ jsxs23("header", { className: "flex items-start gap-2", children: [
8298
+ /* @__PURE__ */ jsxs23("div", { className: "flex min-w-0 flex-1 flex-col", children: [
7902
8299
  /* @__PURE__ */ jsx43("h2", { className: "truncate text-sm font-semibold tracking-tight", children: title }),
7903
8300
  /* @__PURE__ */ jsx43("p", { className: "text-xs text-muted-foreground", children: description })
7904
8301
  ] }),
@@ -7915,8 +8312,8 @@ function FeedbackPanel({
7915
8312
  }
7916
8313
  )
7917
8314
  ] }),
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: [
8315
+ /* @__PURE__ */ jsxs23("div", { className: "grid", children: [
8316
+ /* @__PURE__ */ jsx43(Collapse, { isOpen: !isSent, children: /* @__PURE__ */ jsxs23("div", { className: "flex flex-col gap-1.5", children: [
7920
8317
  /* @__PURE__ */ jsx43(
7921
8318
  Textarea,
7922
8319
  {
@@ -7929,7 +8326,7 @@ function FeedbackPanel({
7929
8326
  "aria-label": title,
7930
8327
  ...{ [AUTOFOCUS_ATTRIBUTE]: "" },
7931
8328
  className: cn(
7932
- TEXT_FIELD_SHELL,
8329
+ WIDGET_TEXT_FIELD_SHELL,
7933
8330
  "max-h-40 min-h-18 px-2.5 py-2",
7934
8331
  // 16px on phones, in pixels: any focused field smaller than
7935
8332
  // that makes iOS zoom the host's whole page in.
@@ -7947,14 +8344,14 @@ function FeedbackPanel({
7947
8344
  )) }),
7948
8345
  notice && /* @__PURE__ */ jsx43("p", { className: "animate-in px-0.5 text-xs text-destructive fade-in duration-150", children: notice })
7949
8346
  ] }) }),
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: [
8347
+ /* @__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
8348
  /* @__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
8349
  "Thanks for your feedback."
7953
8350
  ] }) })
7954
8351
  ] }),
7955
- /* @__PURE__ */ jsxs22("div", { className: "flex min-h-8 items-center gap-2", children: [
8352
+ /* @__PURE__ */ jsxs23("div", { className: "flex min-h-8 items-center gap-2", children: [
7956
8353
  showBadge && /* @__PURE__ */ jsx43(PoweredByMedian, { className: "justify-start" }),
7957
- !isSent && /* @__PURE__ */ jsxs22(Fragment32, { children: [
8354
+ !isSent && /* @__PURE__ */ jsxs23(Fragment32, { children: [
7958
8355
  /* @__PURE__ */ jsx43(
7959
8356
  "input",
7960
8357
  {
@@ -7980,7 +8377,7 @@ function FeedbackPanel({
7980
8377
  children: /* @__PURE__ */ jsx43(ImageSquare, {})
7981
8378
  }
7982
8379
  ),
7983
- /* @__PURE__ */ jsxs22(
8380
+ /* @__PURE__ */ jsxs23(
7984
8381
  WidgetButton,
7985
8382
  {
7986
8383
  type: "button",
@@ -8017,7 +8414,7 @@ function FeedbackPanel({
8017
8414
  }
8018
8415
 
8019
8416
  // ../../apps/web/src/components/support-widget/sdk/feedback-control.ts
8020
- import { useSyncExternalStore as useSyncExternalStore12 } from "react";
8417
+ import { useSyncExternalStore as useSyncExternalStore13 } from "react";
8021
8418
  var store2 = createOpenStore("MedianFeedback");
8022
8419
  var medianFeedback = {
8023
8420
  /** What the panel is doing right now. */
@@ -8040,7 +8437,7 @@ var medianFeedback = {
8040
8437
  }
8041
8438
  };
8042
8439
  function useMedianFeedback() {
8043
- const isOpen = useSyncExternalStore12(
8440
+ const isOpen = useSyncExternalStore13(
8044
8441
  store2.subscribe,
8045
8442
  store2.readEffective,
8046
8443
  store2.readServerEffective
@@ -8060,8 +8457,7 @@ function useRegisterMedianFeedback() {
8060
8457
  }
8061
8458
 
8062
8459
  // ../../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";
8460
+ import { useCallback as useCallback14, useState as useState26, useSyncExternalStore as useSyncExternalStore14 } from "react";
8065
8461
  var REFUSALS = {
8066
8462
  off: "Feedback is currently unavailable. Try again later.",
8067
8463
  empty: "Enter your feedback before sending."
@@ -8070,16 +8466,17 @@ function useFeedbackNote({
8070
8466
  apiKey,
8071
8467
  user,
8072
8468
  telemetry = true,
8469
+ screenshot = true,
8073
8470
  diagnostics,
8074
8471
  appState
8075
8472
  }) {
8076
- const sessionId = useSyncExternalStore13(
8473
+ const sessionId = useSyncExternalStore14(
8077
8474
  subscribeSession,
8078
8475
  useCallback14(() => getOrCreateSessionId(apiKey), [apiKey]),
8079
8476
  () => null
8080
8477
  );
8081
- const submitNote = useMutation2(api.feedback.submit);
8082
- const getUploadUrl = useMutation2(api.widget.generateUploadUrl);
8478
+ const submitNote = useAuditedMutation(api.feedback.submitV2);
8479
+ const getUploadUrl = useAuditedMutation(api.widget.generateUploadUrlV2);
8083
8480
  const takeDiagnostics = useDiagnostics(diagnostics, appState);
8084
8481
  const [notice, setNotice] = useState26(null);
8085
8482
  const submit = useCallback14(
@@ -8096,9 +8493,10 @@ function useFeedbackNote({
8096
8493
  const page = telemetry ? collectPage() : void 0;
8097
8494
  const collected = takeDiagnostics();
8098
8495
  const { facts, claim } = splitIdentity(user ?? null);
8496
+ const shot = screenshot && images.length < MAX_REPORT_IMAGES ? await capturePage() : null;
8099
8497
  const attached = await uploadAttachments(
8100
8498
  () => getUploadUrl({ apiKey, sessionId }),
8101
- images
8499
+ shot === null ? images : [...images, shot]
8102
8500
  );
8103
8501
  const outcome = await submitNote({
8104
8502
  apiKey,
@@ -8114,13 +8512,18 @@ function useFeedbackNote({
8114
8512
  });
8115
8513
  if (outcome !== "taken") {
8116
8514
  explained = true;
8117
- setNotice(REFUSALS[outcome] ?? "Could not send your feedback. Try again.");
8515
+ setNotice(
8516
+ REFUSALS[outcome] ?? "Could not send your feedback. Try again."
8517
+ );
8118
8518
  throw new Error(outcome);
8119
8519
  }
8120
8520
  } catch (caught) {
8121
8521
  if (custom) restoreCustomData(custom);
8122
8522
  if (!explained) {
8123
- console.error("Sending feedback from the Median panel failed", caught);
8523
+ console.error(
8524
+ "Sending feedback from the Median panel failed",
8525
+ caught
8526
+ );
8124
8527
  setNotice(errorMessage(caught));
8125
8528
  }
8126
8529
  throw caught;
@@ -8131,6 +8534,7 @@ function useFeedbackNote({
8131
8534
  sessionId,
8132
8535
  user,
8133
8536
  telemetry,
8537
+ screenshot,
8134
8538
  takeDiagnostics,
8135
8539
  submitNote,
8136
8540
  getUploadUrl
@@ -8140,7 +8544,7 @@ function useFeedbackNote({
8140
8544
  }
8141
8545
 
8142
8546
  // ../../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";
8547
+ import { Fragment as Fragment33, jsx as jsx44, jsxs as jsxs24 } from "react/jsx-runtime";
8144
8548
  var DEFAULT_WIDTH2 = "20rem";
8145
8549
  function MedianFeedback(props) {
8146
8550
  return /* @__PURE__ */ jsx44(MedianErrorBoundary, { children: /* @__PURE__ */ jsx44(FeedbackIdentity, { ...props }) });
@@ -8156,14 +8560,7 @@ function FeedbackIdentity({
8156
8560
  const key = apiKey ?? resolved.apiKey;
8157
8561
  const person = user ?? resolved.user;
8158
8562
  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
- ) });
8563
+ return /* @__PURE__ */ jsx44(MedianConnection, { what: "feedback panel", children: /* @__PURE__ */ jsx44(Feedback, { ...rest, apiKey: key, ...person ? { user: person } : {} }) });
8167
8564
  }
8168
8565
  function Feedback({
8169
8566
  apiKey,
@@ -8171,6 +8568,7 @@ function Feedback({
8171
8568
  anchor,
8172
8569
  user,
8173
8570
  telemetry,
8571
+ screenshot,
8174
8572
  diagnostics,
8175
8573
  appState,
8176
8574
  open,
@@ -8183,7 +8581,7 @@ function Feedback({
8183
8581
  useRegisterMedianFeedback();
8184
8582
  useApiKeyShapeWarning("MedianFeedback", apiKey);
8185
8583
  useRememberInstall(apiKey, user);
8186
- const triggerRef = useRef20(null);
8584
+ const triggerRef = useRef22(null);
8187
8585
  const [isOpen, setOpen] = useMedianFeedbackOpenState({
8188
8586
  ...open !== void 0 ? { open } : {},
8189
8587
  ...onOpenChange ? { onOpenChange } : {}
@@ -8192,18 +8590,20 @@ function Feedback({
8192
8590
  apiKey,
8193
8591
  ...user ? { user } : {},
8194
8592
  ...telemetry !== void 0 ? { telemetry } : {},
8593
+ ...screenshot !== void 0 ? { screenshot } : {},
8195
8594
  ...diagnostics !== void 0 ? { diagnostics } : {},
8196
8595
  ...appState ? { appState } : {}
8197
8596
  });
8597
+ const showBranding = useMedianBranding(apiKey);
8198
8598
  const close = useCallback15(() => setOpen(false), [setOpen]);
8199
8599
  const [attempt, setAttempt] = useState27(0);
8200
- const hasSent = useRef20(false);
8201
- useEffect25(() => {
8600
+ const hasSent = useRef22(false);
8601
+ useEffect27(() => {
8202
8602
  if (!isOpen || !hasSent.current) return;
8203
8603
  hasSent.current = false;
8204
- setAttempt((count) => count + 1);
8604
+ setAttempt((count2) => count2 + 1);
8205
8605
  }, [isOpen]);
8206
- return /* @__PURE__ */ jsxs23(Fragment33, { children: [
8606
+ return /* @__PURE__ */ jsxs24(Fragment33, { children: [
8207
8607
  children !== void 0 && // An inline span rather than a clone of whatever was passed: the
8208
8608
  // host's trigger keeps its own props, its own ref and its own
8209
8609
  // styles, and the click is caught on the way past. `display:
@@ -8228,6 +8628,7 @@ function Feedback({
8228
8628
  children: /* @__PURE__ */ jsx44(
8229
8629
  FeedbackPanel,
8230
8630
  {
8631
+ showBadge: showBranding,
8231
8632
  onSubmit: note.submit,
8232
8633
  onClose: close,
8233
8634
  onSent: () => {
@@ -8260,23 +8661,23 @@ function mountMedianFeedback(options) {
8260
8661
  }
8261
8662
 
8262
8663
  // ../../apps/web/src/components/support-widget/sdk/median-contact-form.tsx
8263
- import { useEffect as useEffect27, useMemo as useMemo5 } from "react";
8664
+ import { useEffect as useEffect29, useMemo as useMemo6 } from "react";
8264
8665
 
8265
8666
  // ../../apps/web/src/components/support-widget/contact/contact-form.tsx
8266
8667
  import {
8267
- useEffect as useEffect26,
8668
+ useEffect as useEffect28,
8268
8669
  useId,
8269
- useMemo as useMemo3,
8270
- useRef as useRef22,
8670
+ useMemo as useMemo4,
8671
+ useRef as useRef24,
8271
8672
  useState as useState29
8272
8673
  } from "react";
8273
8674
 
8274
8675
  // ../../apps/web/src/components/support-widget/contact/contact-field.tsx
8275
8676
  import { useState as useState28 } from "react";
8276
- import { jsx as jsx46, jsxs as jsxs24 } from "react/jsx-runtime";
8677
+ import { jsx as jsx46, jsxs as jsxs25 } from "react/jsx-runtime";
8277
8678
  var LINE_MAX_LENGTH = 200;
8278
8679
  var SHELL = cn(
8279
- TEXT_FIELD_SHELL,
8680
+ WIDGET_TEXT_FIELD_SHELL,
8280
8681
  "w-full bg-card text-base max-sm:text-[16px] dark:bg-card"
8281
8682
  );
8282
8683
  var LINE = "h-11 rounded-full px-4";
@@ -8308,8 +8709,8 @@ function ContactField({
8308
8709
  event.preventDefault();
8309
8710
  onSubmit?.();
8310
8711
  }
8311
- return /* @__PURE__ */ jsxs24("div", { className: "flex flex-col", children: [
8312
- /* @__PURE__ */ jsxs24(
8712
+ return /* @__PURE__ */ jsxs25("div", { className: "flex flex-col", children: [
8713
+ /* @__PURE__ */ jsxs25(
8313
8714
  "label",
8314
8715
  {
8315
8716
  htmlFor: id,
@@ -8331,8 +8732,8 @@ function ContactField({
8331
8732
  onKeyDown: handleKeyDown,
8332
8733
  className: cn(SHELL, PARAGRAPH)
8333
8734
  }
8334
- ) : field.type === "select" ? /* @__PURE__ */ jsxs24("div", { className: "relative", children: [
8335
- /* @__PURE__ */ jsxs24(
8735
+ ) : field.type === "select" ? /* @__PURE__ */ jsxs25("div", { className: "relative", children: [
8736
+ /* @__PURE__ */ jsxs25(
8336
8737
  "select",
8337
8738
  {
8338
8739
  ...control,
@@ -8498,8 +8899,8 @@ function fieldComplaints(fields) {
8498
8899
  }
8499
8900
 
8500
8901
  // ../../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";
8902
+ import { useLayoutEffect as useLayoutEffect12, useRef as useRef23 } from "react";
8903
+ import { jsx as jsx47, jsxs as jsxs26 } from "react/jsx-runtime";
8503
8904
  var EASE_OUT = "cubic-bezier(0.22, 1, 0.36, 1)";
8504
8905
  var TRAVEL_MS = 550;
8505
8906
  var DRAW_LEAD_MS = 150;
@@ -8512,11 +8913,11 @@ function SentMark({
8512
8913
  from = null,
8513
8914
  className
8514
8915
  }) {
8515
- const frame = useRef21(null);
8516
- const square = useRef21(null);
8517
- const tick = useRef21(null);
8518
- const ring = useRef21(null);
8519
- useLayoutEffect11(() => {
8916
+ const frame = useRef23(null);
8917
+ const square = useRef23(null);
8918
+ const tick = useRef23(null);
8919
+ const ring = useRef23(null);
8920
+ useLayoutEffect12(() => {
8520
8921
  const rest = frame.current;
8521
8922
  const box = square.current;
8522
8923
  const path = tick.current;
@@ -8574,7 +8975,7 @@ function SentMark({
8574
8975
  for (const animation of animations) animation.cancel();
8575
8976
  };
8576
8977
  }, [from]);
8577
- return /* @__PURE__ */ jsxs25(
8978
+ return /* @__PURE__ */ jsxs26(
8578
8979
  "span",
8579
8980
  {
8580
8981
  ref: frame,
@@ -8618,7 +9019,7 @@ function SentMark({
8618
9019
  }
8619
9020
 
8620
9021
  // ../../apps/web/src/components/support-widget/contact/contact-form.tsx
8621
- import { jsx as jsx48, jsxs as jsxs26 } from "react/jsx-runtime";
9022
+ import { jsx as jsx48, jsxs as jsxs27 } from "react/jsx-runtime";
8622
9023
  var EMAIL_PATTERN2 = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
8623
9024
  var FORM_SWAP = "transition-[opacity,translate] duration-250 ease-out-quint motion-reduce:transition-none";
8624
9025
  var THANKS_SWAP = "transition-opacity ease-out motion-reduce:transition-none";
@@ -8661,10 +9062,10 @@ function ContactForm({
8661
9062
  showBadge = true,
8662
9063
  className
8663
9064
  }) {
8664
- const fields = useMemo3(() => resolveFields(hostFields), [hostFields]);
9065
+ const fields = useMemo4(() => resolveFields(hostFields), [hostFields]);
8665
9066
  const prefix = useId();
8666
- const formRef = useRef22(null);
8667
- const sendRef = useRef22(null);
9067
+ const formRef = useRef24(null);
9068
+ const sendRef = useRef24(null);
8668
9069
  const [values, setValues] = useState29(
8669
9070
  () => startingValues(fields, initialValues)
8670
9071
  );
@@ -8685,7 +9086,7 @@ function ContactForm({
8685
9086
  });
8686
9087
  }
8687
9088
  const [focusRequest, setFocusRequest] = useState29(0);
8688
- useEffect26(() => {
9089
+ useEffect28(() => {
8689
9090
  if (focusRequest === 0) return;
8690
9091
  formRef.current?.querySelector('[aria-invalid="true"]')?.focus({ preventScroll: false });
8691
9092
  }, [focusRequest]);
@@ -8707,7 +9108,7 @@ function ContactForm({
8707
9108
  }
8708
9109
  if (Object.keys(found).length > 0) {
8709
9110
  setErrors(found);
8710
- setFocusRequest((count) => count + 1);
9111
+ setFocusRequest((count2) => count2 + 1);
8711
9112
  return;
8712
9113
  }
8713
9114
  const message = composeMessage(fields, values);
@@ -8736,22 +9137,22 @@ function ContactForm({
8736
9137
  event.preventDefault();
8737
9138
  void send();
8738
9139
  }
8739
- return /* @__PURE__ */ jsxs26(
9140
+ return /* @__PURE__ */ jsxs27(
8740
9141
  "section",
8741
9142
  {
8742
9143
  "aria-label": title,
8743
9144
  "data-median-widget": "",
8744
9145
  className: cn(
8745
- "flex w-full flex-col overflow-hidden rounded-xl border bg-card p-5 text-card-foreground [--radius:0.625rem]",
9146
+ `flex w-full flex-col overflow-hidden ${WIDGET_CORNER} border bg-card p-5 text-card-foreground [--radius:0.625rem]`,
8746
9147
  className
8747
9148
  ),
8748
9149
  children: [
8749
- /* @__PURE__ */ jsxs26("header", { className: "flex flex-col gap-0.5 pb-4", children: [
9150
+ /* @__PURE__ */ jsxs27("header", { className: "flex flex-col gap-0.5 pb-4", children: [
8750
9151
  /* @__PURE__ */ jsx48("h2", { className: "text-lg font-semibold tracking-tight", children: title }),
8751
9152
  /* @__PURE__ */ jsx48("p", { className: "text-sm text-muted-foreground", children: description })
8752
9153
  ] }),
8753
- /* @__PURE__ */ jsxs26("div", { className: "grid", children: [
8754
- /* @__PURE__ */ jsxs26(
9154
+ /* @__PURE__ */ jsxs27("div", { className: "grid", children: [
9155
+ /* @__PURE__ */ jsxs27(
8755
9156
  "form",
8756
9157
  {
8757
9158
  ref: formRef,
@@ -8778,9 +9179,9 @@ function ContactForm({
8778
9179
  field.name
8779
9180
  )),
8780
9181
  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: [
9182
+ /* @__PURE__ */ jsxs27("div", { className: "flex min-h-10 flex-wrap items-center gap-3 pt-1", children: [
8782
9183
  showBadge && /* @__PURE__ */ jsx48(PoweredByMedian, { className: "justify-start" }),
8783
- /* @__PURE__ */ jsxs26(
9184
+ /* @__PURE__ */ jsxs27(
8784
9185
  WidgetButton,
8785
9186
  {
8786
9187
  ref: sendRef,
@@ -8832,15 +9233,15 @@ function ContactForm({
8832
9233
  THANKS_SWAP,
8833
9234
  isSent ? "duration-0" : "pointer-events-none opacity-0 duration-200"
8834
9235
  ),
8835
- children: sent !== null && /* @__PURE__ */ jsxs26("div", { className: "contents", children: [
9236
+ children: sent !== null && /* @__PURE__ */ jsxs27("div", { className: "contents", children: [
8836
9237
  /* @__PURE__ */ jsx48(SentMark, { from: sent.from }),
8837
- /* @__PURE__ */ jsxs26(
9238
+ /* @__PURE__ */ jsxs27(
8838
9239
  "div",
8839
9240
  {
8840
9241
  className: cn("flex max-w-72 flex-col gap-1", ARRIVES2, "delay-[400ms]"),
8841
9242
  children: [
8842
9243
  /* @__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: [
9244
+ /* @__PURE__ */ jsxs27("p", { className: "text-base text-balance text-muted-foreground", children: [
8844
9245
  "Replies go to ",
8845
9246
  sent.email,
8846
9247
  "."
@@ -8848,7 +9249,7 @@ function ContactForm({
8848
9249
  ]
8849
9250
  }
8850
9251
  ),
8851
- /* @__PURE__ */ jsxs26(
9252
+ /* @__PURE__ */ jsxs27(
8852
9253
  "div",
8853
9254
  {
8854
9255
  className: cn(
@@ -8882,10 +9283,10 @@ function ContactForm({
8882
9283
  }
8883
9284
 
8884
9285
  // ../../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";
9286
+ import { useCallback as useCallback16, useMemo as useMemo5, useState as useState30, useSyncExternalStore as useSyncExternalStore15 } from "react";
8886
9287
 
8887
9288
  // ../../apps/web/src/components/support-widget/sdk/contact.ts
8888
- import { ConvexHttpClient as ConvexHttpClient2 } from "convex/browser";
9289
+ import { ConvexHttpClient as ConvexHttpClient3 } from "convex/browser";
8889
9290
  var client2 = null;
8890
9291
  async function deliverContact(options, message) {
8891
9292
  const { apiKey, sessionId, user, telemetry = true } = options;
@@ -8898,11 +9299,11 @@ async function deliverContact(options, message) {
8898
9299
  "The Median SDK was built without a deployment URL. Set NEXT_PUBLIC_CONVEX_URL."
8899
9300
  );
8900
9301
  }
8901
- client2 ??= new ConvexHttpClient2(MEDIAN_DEPLOYMENT_URL);
9302
+ client2 ??= new ConvexHttpClient3(MEDIAN_DEPLOYMENT_URL);
8902
9303
  const context = telemetry ? collectContext() : void 0;
8903
9304
  const page = telemetry ? collectPage() : void 0;
8904
9305
  const { facts, claim } = splitIdentity(user ?? null);
8905
- await client2.mutation(api.widget.send, {
9306
+ await client2.mutation(api.widget.sendV2, {
8906
9307
  apiKey,
8907
9308
  sessionId,
8908
9309
  body: composeBody(message),
@@ -8914,7 +9315,7 @@ async function deliverContact(options, message) {
8914
9315
  ...page ? { page } : {},
8915
9316
  ...options.diagnostics ? { diagnostics: options.diagnostics } : {},
8916
9317
  ...custom ? { customData: custom } : {}
8917
- });
9318
+ }).then(unwrapMutationResult);
8918
9319
  } catch (caught) {
8919
9320
  if (custom) restoreCustomData(custom);
8920
9321
  console.error("Median: sending a contact message failed", caught);
@@ -8938,12 +9339,16 @@ function useMedianContact(options) {
8938
9339
  const resolved = useMedianIdentity(identity);
8939
9340
  useMissingCredentials("useMedianContact", apiKey, identity);
8940
9341
  const key = apiKey ?? resolved.apiKey;
9342
+ const showBranding = useMedianBranding(key);
8941
9343
  useApiKeyShapeWarning("useMedianContact", key);
8942
9344
  const person = user ?? resolved.user;
8943
9345
  useRememberInstall(key, person);
8944
- const sessionId = useSyncExternalStore14(
9346
+ const sessionId = useSyncExternalStore15(
8945
9347
  subscribeSession,
8946
- useCallback16(() => key === void 0 ? null : getOrCreateSessionId(key), [key]),
9348
+ useCallback16(
9349
+ () => key === void 0 ? null : getOrCreateSessionId(key),
9350
+ [key]
9351
+ ),
8947
9352
  () => null
8948
9353
  );
8949
9354
  const takeDiagnostics = useDiagnostics(diagnostics, appState);
@@ -8980,13 +9385,14 @@ function useMedianContact(options) {
8980
9385
  },
8981
9386
  [key, sessionId, person, telemetry, takeDiagnostics]
8982
9387
  );
8983
- const known = useMemo4(() => {
9388
+ const known = useMemo5(() => {
8984
9389
  const email = person?.email || knownEmail || void 0;
8985
9390
  if (person === void 0 && email === void 0) return void 0;
8986
9391
  return { ...person, ...email ? { email } : {} };
8987
9392
  }, [person, knownEmail]);
8988
9393
  return {
8989
9394
  send,
9395
+ showBranding,
8990
9396
  isSending,
8991
9397
  error,
8992
9398
  isReady: key !== void 0 && sessionId !== null,
@@ -9001,10 +9407,12 @@ function MedianContactForm(props) {
9001
9407
  }
9002
9408
  function useFieldComplaints(fields) {
9003
9409
  const key = fields === void 0 ? null : JSON.stringify(fields);
9004
- useEffect27(() => {
9410
+ useEffect29(() => {
9005
9411
  if (key === null) return;
9006
9412
  for (const complaint of fieldComplaints(JSON.parse(key))) {
9007
- console.error(`Median: in the fields passed to <MedianContactForm>, ${complaint}`);
9413
+ console.error(
9414
+ `Median: in the fields passed to <MedianContactForm>, ${complaint}`
9415
+ );
9008
9416
  }
9009
9417
  }, [key]);
9010
9418
  }
@@ -9019,7 +9427,7 @@ function ConnectedContactForm({
9019
9427
  useFieldComplaints(fields);
9020
9428
  const knownEmail = contact.user?.email || void 0;
9021
9429
  const knownName = contact.user?.name || void 0;
9022
- const initialValues = useMemo5(
9430
+ const initialValues = useMemo6(
9023
9431
  () => ({
9024
9432
  ...knownEmail ? { email: knownEmail } : {},
9025
9433
  ...knownName ? { name: knownName } : {}
@@ -9029,6 +9437,7 @@ function ConnectedContactForm({
9029
9437
  return /* @__PURE__ */ jsx49(
9030
9438
  ContactForm,
9031
9439
  {
9440
+ showBadge: contact.showBranding,
9032
9441
  fields,
9033
9442
  onSubmit: async (message) => {
9034
9443
  await contact.send(message);