@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.js CHANGED
@@ -1,7 +1,7 @@
1
1
  "use client";
2
2
  import * as React6 from 'react';
3
3
  import { createContext, useContext, useState, useCallback, useEffect, useRef, useMemo } from 'react';
4
- import { ChevronsUpDown, CheckIcon, Smile, SearchIcon, Search, Clock, Flag, Hash, Lightbulb, Trophy, Plane, Apple, Leaf, Hand, Loader2, AlertTriangle, Inbox, Monitor, Sun, Moon, Building2, Check, Plus, Languages, Upload, Move, ZoomOut, ZoomIn, RotateCcw, RotateCw, Grid3X3, RefreshCw, X, User, Pencil, Play, AlertCircle, VolumeX, Pause, Volume1, Volume2, Settings, PictureInPicture2, Minimize, Maximize, BadgeCheck, ArrowDownIcon, MessageCircleDashedIcon, XIcon } from 'lucide-react';
4
+ import { ChevronsUpDown, CheckIcon, Smile, SearchIcon, Search, Clock, Flag, Hash, Lightbulb, Trophy, Plane, Apple, Leaf, Hand, Loader2, AlertTriangle, Inbox, Monitor, Sun, Moon, Building2, Check, Plus, Languages, Upload, Move, ZoomOut, ZoomIn, RotateCcw, RotateCw, Grid3X3, RefreshCw, X, User, Pencil, Play, AlertCircle, VolumeX, Pause, Volume1, Volume2, Settings, PictureInPicture2, Minimize, Maximize, BadgeCheck, ArrowDownIcon, MessageCircleDashedIcon, MessageCircle, XIcon, ChevronLeft } from 'lucide-react';
5
5
  import { clsx } from 'clsx';
6
6
  import { twMerge } from 'tailwind-merge';
7
7
  import { cva } from 'class-variance-authority';
@@ -1956,6 +1956,8 @@ function AvatarEditorDialog({
1956
1956
  dialogTitle = "Edit Avatar",
1957
1957
  acceptText = "Accept",
1958
1958
  cancelText = "Cancel",
1959
+ savingText = "Saving...",
1960
+ savedText = "Saved!",
1959
1961
  successMessage = "Avatar saved successfully!",
1960
1962
  errorMessage = "Failed to save avatar. Please try again.",
1961
1963
  className
@@ -2117,10 +2119,10 @@ function AvatarEditorDialog({
2117
2119
  )
2118
2120
  }
2119
2121
  ),
2120
- "Saving..."
2122
+ savingText
2121
2123
  ] }) : feedback?.type === "success" ? /* @__PURE__ */ jsxs(Fragment, { children: [
2122
2124
  /* @__PURE__ */ jsx(Check, { className: cn("mr-2", isMobile ? "w-5 h-5" : "w-4 h-4") }),
2123
- "Saved!"
2125
+ savedText
2124
2126
  ] }) : acceptText
2125
2127
  }
2126
2128
  )
@@ -5038,7 +5040,636 @@ function ChatMessageList({
5038
5040
  /* @__PURE__ */ jsx(MessageScrollerButton, {})
5039
5041
  ] }) });
5040
5042
  }
