@sesionario/blocks 1.4.1 → 1.6.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.cjs CHANGED
@@ -1987,6 +1987,8 @@ function AvatarEditorDialog({
1987
1987
  dialogTitle = "Edit Avatar",
1988
1988
  acceptText = "Accept",
1989
1989
  cancelText = "Cancel",
1990
+ savingText = "Saving...",
1991
+ savedText = "Saved!",
1990
1992
  successMessage = "Avatar saved successfully!",
1991
1993
  errorMessage = "Failed to save avatar. Please try again.",
1992
1994
  className
@@ -2148,10 +2150,10 @@ function AvatarEditorDialog({
2148
2150
  )
2149
2151
  }
2150
2152
  ),
2151
- "Saving..."
2153
+ savingText
2152
2154
  ] }) : feedback?.type === "success" ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
2153
2155
  /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Check, { className: cn("mr-2", isMobile ? "w-5 h-5" : "w-4 h-4") }),
2154
- "Saved!"
2156
+ savedText
2155
2157
  ] }) : acceptText
2156
2158
  }
2157
2159
  )
@@ -5069,6 +5071,635 @@ function ChatMessageList({
5069
5071
  /* @__PURE__ */ jsxRuntime.jsx(MessageScrollerButton, {})
5070
5072
  ] }) });
5071
5073
  }