5043
+ function useMediaQuery2(query) {
5044
+ const [matches, setMatches] = useState(false);
5045
+ useEffect(() => {
5046
+ if (typeof window === "undefined" || !window.matchMedia) return;
5047
+ const mql = window.matchMedia(query);
5048
+ const onChange = () => setMatches(mql.matches);
5049
+ onChange();
5050
+ mql.addEventListener("change", onChange);
5051
+ return () => mql.removeEventListener("change", onChange);
5052
+ }, [query]);
5053
+ return matches;
5054
+ }
5055
+ var RECONNECT_DELAY_MS = 2e3;
5056
+ function useSupportConversation({
5057
+ client,
5058
+ getWsUrl,
5059
+ conversationId: conversationIdProp,
5060
+ open = null,
5061
+ enabled = true,
5062
+ onConversationReady,
5063
+ onIncomingMessage
5064
+ }) {
5065
+ const [conversationId, setConversationId] = useState(
5066
+ conversationIdProp
5067
+ );
5068
+ const [messages, setMessages] = useState([]);
5069
+ const [loading, setLoading] = useState(false);
5070
+ const [sending, setSending] = useState(false);
5071
+ const [error, setError] = useState(null);
5072
+ const socketRef = useRef(null);
5073
+ const reconnectRef = useRef(null);
5074
+ const closedByUser = useRef(false);
5075
+ const onReadyRef = useRef(onConversationReady);
5076
+ const onIncomingRef = useRef(onIncomingMessage);
5077
+ onReadyRef.current = onConversationReady;
5078
+ onIncomingRef.current = onIncomingMessage;
5079
+ const load = useCallback(async () => {
5080
+ if (!enabled) return;
5081
+ setLoading(true);
5082
+ setError(null);
5083
+ try {
5084
+ let id = conversationIdProp;
5085
+ if (!id && open) {
5086
+ const conv = await client.findOrCreateConversation(open);
5087
+ id = conv.id;
5088
+ }
5089
+ if (!id) {
5090
+ setConversationId(null);
5091
+ setMessages([]);
5092
+ return;
5093
+ }
5094
+ setConversationId(id);
5095
+ onReadyRef.current?.(id);
5096
+ const msgs = await client.listMessages(id);
5097
+ setMessages(msgs);
5098
+ await client.markRead(id);
5099
+ } catch (err) {
5100
+ setError(err instanceof Error ? err.message : "Chat load failed");
5101
+ } finally {
5102
+ setLoading(false);
5103
+ }
5104
+ }, [client, conversationIdProp, enabled, open]);
5105
+ useEffect(() => {
5106
+ void load();
5107
+ }, [load]);
5108
+ useEffect(() => {
5109
+ closedByUser.current = false;
5110
+ if (!enabled || !conversationId) return;
5111
+ let cancelled = false;
5112
+ const connect = async () => {
5113
+ const url = await getWsUrl(conversationId);
5114
+ if (!url || cancelled || closedByUser.current) return;
5115
+ const socket = new WebSocket(url);
5116
+ socketRef.current = socket;
5117
+ socket.onmessage = (event) => {
5118
+ let data;
5119
+ try {
5120
+ data = JSON.parse(String(event.data));
5121
+ } catch {
5122
+ return;
5123
+ }
5124
+ if (data.type === "message") {
5125
+ setMessages((prev) => {
5126
+ if (prev.some((m) => m.id === data.id)) return prev;
5127
+ return [...prev, data];
5128
+ });
5129
+ onIncomingRef.current?.(data);
5130
+ } else if (data.type === "deleted") {
5131
+ setMessages(
5132
+ (prev) => prev.map(
5133
+ (m) => m.id === data.id ? { ...m, deleted: true, body: "" } : m
5134
+ )
5135
+ );
5136
+ }
5137
+ };
5138
+ socket.onclose = () => {
5139
+ if (cancelled || closedByUser.current) return;
5140
+ reconnectRef.current = setTimeout(() => {
5141
+ void connect();
5142
+ }, RECONNECT_DELAY_MS);
5143
+ };
5144
+ };
5145
+ void connect();
5146
+ return () => {
5147
+ cancelled = true;
5148
+ closedByUser.current = true;
5149
+ if (reconnectRef.current) clearTimeout(reconnectRef.current);
5150
+ socketRef.current?.close();
5151
+ socketRef.current = null;
5152
+ };
5153
+ }, [conversationId, enabled, getWsUrl]);
5154
+ const send = useCallback(
5155
+ async (text) => {
5156
+ const trimmed = text.trim();
5157
+ if (!conversationId || !trimmed || sending) return;
5158
+ setSending(true);
5159
+ setError(null);
5160
+ try {
5161
+ const msg = await client.sendMessage(conversationId, trimmed);
5162
+ setMessages((prev) => {
5163
+ if (prev.some((m) => m.id === msg.id)) return prev;
5164
+ return [...prev, msg];
5165
+ });
5166
+ await client.markRead(conversationId);
5167
+ } catch (err) {
5168
+ setError(err instanceof Error ? err.message : "Send failed");
5169
+ throw err;
5170
+ } finally {
5171
+ setSending(false);
5172
+ }
5173
+ },
5174
+ [client, conversationId, sending]
5175
+ );
5176
+ return {
5177
+ conversationId,
5178
+ messages,
5179
+ loading,
5180
+ sending,
5181
+ error,
5182
+ send,
5183
+ reload: load
5184
+ };
5185
+ }
5186
+ var SupportMessengerContext = createContext(null);
5187
+ function useSupportMessenger() {
5188
+ const ctx = useContext(SupportMessengerContext);
5189
+ if (!ctx) {
5190
+ throw new Error(
5191
+ "useSupportMessenger must be used within SupportMessengerProvider"
5192
+ );
5193
+ }
5194
+ return ctx;
5195
+ }
5196
+ function useSupportMessengerOptional() {
5197
+ return useContext(SupportMessengerContext);
5198
+ }
5199
+ function ThreadPanel({
5200
+ thread,
5201
+ client,
5202
+ getWsUrl,
5203
+ currentUserId,
5204
+ labels,
5205
+ emojiLocale,
5206
+ onClose,
5207
+ onBack,
5208
+ showBack,
5209
+ onConversationReady,
5210
+ className
5211
+ }) {
5212
+ const [draft, setDraft] = useState("");
5213
+ const { messages, loading, sending, error, send } = useSupportConversation({
5214
+ client,
5215
+ getWsUrl,
5216
+ conversationId: thread.conversationId ?? null,
5217
+ open: thread.open ?? null,
5218
+ onConversationReady
5219
+ });
5220
+ async function onSubmit(e) {
5221
+ e.preventDefault();
5222
+ const text = draft.trim();
5223
+ if (!text) return;
5224
+ setDraft("");
5225
+ try {
5226
+ await send(text);
5227
+ } catch {
5228
+ setDraft(text);
5229
+ }
5230
+ }
5231
+ return /* @__PURE__ */ jsxs(
5232
+ "div",
5233
+ {
5234
+ className: cn(
5235
+ "flex h-full min-h-0 flex-col overflow-hidden rounded-xl border bg-card shadow-lg",
5236
+ className
5237
+ ),
5238
+ children: [
5239
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1 border-b px-2 py-2", children: [
5240
+ showBack && onBack ? /* @__PURE__ */ jsx(
5241
+ Button,
5242
+ {
5243
+ type: "button",
5244
+ variant: "ghost",
5245
+ size: "icon-sm",
5246
+ onClick: onBack,
5247
+ "aria-label": labels.back,
5248
+ children: /* @__PURE__ */ jsx(ChevronLeft, { className: "size-4" })
5249
+ }
5250
+ ) : null,
5251
+ /* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1 px-1", children: [
5252
+ /* @__PURE__ */ jsx("p", { className: "truncate text-sm font-semibold", children: thread.title }),
5253
+ thread.subtitle ? /* @__PURE__ */ jsx("p", { className: "truncate text-xs text-muted-foreground", children: thread.subtitle }) : null
5254
+ ] }),
5255
+ /* @__PURE__ */ jsx(
5256
+ Button,
5257
+ {
5258
+ type: "button",
5259
+ variant: "ghost",
5260
+ size: "icon-sm",
5261
+ onClick: onClose,
5262
+ "aria-label": labels.close,
5263
+ children: /* @__PURE__ */ jsx(X, { className: "size-4" })
5264
+ }
5265
+ )
5266
+ ] }),
5267
+ /* @__PURE__ */ jsx("div", { className: "min-h-0 flex-1", children: loading ? /* @__PURE__ */ jsx("div", { className: "flex h-full items-center justify-center p-6", children: /* @__PURE__ */ jsx(LoadingState, { label: labels.loading, className: "py-0" }) }) : error ? /* @__PURE__ */ jsx("p", { className: "p-4 text-sm text-destructive", role: "alert", children: error }) : /* @__PURE__ */ jsx(
5268
+ ChatMessageList,
5269
+ {
5270
+ messages,
5271
+ currentUserId,
5272
+ emptyDescription: labels.emptyThread
5273
+ }
5274
+ ) }),
5275
+ /* @__PURE__ */ jsxs("form", { className: "flex gap-2 border-t p-3", onSubmit, children: [
5276
+ /* @__PURE__ */ jsx(
5277
+ EmojiTextarea,
5278
+ {
5279
+ locale: emojiLocale,
5280
+ value: draft,
5281
+ onChange: (e) => setDraft(e.target.value.slice(0, 2e3)),
5282
+ placeholder: labels.placeholder,
5283
+ "aria-label": labels.placeholder,
5284
+ disabled: loading || sending,
5285
+ maxLength: 2e3,
5286
+ rows: 2,
5287
+ className: "min-h-10 flex-1"
5288
+ }
5289
+ ),
5290
+ /* @__PURE__ */ jsx(Button, { type: "submit", disabled: !draft.trim() || sending || loading, children: labels.send })
5291
+ ] })
5292
+ ]
5293
+ }
5294
+ );
5295
+ }
5296
+ function ListPanel({
5297
+ items,
5298
+ labels,
5299
+ tabs,
5300
+ activeTab,
5301
+ onTabChange,
5302
+ onSelect,
5303
+ onClose,
5304
+ searchThreshold,
5305
+ className
5306
+ }) {
5307
+ const [query, setQuery] = useState("");
5308
+ const showSearch = items.length >= searchThreshold;
5309
+ const filtered = useMemo(() => {
5310
+ const q = query.trim().toLowerCase();
5311
+ if (!q) return items;
5312
+ return items.filter(
5313
+ (item) => item.title.toLowerCase().includes(q) || (item.subtitle?.toLowerCase().includes(q) ?? false)
5314
+ );
5315
+ }, [items, query]);
5316
+ return /* @__PURE__ */ jsxs(
5317
+ "div",
5318
+ {
5319
+ className: cn(
5320
+ "flex h-full min-h-0 flex-col overflow-hidden rounded-xl border bg-card shadow-lg",
5321
+ className
5322
+ ),
5323
+ children: [
5324
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between border-b px-3 py-2", children: [
5325
+ /* @__PURE__ */ jsx("p", { className: "text-sm font-semibold", children: labels.listTitle }),
5326
+ /* @__PURE__ */ jsx(
5327
+ Button,
5328
+ {
5329
+ type: "button",
5330
+ variant: "ghost",
5331
+ size: "icon-sm",
5332
+ onClick: onClose,
5333
+ "aria-label": labels.close,
5334
+ children: /* @__PURE__ */ jsx(X, { className: "size-4" })
5335
+ }
5336
+ )
5337
+ ] }),
5338
+ tabs && tabs.length > 0 ? /* @__PURE__ */ jsx("div", { className: "flex gap-1 border-b p-2", children: tabs.map((tab) => /* @__PURE__ */ jsx(
5339
+ Button,
5340
+ {
5341
+ type: "button",
5342
+ size: "sm",
5343
+ variant: activeTab === tab.id ? "default" : "outline",
5344
+ onClick: () => onTabChange?.(tab.id),
5345
+ children: tab.label
5346
+ },
5347
+ tab.id
5348
+ )) }) : null,
5349
+ showSearch ? /* @__PURE__ */ jsx("div", { className: "border-b p-2", children: /* @__PURE__ */ jsxs("label", { className: "relative block", children: [
5350
+ /* @__PURE__ */ jsx(Search, { className: "pointer-events-none absolute top-1/2 left-2.5 size-3.5 -translate-y-1/2 text-muted-foreground" }),
5351
+ /* @__PURE__ */ jsx(
5352
+ "input",
5353
+ {
5354
+ value: query,
5355
+ onChange: (e) => setQuery(e.target.value),
5356
+ placeholder: labels.searchPlaceholder ?? "Buscar\u2026",
5357
+ 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"
5358
+ }
5359
+ )
5360
+ ] }) }) : null,
5361
+ /* @__PURE__ */ jsx("ul", { className: "min-h-0 flex-1 overflow-y-auto p-1", children: filtered.length === 0 ? /* @__PURE__ */ jsx("li", { className: "px-3 py-8 text-center text-sm text-muted-foreground", children: labels.emptyList }) : filtered.map((item) => /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsxs(
5362
+ "button",
5363
+ {
5364
+ type: "button",
5365
+ className: "flex w-full items-center gap-3 rounded-lg px-2 py-2.5 text-left transition-colors hover:bg-muted/80",
5366
+ onClick: () => onSelect(item),
5367
+ children: [
5368
+ /* @__PURE__ */ jsx(
5369
+ "span",
5370
+ {
5371
+ className: "flex size-9 shrink-0 items-center justify-center overflow-hidden rounded-full text-xs font-semibold text-white",
5372
+ style: item.avatarUrl ? void 0 : avatarFallbackStyle(item.title),
5373
+ children: item.avatarUrl ? /* @__PURE__ */ jsx(
5374
+ "img",
5375
+ {
5376
+ src: item.avatarUrl,
5377
+ alt: "",
5378
+ className: "size-full object-cover"
5379
+ }
5380
+ ) : getInitials(item.title)
5381
+ }
5382
+ ),
5383
+ /* @__PURE__ */ jsxs("span", { className: "min-w-0 flex-1", children: [
5384
+ /* @__PURE__ */ jsx("span", { className: "block truncate text-sm font-medium", children: item.title }),
5385
+ item.subtitle ? /* @__PURE__ */ jsx("span", { className: "block truncate text-xs text-muted-foreground", children: item.subtitle }) : null
5386
+ ] }),
5387
+ (item.unreadCount ?? 0) > 0 ? /* @__PURE__ */ 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
5388
+ ]
5389
+ }
5390
+ ) }, item.key)) })
5391
+ ]
5392
+ }
5393
+ );
5394
+ }
5395
+ function SupportMessengerProvider({
5396
+ children,
5397
+ client,
5398
+ getWsUrl,
5399
+ currentUserId,
5400
+ items,
5401
+ labels,
5402
+ enabled = true,
5403
+ forceMinimized = false,
5404
+ emojiLocale = "es",
5405
+ tabs,
5406
+ activeTab,
5407
+ onTabChange,
5408
+ onRefreshItems,
5409
+ searchThreshold = 8,
5410
+ className
5411
+ }) {
5412
+ const isMobile = useMediaQuery2("(max-width: 767px)");
5413
+ const [listOpen, setListOpen] = useState(false);
5414
+ const [threads, setThreads] = useState([]);
5415
+ const [mobileThreadKey, setMobileThreadKey] = useState(null);
5416
+ const totalUnread = useMemo(
5417
+ () => items.reduce((sum, item) => sum + (item.unreadCount ?? 0), 0),
5418
+ [items]
5419
+ );
5420
+ useEffect(() => {
5421
+ if (!forceMinimized) return;
5422
+ setListOpen(false);
5423
+ setThreads([]);
5424
+ setMobileThreadKey(null);
5425
+ }, [forceMinimized]);
5426
+ const openList = useCallback(() => {
5427
+ setListOpen(true);
5428
+ void onRefreshItems?.();
5429
+ }, [onRefreshItems]);
5430
+ const closeList = useCallback(() => setListOpen(false), []);
5431
+ const toggleList = useCallback(() => {
5432
+ setListOpen((prev) => {
5433
+ const next = !prev;
5434
+ if (next) void onRefreshItems?.();
5435
+ return next;
5436
+ });
5437
+ }, [onRefreshItems]);
5438
+ const openThread = useCallback((req) => {
5439
+ setListOpen(false);
5440
+ setThreads((prev) => {
5441
+ const existing = prev.find((t) => t.key === req.key);
5442
+ if (existing) {
5443
+ return [
5444
+ ...prev.filter((t) => t.key !== req.key),
5445
+ { ...existing, ...req }
5446
+ ];
5447
+ }
5448
+ const next = [...prev, req];
5449
+ return next.slice(-2);
5450
+ });
5451
+ setMobileThreadKey(req.key);
5452
+ }, []);
5453
+ const closeThread = useCallback((key) => {
5454
+ setThreads((prev) => prev.filter((t) => t.key !== key));
5455
+ setMobileThreadKey((prev) => prev === key ? null : prev);
5456
+ }, []);
5457
+ const closeAll = useCallback(() => {
5458
+ setListOpen(false);
5459
+ setThreads([]);
5460
+ setMobileThreadKey(null);
5461
+ }, []);
5462
+ const ctx = useMemo(
5463
+ () => ({
5464
+ listOpen,
5465
+ openList,
5466
+ closeList,
5467
+ toggleList,
5468
+ openThread,
5469
+ closeThread,
5470
+ closeAll,
5471
+ activeThreadKey: mobileThreadKey ?? threads[threads.length - 1]?.key ?? null,
5472
+ totalUnread
5473
+ }),
5474
+ [
5475
+ listOpen,
5476
+ openList,
5477
+ closeList,
5478
+ toggleList,
5479
+ openThread,
5480
+ closeThread,
5481
+ closeAll,
5482
+ mobileThreadKey,
5483
+ threads,
5484
+ totalUnread
5485
+ ]
5486
+ );
5487
+ const handleSelectItem = useCallback(
5488
+ (item) => {
5489
+ openThread({
5490
+ key: item.key,
5491
+ title: item.title,
5492
+ subtitle: item.subtitle,
5493
+ conversationId: item.conversationId,
5494
+ open: item.open
5495
+ });
5496
+ },
5497
+ [openThread]
5498
+ );
5499
+ const activeMobileThread = threads.find((t) => t.key === mobileThreadKey);
5500
+ return /* @__PURE__ */ jsxs(SupportMessengerContext.Provider, { value: ctx, children: [
5501
+ children,
5502
+ enabled ? /* @__PURE__ */ jsxs(
5503
+ "div",
5504
+ {
5505
+ className: cn(
5506
+ "pointer-events-none fixed right-4 bottom-4 z-50 flex flex-col items-end gap-3",
5507
+ className
5508
+ ),
5509
+ children: [
5510
+ !isMobile ? /* @__PURE__ */ jsxs("div", { className: "pointer-events-auto flex items-end gap-3", children: [
5511
+ threads.map((thread) => /* @__PURE__ */ jsx("div", { className: "h-[28rem] w-[22rem]", children: /* @__PURE__ */ jsx(
5512
+ ThreadPanel,
5513
+ {
5514
+ thread,
5515
+ client,
5516
+ getWsUrl,
5517
+ currentUserId,
5518
+ labels,
5519
+ emojiLocale,
5520
+ onClose: () => closeThread(thread.key),
5521
+ onConversationReady: () => void onRefreshItems?.()
5522
+ }
5523
+ ) }, thread.key)),
5524
+ listOpen ? /* @__PURE__ */ jsx("div", { className: "h-[28rem] w-[20rem]", children: /* @__PURE__ */ jsx(
5525
+ ListPanel,
5526
+ {
5527
+ items,
5528
+ labels,
5529
+ tabs,
5530
+ activeTab,
5531
+ onTabChange,
5532
+ onSelect: handleSelectItem,
5533
+ onClose: closeList,
5534
+ searchThreshold
5535
+ }
5536
+ ) }) : null
5537
+ ] }) : null,
5538
+ isMobile ? /* @__PURE__ */ jsx(
5539
+ Drawer,
5540
+ {
5541
+ open: listOpen || Boolean(activeMobileThread),
5542
+ onOpenChange: (open) => {
5543
+ if (!open) closeAll();
5544
+ },
5545
+ children: /* @__PURE__ */ jsx(DrawerContent, { className: "pointer-events-auto mx-auto flex h-[85vh] max-w-lg flex-col", children: activeMobileThread ? /* @__PURE__ */ jsxs(Fragment, { children: [
5546
+ /* @__PURE__ */ jsx(DrawerHeader, { className: "sr-only", children: /* @__PURE__ */ jsx(DrawerTitle, { children: activeMobileThread.title }) }),
5547
+ /* @__PURE__ */ jsx(
5548
+ ThreadPanel,
5549
+ {
5550
+ thread: activeMobileThread,
5551
+ client,
5552
+ getWsUrl,
5553
+ currentUserId,
5554
+ labels,
5555
+ emojiLocale,
5556
+ showBack: true,
5557
+ onBack: () => {
5558
+ closeThread(activeMobileThread.key);
5559
+ setListOpen(true);
5560
+ },
5561
+ onClose: closeAll,
5562
+ onConversationReady: () => void onRefreshItems?.(),
5563
+ className: "h-full rounded-none border-0 shadow-none"
5564
+ }
5565
+ )
5566
+ ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
5567
+ /* @__PURE__ */ jsx(DrawerHeader, { className: "sr-only", children: /* @__PURE__ */ jsx(DrawerTitle, { children: labels.listTitle }) }),
5568
+ /* @__PURE__ */ jsx(
5569
+ ListPanel,
5570
+ {
5571
+ items,
5572
+ labels,
5573
+ tabs,
5574
+ activeTab,
5575
+ onTabChange,
5576
+ onSelect: handleSelectItem,
5577
+ onClose: closeAll,
5578
+ searchThreshold,
5579
+ className: "h-full rounded-none border-0 shadow-none"
5580
+ }
5581
+ )
5582
+ ] }) })
5583
+ }
5584
+ ) : null,
5585
+ /* @__PURE__ */ jsxs(
5586
+ "button",
5587
+ {
5588
+ type: "button",
5589
+ className: cn(
5590
+ "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",
5591
+ listOpen && !forceMinimized && "ring-2 ring-ring ring-offset-2"
5592
+ ),
5593
+ "aria-label": totalUnread > 0 && labels.unreadBadgeAria ? `${labels.launcherAria}. ${labels.unreadBadgeAria.replace("{count}", String(totalUnread))}` : labels.launcherAria,
5594
+ onClick: () => {
5595
+ if (isMobile) {
5596
+ if (activeMobileThread) {
5597
+ closeAll();
5598
+ } else {
5599
+ toggleList();
5600
+ }
5601
+ return;
5602
+ }
5603
+ toggleList();
5604
+ },
5605
+ children: [
5606
+ /* @__PURE__ */ jsx(MessageCircle, { className: "size-6" }),
5607
+ totalUnread > 0 ? /* @__PURE__ */ 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
5608
+ ]
5609
+ }
5610
+ )
5611
+ ]
5612
+ }
5613
+ ) : null
5614
+ ] });
5615
+ }
5616
+
5617
+ // src/lib/support-chat.ts
5618
+ async function parseJson(res) {
5619
+ const body = await res.json();
5620
+ if (!res.ok) {
5621
+ throw new Error(
5622
+ typeof body === "object" && body && "error" in body && body.error ? String(body.error) : `Chat request failed (${res.status})`
5623
+ );
5624
+ }
5625
+ return body;
5626
+ }
5627
+ function createSupportChatClient(basePath = "/api/chat-svc") {
5628
+ const root = basePath.replace(/\/$/, "");
5629
+ return {
5630
+ basePath: root,
5631
+ async listConversations() {
5632
+ const res = await fetch(`${root}/me/conversations`);
5633
+ const body = await parseJson(res);
5634
+ return body.result ?? [];
5635
+ },
5636
+ async findOrCreateConversation(input) {
5637
+ const res = await fetch(`${root}/conversations`, {
5638
+ method: "POST",
5639
+ headers: { "content-type": "application/json" },
5640
+ body: JSON.stringify(input)
5641
+ });
5642
+ const body = await parseJson(res);
5643
+ return body.result;
5644
+ },
5645
+ async listMessages(conversationId) {
5646
+ const res = await fetch(
5647
+ `${root}/conversations/${encodeURIComponent(conversationId)}/messages`
5648
+ );
5649
+ const body = await parseJson(res);
5650
+ return body.result ?? [];
5651
+ },
5652
+ async sendMessage(conversationId, text) {
5653
+ const res = await fetch(
5654
+ `${root}/conversations/${encodeURIComponent(conversationId)}/messages`,
5655
+ {
5656
+ method: "POST",
5657
+ headers: { "content-type": "application/json" },
5658
+ body: JSON.stringify({ body: text })
5659
+ }
5660
+ );
5661
+ const body = await parseJson(res);
5662
+ return body.result;
5663
+ },
5664
+ async markRead(conversationId) {
5665
+ await fetch(
5666
+ `${root}/conversations/${encodeURIComponent(conversationId)}/read`,
5667
+ { method: "POST" }
5668
+ );
5669
+ }
5670
+ };
5671
+ }
5041
5672
 