5074
+ function useMediaQuery2(query) {
5075
+ const [matches, setMatches] = React6.useState(false);
5076
+ React6.useEffect(() => {
5077
+ if (typeof window === "undefined" || !window.matchMedia) return;
5078
+ const mql = window.matchMedia(query);
5079
+ const onChange = () => setMatches(mql.matches);
5080
+ onChange();
5081
+ mql.addEventListener("change", onChange);
5082
+ return () => mql.removeEventListener("change", onChange);
5083
+ }, [query]);
5084
+ return matches;
5085
+ }
5086
+ var RECONNECT_DELAY_MS = 2e3;
5087
+ function useSupportConversation({
5088
+ client,
5089
+ getWsUrl,
5090
+ conversationId: conversationIdProp,
5091
+ open = null,
5092
+ enabled = true,
5093
+ onConversationReady,
5094
+ onIncomingMessage
5095
+ }) {
5096
+ const [conversationId, setConversationId] = React6.useState(
5097
+ conversationIdProp
5098
+ );
5099
+ const [messages, setMessages] = React6.useState([]);
5100
+ const [loading, setLoading] = React6.useState(false);
5101
+ const [sending, setSending] = React6.useState(false);
5102
+ const [error, setError] = React6.useState(null);
5103
+ const socketRef = React6.useRef(null);
5104
+ const reconnectRef = React6.useRef(null);
5105
+ const closedByUser = React6.useRef(false);
5106
+ const onReadyRef = React6.useRef(onConversationReady);
5107
+ const onIncomingRef = React6.useRef(onIncomingMessage);
5108
+ onReadyRef.current = onConversationReady;
5109
+ onIncomingRef.current = onIncomingMessage;
5110
+ const load = React6.useCallback(async () => {
5111
+ if (!enabled) return;
5112
+ setLoading(true);
5113
+ setError(null);
5114
+ try {
5115
+ let id = conversationIdProp;
5116
+ if (!id && open) {
5117
+ const conv = await client.findOrCreateConversation(open);
5118
+ id = conv.id;
5119
+ }
5120
+ if (!id) {
5121
+ setConversationId(null);
5122
+ setMessages([]);
5123
+ return;
5124
+ }
5125
+ setConversationId(id);
5126
+ onReadyRef.current?.(id);
5127
+ const msgs = await client.listMessages(id);
5128
+ setMessages(msgs);
5129
+ await client.markRead(id);
5130
+ } catch (err) {
5131
+ setError(err instanceof Error ? err.message : "Chat load failed");
5132
+ } finally {
5133
+ setLoading(false);
5134
+ }
5135
+ }, [client, conversationIdProp, enabled, open]);
5136
+ React6.useEffect(() => {
5137
+ void load();
5138
+ }, [load]);
5139
+ React6.useEffect(() => {
5140
+ closedByUser.current = false;
5141
+ if (!enabled || !conversationId) return;
5142
+ let cancelled = false;
5143
+ const connect = async () => {
5144
+ const url = await getWsUrl(conversationId);
5145
+ if (!url || cancelled || closedByUser.current) return;
5146
+ const socket = new WebSocket(url);
5147
+ socketRef.current = socket;
5148
+ socket.onmessage = (event) => {
5149
+ let data;
5150
+ try {
5151
+ data = JSON.parse(String(event.data));
5152
+ } catch {
5153
+ return;
5154
+ }
5155
+ if (data.type === "message") {
5156
+ setMessages((prev) => {
5157
+ if (prev.some((m) => m.id === data.id)) return prev;
5158
+ return [...prev, data];
5159
+ });
5160
+ onIncomingRef.current?.(data);
5161
+ } else if (data.type === "deleted") {
5162
+ setMessages(
5163
+ (prev) => prev.map(
5164
+ (m) => m.id === data.id ? { ...m, deleted: true, body: "" } : m
5165
+ )
5166
+ );
5167
+ }
5168
+ };
5169
+ socket.onclose = () => {
5170
+ if (cancelled || closedByUser.current) return;
5171
+ reconnectRef.current = setTimeout(() => {
5172
+ void connect();
5173
+ }, RECONNECT_DELAY_MS);
5174
+ };
5175
+ };
5176
+ void connect();
5177
+ return () => {
5178
+ cancelled = true;
5179
+ closedByUser.current = true;
5180
+ if (reconnectRef.current) clearTimeout(reconnectRef.current);
5181
+ socketRef.current?.close();
5182
+ socketRef.current = null;
5183
+ };
5184
+ }, [conversationId, enabled, getWsUrl]);
5185
+ const send = React6.useCallback(
5186
+ async (text) => {
5187
+ const trimmed = text.trim();
5188
+ if (!conversationId || !trimmed || sending) return;
5189
+ setSending(true);
5190
+ setError(null);
5191
+ try {
5192
+ const msg = await client.sendMessage(conversationId, trimmed);
5193
+ setMessages((prev) => {
5194
+ if (prev.some((m) => m.id === msg.id)) return prev;
5195
+ return [...prev, msg];
5196
+ });
5197
+ await client.markRead(conversationId);
5198
+ } catch (err) {
5199
+ setError(err instanceof Error ? err.message : "Send failed");
5200
+ throw err;
5201
+ } finally {
5202
+ setSending(false);
5203
+ }
5204
+ },
5205
+ [client, conversationId, sending]
5206
+ );
5207
+ return {
5208
+ conversationId,
5209
+ messages,
5210
+ loading,
5211
+ sending,
5212
+ error,
5213
+ send,
5214
+ reload: load
5215
+ };
5216
+ }
5217
+ var SupportMessengerContext = React6.createContext(null);
5218
+ function useSupportMessenger() {
5219
+ const ctx = React6.useContext(SupportMessengerContext);
5220
+ if (!ctx) {
5221
+ throw new Error(
5222
+ "useSupportMessenger must be used within SupportMessengerProvider"
5223
+ );
5224
+ }
5225
+ return ctx;
5226
+ }
5227
+ function useSupportMessengerOptional() {
5228
+ return React6.useContext(SupportMessengerContext);
5229
+ }
5230
+ function ThreadPanel({
5231
+ thread,
5232
+ client,
5233
+ getWsUrl,
5234
+ currentUserId,
5235
+ labels,
5236
+ emojiLocale,
5237
+ onClose,
5238
+ onBack,
5239
+ showBack,
5240
+ onConversationReady,
5241
+ className
5242
+ }) {
5243
+ const [draft, setDraft] = React6.useState("");
5244
+ const { messages, loading, sending, error, send } = useSupportConversation({
5245
+ client,
5246
+ getWsUrl,
5247
+ conversationId: thread.conversationId ?? null,
5248
+ open: thread.open ?? null,
5249
+ onConversationReady
5250
+ });
5251
+ async function onSubmit(e) {
5252
+ e.preventDefault();
5253
+ const text = draft.trim();
5254
+ if (!text) return;
5255
+ setDraft("");
5256
+ try {
5257
+ await send(text);
5258
+ } catch {
5259
+ setDraft(text);
5260
+ }
5261
+ }
5262
+ return /* @__PURE__ */ jsxRuntime.jsxs(
5263
+ "div",
5264
+ {
5265
+ className: cn(
5266
+ "flex h-full min-h-0 flex-col overflow-hidden rounded-xl border bg-card shadow-lg",
5267
+ className
5268
+ ),
5269
+ children: [
5270
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1 border-b px-2 py-2", children: [
5271
+ showBack && onBack ? /* @__PURE__ */ jsxRuntime.jsx(
5272
+ Button,
5273
+ {
5274
+ type: "button",
5275
+ variant: "ghost",
5276
+ size: "icon-sm",
5277
+ onClick: onBack,
5278
+ "aria-label": labels.back,
5279
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronLeft, { className: "size-4" })
5280
+ }
5281
+ ) : null,
5282
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "min-w-0 flex-1 px-1", children: [
5283
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "truncate text-sm font-semibold", children: thread.title }),
5284
+ thread.subtitle ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: "truncate text-xs text-muted-foreground", children: thread.subtitle }) : null
5285
+ ] }),
5286
+ /* @__PURE__ */ jsxRuntime.jsx(
5287
+ Button,
5288
+ {
5289
+ type: "button",
5290
+ variant: "ghost",
5291
+ size: "icon-sm",
5292
+ onClick: onClose,
5293
+ "aria-label": labels.close,
5294
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.X, { className: "size-4" })
5295
+ }
5296
+ )
5297
+ ] }),
5298
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "min-h-0 flex-1", children: loading ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex h-full items-center justify-center p-6", children: /* @__PURE__ */ jsxRuntime.jsx(LoadingState, { label: labels.loading, className: "py-0" }) }) : error ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: "p-4 text-sm text-destructive", role: "alert", children: error }) : /* @__PURE__ */ jsxRuntime.jsx(
5299
+ ChatMessageList,
5300
+ {
5301
+ messages,
5302
+ currentUserId,
5303
+ emptyDescription: labels.emptyThread
5304
+ }
5305
+ ) }),
5306
+ /* @__PURE__ */ jsxRuntime.jsxs("form", { className: "flex gap-2 border-t p-3", onSubmit, children: [
5307
+ /* @__PURE__ */ jsxRuntime.jsx(
5308
+ EmojiTextarea,
5309
+ {
5310
+ locale: emojiLocale,
5311
+ value: draft,
5312
+ onChange: (e) => setDraft(e.target.value.slice(0, 2e3)),
5313
+ placeholder: labels.placeholder,
5314
+ "aria-label": labels.placeholder,
5315
+ disabled: loading || sending,
5316
+ maxLength: 2e3,
5317
+ rows: 2,
5318
+ className: "min-h-10 flex-1"
5319
+ }
5320
+ ),
5321
+ /* @__PURE__ */ jsxRuntime.jsx(Button, { type: "submit", disabled: !draft.trim() || sending || loading, children: labels.send })
5322
+ ] })
5323
+ ]
5324
+ }
5325
+ );
5326
+ }
5327
+ function ListPanel({
5328
+ items,
5329
+ labels,
5330
+ tabs,
5331
+ activeTab,
5332
+ onTabChange,
5333
+ onSelect,
5334
+ onClose,
5335
+ searchThreshold,
5336
+ className
5337
+ }) {
5338
+ const [query, setQuery] = React6.useState("");
5339
+ const showSearch = items.length >= searchThreshold;
5340
+ const filtered = React6.useMemo(() => {
5341
+ const q = query.trim().toLowerCase();
5342
+ if (!q) return items;
5343
+ return items.filter(
5344
+ (item) => item.title.toLowerCase().includes(q) || (item.subtitle?.toLowerCase().includes(q) ?? false)
5345
+ );
5346
+ }, [items, query]);
5347
+ return /* @__PURE__ */ jsxRuntime.jsxs(
5348
+ "div",
5349
+ {
5350
+ className: cn(
5351
+ "flex h-full min-h-0 flex-col overflow-hidden rounded-xl border bg-card shadow-lg",
5352
+ className
5353
+ ),
5354
+ children: [
5355
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between border-b px-3 py-2", children: [
5356
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm font-semibold", children: labels.listTitle }),
5357
+ /* @__PURE__ */ jsxRuntime.jsx(
5358
+ Button,
5359
+ {
5360
+ type: "button",
5361
+ variant: "ghost",
5362
+ size: "icon-sm",
5363
+ onClick: onClose,
5364
+ "aria-label": labels.close,
5365
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.X, { className: "size-4" })
5366
+ }
5367
+ )
5368
+ ] }),
5369
+ tabs && tabs.length > 0 ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex gap-1 border-b p-2", children: tabs.map((tab) => /* @__PURE__ */ jsxRuntime.jsx(
5370
+ Button,
5371
+ {
5372
+ type: "button",
5373
+ size: "sm",
5374
+ variant: activeTab === tab.id ? "default" : "outline",
5375
+ onClick: () => onTabChange?.(tab.id),
5376
+ children: tab.label
5377
+ },
5378
+ tab.id
5379
+ )) }) : null,
5380
+ showSearch ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "border-b p-2", children: /* @__PURE__ */ jsxRuntime.jsxs("label", { className: "relative block", children: [
5381
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Search, { className: "pointer-events-none absolute top-1/2 left-2.5 size-3.5 -translate-y-1/2 text-muted-foreground" }),
5382
+ /* @__PURE__ */ jsxRuntime.jsx(
5383
+ "input",
5384
+ {
5385
+ value: query,
5386
+ onChange: (e) => setQuery(e.target.value),
5387
+ placeholder: labels.searchPlaceholder ?? "Buscar\u2026",
5388
+ className: "h-8 w-full rounded-md border bg-transparent pr-2 pl-8 text-sm outline-none focus-visible:ring-2 focus-visible:ring-ring"
5389
+ }
5390
+ )
5391
+ ] }) }) : null,
5392
+ /* @__PURE__ */ jsxRuntime.jsx("ul", { className: "min-h-0 flex-1 overflow-y-auto p-1", children: filtered.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("li", { className: "px-3 py-8 text-center text-sm text-muted-foreground", children: labels.emptyList }) : filtered.map((item) => /* @__PURE__ */ jsxRuntime.jsx("li", { children: /* @__PURE__ */ jsxRuntime.jsxs(
5393
+ "button",
5394
+ {
5395
+ type: "button",
5396
+ className: "flex w-full items-center gap-3 rounded-lg px-2 py-2.5 text-left transition-colors hover:bg-muted/80",
5397
+ onClick: () => onSelect(item),
5398
+ children: [
5399
+ /* @__PURE__ */ jsxRuntime.jsx(
5400
+ "span",
5401
+ {
5402
+ className: "flex size-9 shrink-0 items-center justify-center overflow-hidden rounded-full text-xs font-semibold text-white",
5403
+ style: item.avatarUrl ? void 0 : avatarFallbackStyle(item.title),
5404
+ children: item.avatarUrl ? /* @__PURE__ */ jsxRuntime.jsx(
5405
+ "img",
5406
+ {
5407
+ src: item.avatarUrl,
5408
+ alt: "",
5409
+ className: "size-full object-cover"
5410
+ }
5411
+ ) : getInitials(item.title)
5412
+ }
5413
+ ),
5414
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "min-w-0 flex-1", children: [
5415
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "block truncate text-sm font-medium", children: item.title }),
5416
+ item.subtitle ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "block truncate text-xs text-muted-foreground", children: item.subtitle }) : null
5417
+ ] }),
5418
+ (item.unreadCount ?? 0) > 0 ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "rounded-full bg-primary px-2 py-0.5 text-[10px] font-semibold text-primary-foreground", children: (item.unreadCount ?? 0) > 99 ? "99+" : item.unreadCount }) : null
5419
+ ]
5420
+ }
5421
+ ) }, item.key)) })
5422
+ ]
5423
+ }
5424
+ );
5425
+ }
5426
+ function SupportMessengerProvider({
5427
+ children,
5428
+ client,
5429
+ getWsUrl,
5430
+ currentUserId,
5431
+ items,
5432
+ labels,
5433
+ enabled = true,
5434
+ forceMinimized = false,
5435
+ emojiLocale = "es",
5436
+ tabs,
5437
+ activeTab,
5438
+ onTabChange,
5439
+ onRefreshItems,
5440
+ searchThreshold = 8,
5441
+ className
5442
+ }) {
5443
+ const isMobile = useMediaQuery2("(max-width: 767px)");
5444
+ const [listOpen, setListOpen] = React6.useState(false);
5445
+ const [threads, setThreads] = React6.useState([]);
5446
+ const [mobileThreadKey, setMobileThreadKey] = React6.useState(null);
5447
+ const totalUnread = React6.useMemo(
5448
+ () => items.reduce((sum, item) => sum + (item.unreadCount ?? 0), 0),
5449
+ [items]
5450
+ );
5451
+ React6.useEffect(() => {
5452
+ if (!forceMinimized) return;
5453
+ setListOpen(false);
5454
+ setThreads([]);
5455
+ setMobileThreadKey(null);
5456
+ }, [forceMinimized]);
5457
+ const openList = React6.useCallback(() => {
5458
+ setListOpen(true);
5459
+ void onRefreshItems?.();
5460
+ }, [onRefreshItems]);
5461
+ const closeList = React6.useCallback(() => setListOpen(false), []);
5462
+ const toggleList = React6.useCallback(() => {
5463
+ setListOpen((prev) => {
5464
+ const next = !prev;
5465
+ if (next) void onRefreshItems?.();
5466
+ return next;
5467
+ });
5468
+ }, [onRefreshItems]);
5469
+ const openThread = React6.useCallback((req) => {
5470
+ setListOpen(false);
5471
+ setThreads((prev) => {
5472
+ const existing = prev.find((t) => t.key === req.key);
5473
+ if (existing) {
5474
+ return [
5475
+ ...prev.filter((t) => t.key !== req.key),
5476
+ { ...existing, ...req }
5477
+ ];
5478
+ }
5479
+ const next = [...prev, req];
5480
+ return next.slice(-2);
5481
+ });
5482
+ setMobileThreadKey(req.key);
5483
+ }, []);
5484
+ const closeThread = React6.useCallback((key) => {
5485
+ setThreads((prev) => prev.filter((t) => t.key !== key));
5486
+ setMobileThreadKey((prev) => prev === key ? null : prev);
5487
+ }, []);
5488
+ const closeAll = React6.useCallback(() => {
5489
+ setListOpen(false);
5490
+ setThreads([]);
5491
+ setMobileThreadKey(null);
5492
+ }, []);
5493
+ const ctx = React6.useMemo(
5494
+ () => ({
5495
+ listOpen,
5496
+ openList,
5497
+ closeList,
5498
+ toggleList,
5499
+ openThread,
5500
+ closeThread,
5501
+ closeAll,
5502
+ activeThreadKey: mobileThreadKey ?? threads[threads.length - 1]?.key ?? null,
5503
+ totalUnread
5504
+ }),
5505
+ [
5506
+ listOpen,
5507
+ openList,
5508
+ closeList,
5509
+ toggleList,
5510
+ openThread,
5511
+ closeThread,
5512
+ closeAll,
5513
+ mobileThreadKey,
5514
+ threads,
5515
+ totalUnread
5516
+ ]
5517
+ );
5518
+ const handleSelectItem = React6.useCallback(
5519
+ (item) => {
5520
+ openThread({
5521
+ key: item.key,
5522
+ title: item.title,
5523
+ subtitle: item.subtitle,
5524
+ conversationId: item.conversationId,
5525
+ open: item.open
5526
+ });
5527
+ },
5528
+ [openThread]
5529
+ );
5530
+ const activeMobileThread = threads.find((t) => t.key === mobileThreadKey);
5531
+ return /* @__PURE__ */ jsxRuntime.jsxs(SupportMessengerContext.Provider, { value: ctx, children: [
5532
+ children,
5533
+ enabled ? /* @__PURE__ */ jsxRuntime.jsxs(
5534
+ "div",
5535
+ {
5536
+ className: cn(
5537
+ "pointer-events-none fixed right-4 bottom-4 z-50 flex flex-col items-end gap-3",
5538
+ className
5539
+ ),
5540
+ children: [
5541
+ !isMobile ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "pointer-events-auto flex items-end gap-3", children: [
5542
+ threads.map((thread) => /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-[28rem] w-[22rem]", children: /* @__PURE__ */ jsxRuntime.jsx(
5543
+ ThreadPanel,
5544
+ {
5545
+ thread,
5546
+ client,
5547
+ getWsUrl,
5548
+ currentUserId,
5549
+ labels,
5550
+ emojiLocale,
5551
+ onClose: () => closeThread(thread.key),
5552
+ onConversationReady: () => void onRefreshItems?.()
5553
+ }
5554
+ ) }, thread.key)),
5555
+ listOpen ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-[28rem] w-[20rem]", children: /* @__PURE__ */ jsxRuntime.jsx(
5556
+ ListPanel,
5557
+ {
5558
+ items,
5559
+ labels,
5560
+ tabs,
5561
+ activeTab,
5562
+ onTabChange,
5563
+ onSelect: handleSelectItem,
5564
+ onClose: closeList,
5565
+ searchThreshold
5566
+ }
5567
+ ) }) : null
5568
+ ] }) : null,
5569
+ isMobile ? /* @__PURE__ */ jsxRuntime.jsx(
5570
+ Drawer,
5571
+ {
5572
+ open: listOpen || Boolean(activeMobileThread),
5573
+ onOpenChange: (open) => {
5574
+ if (!open) closeAll();
5575
+ },
5576
+ children: /* @__PURE__ */ jsxRuntime.jsx(DrawerContent, { className: "pointer-events-auto mx-auto flex h-[85vh] max-w-lg flex-col", children: activeMobileThread ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
5577
+ /* @__PURE__ */ jsxRuntime.jsx(DrawerHeader, { className: "sr-only", children: /* @__PURE__ */ jsxRuntime.jsx(DrawerTitle, { children: activeMobileThread.title }) }),
5578
+ /* @__PURE__ */ jsxRuntime.jsx(
5579
+ ThreadPanel,
5580
+ {
5581
+ thread: activeMobileThread,
5582
+ client,
5583
+ getWsUrl,
5584
+ currentUserId,
5585
+ labels,
5586
+ emojiLocale,
5587
+ showBack: true,
5588
+ onBack: () => {
5589
+ closeThread(activeMobileThread.key);
5590
+ setListOpen(true);
5591
+ },
5592
+ onClose: closeAll,
5593
+ onConversationReady: () => void onRefreshItems?.(),
5594
+ className: "h-full rounded-none border-0 shadow-none"
5595
+ }
5596
+ )
5597
+ ] }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
5598
+ /* @__PURE__ */ jsxRuntime.jsx(DrawerHeader, { className: "sr-only", children: /* @__PURE__ */ jsxRuntime.jsx(DrawerTitle, { children: labels.listTitle }) }),
5599
+ /* @__PURE__ */ jsxRuntime.jsx(
5600
+ ListPanel,
5601
+ {
5602
+ items,
5603
+ labels,
5604
+ tabs,
5605
+ activeTab,
5606
+ onTabChange,
5607
+ onSelect: handleSelectItem,
5608
+ onClose: closeAll,
5609
+ searchThreshold,
5610
+ className: "h-full rounded-none border-0 shadow-none"
5611
+ }
5612
+ )
5613
+ ] }) })
5614
+ }
5615
+ ) : null,
5616
+ /* @__PURE__ */ jsxRuntime.jsxs(
5617
+ "button",
5618
+ {
5619
+ type: "button",
5620
+ className: cn(
5621
+ "pointer-events-auto relative flex size-14 items-center justify-center rounded-full bg-primary text-primary-foreground shadow-lg transition-[transform,opacity] motion-safe:hover:scale-105 focus-visible:ring-2 focus-visible:ring-ring focus-visible:outline-none",
5622
+ listOpen && !forceMinimized && "ring-2 ring-ring ring-offset-2"
5623
+ ),
5624
+ "aria-label": totalUnread > 0 && labels.unreadBadgeAria ? `${labels.launcherAria}. ${labels.unreadBadgeAria.replace("{count}", String(totalUnread))}` : labels.launcherAria,
5625
+ onClick: () => {
5626
+ if (isMobile) {
5627
+ if (activeMobileThread) {
5628
+ closeAll();
5629
+ } else {
5630
+ toggleList();
5631
+ }
5632
+ return;
5633
+ }
5634
+ toggleList();
5635
+ },
5636
+ children: [
5637
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.MessageCircle, { className: "size-6" }),
5638
+ totalUnread > 0 ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "absolute -top-0.5 -right-0.5 flex size-5 items-center justify-center rounded-full bg-destructive text-[10px] font-bold text-white", children: totalUnread > 99 ? "99+" : totalUnread }) : null
5639
+ ]
5640
+ }
5641
+ )
5642
+ ]
5643
+ }
5644
+ ) : null
5645
+ ] });
5646
+ }
5647
+
5648
+ // src/lib/support-chat.ts
5649
+ async function parseJson(res) {
5650
+ const body = await res.json();
5651
+ if (!res.ok) {
5652
+ throw new Error(
5653
+ typeof body === "object" && body && "error" in body && body.error ? String(body.error) : `Chat request failed (${res.status})`
5654
+ );
5655
+ }
5656
+ return body;
5657
+ }
5658
+ function createSupportChatClient(basePath = "/api/chat-svc") {
5659
+ const root = basePath.replace(/\/$/, "");
5660
+ return {
5661
+ basePath: root,
5662
+ async listConversations() {
5663
+ const res = await fetch(`${root}/me/conversations`);
5664
+ const body = await parseJson(res);
5665
+ return body.result ?? [];
5666
+ },
5667
+ async findOrCreateConversation(input) {
5668
+ const res = await fetch(`${root}/conversations`, {
5669
+ method: "POST",
5670
+ headers: { "content-type": "application/json" },
5671
+ body: JSON.stringify(input)
5672
+ });
5673
+ const body = await parseJson(res);
5674
+ return body.result;
5675
+ },
5676
+ async listMessages(conversationId) {
5677
+ const res = await fetch(
5678
+ `${root}/conversations/${encodeURIComponent(conversationId)}/messages`
5679
+ );
5680
+ const body = await parseJson(res);
5681
+ return body.result ?? [];
5682
+ },
5683
+ async sendMessage(conversationId, text) {
5684
+ const res = await fetch(
5685
+ `${root}/conversations/${encodeURIComponent(conversationId)}/messages`,
5686
+ {
5687
+ method: "POST",
5688
+ headers: { "content-type": "application/json" },
5689
+ body: JSON.stringify({ body: text })
5690
+ }
5691
+ );
5692
+ const body = await parseJson(res);
5693
+ return body.result;
5694
+ },
5695
+ async markRead(conversationId) {
5696
+ await fetch(
5697
+ `${root}/conversations/${encodeURIComponent(conversationId)}/read`,
5698
+ { method: "POST" }
5699
+ );
5700
+ }
5701
+ };
5702
+ }
5072
5703
 