5042
- export { AvatarEditor, AvatarEditorDialog, Bubble, BubbleContent, BubbleGroup, BubbleReactions, ChatMessageList, ConfirmDestructiveDialog, EmojiInput, EmojiPicker, EmojiPickerPopover, EmojiTextarea, Empty, EmptyContent, EmptyDescription, EmptyHeader, EmptyMedia, EmptyState, EmptyTitle, ErrorState, LanguageContext, LanguageSwitcher, LoadingState, MEXICO_STATES, MEXICO_STATE_PATHS, Marker, MarkerContent, MarkerIcon, Message, MessageAvatar, MessageContent, MessageFooter, MessageGroup, MessageHeader, MessageScroller, MessageScrollerButton, MessageScrollerContent, MessageScrollerItem, MessageScrollerProvider, MessageScrollerViewport, MexicoMap, OrgSwitcher, PhoneInput, SearchCommand, ThemeSwitcher, VerifiedProfessionalBadge, VideoPlayer, avatarFallbackStyle, bubbleVariants, cn, createLiveHlsConfig, createVodHlsConfig, defaultLanguages, describePlaybackError, fuzzyMatch, getAvatarColors, getInitials, insertAtCaret, markerVariants, normalizeEmojiQuery, shouldRetryLiveEdge404, useDebouncedValue, useDebouncedValueStrict, useHlsPlayback, useLanguageContext, usePlaybackStats, useSearchHotkey };
5673
+ export { AvatarEditor, AvatarEditorDialog, Bubble, BubbleContent, BubbleGroup, BubbleReactions, ChatMessageList, ConfirmDestructiveDialog, EmojiInput, EmojiPicker, EmojiPickerPopover, EmojiTextarea, Empty, EmptyContent, EmptyDescription, EmptyHeader, EmptyMedia, EmptyState, EmptyTitle, ErrorState, LanguageContext, LanguageSwitcher, LoadingState, MEXICO_STATES, MEXICO_STATE_PATHS, Marker, MarkerContent, MarkerIcon, Message, MessageAvatar, MessageContent, MessageFooter, MessageGroup, MessageHeader, MessageScroller, MessageScrollerButton, MessageScrollerContent, MessageScrollerItem, MessageScrollerProvider, MessageScrollerViewport, MexicoMap, OrgSwitcher, PhoneInput, SearchCommand, SupportMessengerProvider, ThemeSwitcher, VerifiedProfessionalBadge, VideoPlayer, avatarFallbackStyle, bubbleVariants, cn, createLiveHlsConfig, createSupportChatClient, createVodHlsConfig, defaultLanguages, describePlaybackError, fuzzyMatch, getAvatarColors, getInitials, insertAtCaret, markerVariants, normalizeEmojiQuery, shouldRetryLiveEdge404, useDebouncedValue, useDebouncedValueStrict, useHlsPlayback, useLanguageContext, usePlaybackStats, useSearchHotkey, useSupportConversation, useSupportMessenger, useSupportMessengerOptional };
5043
5674
  //# sourceMappingURL=index.js.map
5044
5675
  //# sourceMappingURL=index.js.map