5073
5704
  Object.defineProperty(exports, "useMessageScroller", {
5074
5705
  enumerable: true,
@@ -5126,6 +5757,7 @@ exports.MexicoMap = MexicoMap;
5126
5757
  exports.OrgSwitcher = OrgSwitcher;
5127
5758
  exports.PhoneInput = PhoneInput;
5128
5759
  exports.SearchCommand = SearchCommand;
5760
+ exports.SupportMessengerProvider = SupportMessengerProvider;
5129
5761
  exports.ThemeSwitcher = ThemeSwitcher;
5130
5762
  exports.VerifiedProfessionalBadge = VerifiedProfessionalBadge;
5131
5763
  exports.VideoPlayer = VideoPlayer;
@@ -5133,6 +5765,7 @@ exports.avatarFallbackStyle = avatarFallbackStyle;
5133
5765
  exports.bubbleVariants = bubbleVariants;
5134
5766
  exports.cn = cn;
5135
5767
  exports.createLiveHlsConfig = createLiveHlsConfig;
5768
+ exports.createSupportChatClient = createSupportChatClient;
5136
5769
  exports.createVodHlsConfig = createVodHlsConfig;
5137
5770
  exports.defaultLanguages = defaultLanguages;
5138
5771
  exports.describePlaybackError = describePlaybackError;
@@ -5149,5 +5782,8 @@ exports.useHlsPlayback = useHlsPlayback;
5149
5782
  exports.useLanguageContext = useLanguageContext;
5150
5783
  exports.usePlaybackStats = usePlaybackStats;
5151
5784
  exports.useSearchHotkey = useSearchHotkey;
5785
+ exports.useSupportConversation = useSupportConversation;
5786
+ exports.useSupportMessenger = useSupportMessenger;
5787
+ exports.useSupportMessengerOptional = useSupportMessengerOptional;
5152
5788
  //# sourceMappingURL=index.cjs.map
5153
5789
  //# sourceMappingURL=index.cjs.map