framepexls-ui-lib 2.2.8 → 2.2.11

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.
@@ -10,6 +10,7 @@ import TimeAgo from "./TimeAgo.mjs";
10
10
  import Tooltip from "./Tooltip.mjs";
11
11
  import {
12
12
  ChatCenteredDotsIcon,
13
+ ChatCircleTextIcon,
13
14
  ClockIcon,
14
15
  CloseIcon,
15
16
  DownloadSimpleIcon,
@@ -194,14 +195,34 @@ function SupportChatWidget({
194
195
  const open = openProp != null ? openProp : openState;
195
196
  const setOpen = (next) => {
196
197
  if (openProp === void 0) setOpenState(next);
198
+ if (next) setUnseenReplyCount(0);
197
199
  onOpenChange == null ? void 0 : onOpenChange(next);
198
200
  };
199
201
  const [messagesState, setMessagesState] = React.useState(() => defaultMessages != null ? defaultMessages : []);
200
202
  const messages = messagesProp != null ? messagesProp : messagesState;
201
203
  const messagesRef = React.useRef(messages);
204
+ const knownMessageIdsRef = React.useRef(new Set(messages.map((message) => message.id)));
205
+ const panelRef = React.useRef(null);
206
+ const triggerRef = React.useRef(null);
207
+ const [unseenReplyCount, setUnseenReplyCount] = React.useState(0);
202
208
  React.useEffect(() => {
203
209
  messagesRef.current = messages;
204
210
  }, [messages]);
211
+ React.useEffect(() => {
212
+ const knownIds = knownMessageIdsRef.current;
213
+ const newReplies = messages.filter((message) => {
214
+ const isNew = !knownIds.has(message.id);
215
+ return isNew && (message.role === "bot" || message.role === "agent");
216
+ });
217
+ messages.forEach((message) => knownIds.add(message.id));
218
+ if (open) {
219
+ setUnseenReplyCount(0);
220
+ return;
221
+ }
222
+ if (newReplies.length) {
223
+ setUnseenReplyCount((current) => Math.min(99, current + newReplies.length));
224
+ }
225
+ }, [messages, open]);
205
226
  const setMessages = (next) => {
206
227
  if (typeof next === "function") {
207
228
  const fn = next;
@@ -278,6 +299,18 @@ function SupportChatWidget({
278
299
  if (!open) return;
279
300
  (_a2 = endRef.current) == null ? void 0 : _a2.scrollIntoView({ behavior: "smooth", block: "end" });
280
301
  }, [open, messages.length, sending]);
302
+ React.useEffect(() => {
303
+ if (!open || position === "inline") return;
304
+ const onPointerDown = (event) => {
305
+ var _a2, _b2;
306
+ const target = event.target;
307
+ if (!target) return;
308
+ if (((_a2 = panelRef.current) == null ? void 0 : _a2.contains(target)) || ((_b2 = triggerRef.current) == null ? void 0 : _b2.contains(target))) return;
309
+ setOpen(false);
310
+ };
311
+ document.addEventListener("pointerdown", onPointerDown);
312
+ return () => document.removeEventListener("pointerdown", onPointerDown);
313
+ }, [open, position]);
281
314
  const appendBotResult = (result) => {
282
315
  if (!result) return;
283
316
  if (typeof result === "string") {
@@ -383,19 +416,22 @@ function SupportChatWidget({
383
416
  await ((_d2 = config.onSave) == null ? void 0 : _d2.call(config, payload));
384
417
  downloadFile(fileName, content, mimeType);
385
418
  };
386
- const trigger = /* @__PURE__ */ jsx(Tooltip, { content: labelTrigger, placement: position === "bottom-left" ? "right" : "left", offset: 10, children: /* @__PURE__ */ jsx("div", { className: cx(positionClass, className), style: position === "inline" ? void 0 : { zIndex }, children: /* @__PURE__ */ jsxs(
419
+ const trigger = /* @__PURE__ */ jsx(Tooltip, { content: labelTrigger, placement: position === "bottom-left" ? "right" : "left", offset: 10, children: /* @__PURE__ */ jsx("div", { ref: triggerRef, className: cx(positionClass, className), style: position === "inline" ? void 0 : { zIndex }, children: /* @__PURE__ */ jsxs(
387
420
  "button",
388
421
  {
389
422
  type: "button",
390
423
  "aria-label": labelTrigger,
391
424
  onClick: () => setOpen(true),
392
425
  className: cx(
393
- "relative grid h-14 w-14 place-items-center rounded-2xl border border-[color-mix(in_oklab,var(--ring)_28%,var(--border))]",
394
- "bg-[var(--primary)] text-[var(--primary-foreground)] shadow-xl transition hover:brightness-95 active:scale-[0.98]"
426
+ "group relative grid size-14 place-items-center overflow-hidden rounded-2xl border border-[var(--border)]",
427
+ "bg-[color-mix(in_oklab,var(--card)_94%,transparent)] text-[var(--primary)] shadow-[0_20px_48px_rgba(6,19,22,0.13)] backdrop-blur-xl",
428
+ "transition duration-200 hover:-translate-y-0.5 hover:border-[color-mix(in_oklab,var(--primary)_38%,var(--border))] hover:bg-[var(--surface)] hover:text-[var(--foreground)] hover:shadow-[0_24px_58px_rgba(6,19,22,0.18)] active:scale-[0.98]",
429
+ "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[color-mix(in_oklab,var(--primary)_42%,transparent)] dark:shadow-[0_20px_48px_rgba(0,0,0,0.28)] dark:hover:shadow-[0_24px_58px_rgba(0,0,0,0.36)]"
395
430
  ),
396
431
  children: [
397
- mode === "live" ? /* @__PURE__ */ jsx(HeadsetIcon, { "aria-hidden": true, className: "h-6 w-6" }) : /* @__PURE__ */ jsx(ChatCenteredDotsIcon, { "aria-hidden": true, className: "h-6 w-6" }),
398
- unreadCount > 0 ? /* @__PURE__ */ jsx("span", { className: "absolute -right-1.5 -top-1.5 grid h-5 min-w-5 place-items-center rounded-full bg-[var(--danger)] px-1 text-[0.65rem] font-semibold text-white shadow ring-2 ring-[var(--card)]", children: unreadCount > 99 ? "99+" : unreadCount }) : null
432
+ /* @__PURE__ */ jsx("span", { className: "absolute inset-0 bg-[radial-gradient(circle_at_30%_20%,color-mix(in_oklab,var(--primary)_18%,transparent),transparent_38%)] opacity-80 transition group-hover:opacity-100" }),
433
+ /* @__PURE__ */ jsx("span", { className: "relative grid size-9 place-items-center rounded-xl bg-[color-mix(in_oklab,var(--primary)_12%,transparent)] ring-1 ring-[color-mix(in_oklab,var(--primary)_18%,transparent)]", children: /* @__PURE__ */ jsx(ChatCircleTextIcon, { "aria-hidden": true, className: "h-5 w-5" }) }),
434
+ Math.max(unreadCount, unseenReplyCount) > 0 ? /* @__PURE__ */ jsx("span", { className: "absolute -right-1.5 -top-1.5 grid h-5 min-w-5 animate-[fx-chat-badge_280ms_ease-out] place-items-center rounded-full bg-[var(--danger)] px-1 text-[0.65rem] font-semibold text-white shadow ring-2 ring-[var(--card)]", children: Math.max(unreadCount, unseenReplyCount) > 99 ? "99+" : Math.max(unreadCount, unseenReplyCount) }) : null
399
435
  ]
400
436
  }
401
437
  ) }) });
@@ -487,43 +523,53 @@ function SupportChatWidget({
487
523
  const prev = messages[index - 1];
488
524
  const showAvatar = !mine && !system && (prev == null ? void 0 : prev.role) !== message.role;
489
525
  const bubbleClass = system ? "mx-auto max-w-[92%] border-[color-mix(in_oklab,var(--warning)_28%,var(--border))] bg-[color-mix(in_oklab,var(--warning)_10%,transparent)] text-center text-[var(--foreground)]" : mine ? "border-[color-mix(in_oklab,var(--ring)_35%,var(--border))] bg-[color-mix(in_oklab,var(--primary)_14%,transparent)] text-[var(--foreground)]" : "border-[var(--border)] bg-[color-mix(in_oklab,var(--card)_94%,transparent)] text-[var(--foreground)]";
490
- return /* @__PURE__ */ jsxs("div", { className: cx("flex items-end gap-2", mine ? "justify-end" : system ? "justify-center" : "justify-start"), children: [
491
- !mine && !system ? /* @__PURE__ */ jsx("div", { className: cx("shrink-0", showAvatar ? "" : "opacity-0"), children: /* @__PURE__ */ jsx(
492
- AvatarSquare,
493
- {
494
- size: 28,
495
- src: (_b2 = actor == null ? void 0 : actor.avatarUrl) != null ? _b2 : void 0,
496
- alt: (_c2 = actor == null ? void 0 : actor.name) != null ? _c2 : brandName,
497
- initials: actor == null ? void 0 : actor.initials,
498
- color: actor == null ? void 0 : actor.color,
499
- radiusClass: "rounded-lg",
500
- className: "ring-0"
501
- }
502
- ) }) : null,
503
- /* @__PURE__ */ jsxs("div", { className: cx("max-w-[82%]", mine ? "items-end" : "items-start"), children: [
504
- !mine && !system && showAvatar ? /* @__PURE__ */ jsx("div", { className: "mb-1 px-2 text-[0.6875rem] font-semibold text-[var(--muted)]", children: (_d2 = actor == null ? void 0 : actor.name) != null ? _d2 : brandName }) : null,
505
- /* @__PURE__ */ jsxs("div", { className: cx("rounded-2xl border px-3.5 py-2.5 text-sm leading-6 shadow-sm", bubbleClass, message.state === "error" ? "border-[color-mix(in_oklab,var(--danger)_35%,var(--border))]" : ""), children: [
506
- message.state === "typing" ? /* @__PURE__ */ jsx(Dots, {}) : /* @__PURE__ */ jsx("span", { className: "whitespace-pre-wrap", children: message.content }),
507
- ((_e2 = message.suggestedReplies) == null ? void 0 : _e2.length) ? /* @__PURE__ */ jsx("div", { className: "mt-3 flex flex-wrap gap-2", children: message.suggestedReplies.slice(0, 4).map((reply) => /* @__PURE__ */ jsx(
508
- "button",
526
+ return /* @__PURE__ */ jsxs(
527
+ "div",
528
+ {
529
+ className: cx(
530
+ "flex animate-[fx-chat-message_260ms_cubic-bezier(.2,.8,.2,1)_both] items-end gap-2",
531
+ mine ? "justify-end [--fx-chat-x:10px]" : system ? "justify-center [--fx-chat-x:0px]" : "justify-start [--fx-chat-x:-10px]"
532
+ ),
533
+ children: [
534
+ !mine && !system ? /* @__PURE__ */ jsx("div", { className: cx("shrink-0", showAvatar ? "" : "opacity-0"), children: /* @__PURE__ */ jsx(
535
+ AvatarSquare,
509
536
  {
510
- type: "button",
511
- onClick: () => {
512
- onQuickReply == null ? void 0 : onQuickReply(reply);
513
- void send(reply);
514
- },
515
- className: "rounded-full border border-[var(--border)] bg-[var(--card)] px-3 py-1.5 text-xs font-semibold text-[var(--foreground)] shadow-sm transition hover:bg-[color-mix(in_oklab,var(--surface)_72%,transparent)]",
516
- children: reply
517
- },
518
- `${message.id}:${reply}`
519
- )) }) : null,
520
- ((_f2 = message.serviceSuggestions) == null ? void 0 : _f2.length) ? /* @__PURE__ */ jsx("div", { className: "mt-3 space-y-2", children: message.serviceSuggestions.slice(0, 3).map(renderService) }) : null
521
- ] }),
522
- message.createdAt ? /* @__PURE__ */ jsx("div", { className: cx("mt-1 px-2 text-[0.6875rem] text-[var(--muted)]", mine ? "text-right" : "text-left"), children: /* @__PURE__ */ jsx(TimeAgo, { iso: message.createdAt }) }) : null
523
- ] })
524
- ] }, message.id);
537
+ size: 28,
538
+ src: (_b2 = actor == null ? void 0 : actor.avatarUrl) != null ? _b2 : void 0,
539
+ alt: (_c2 = actor == null ? void 0 : actor.name) != null ? _c2 : brandName,
540
+ initials: actor == null ? void 0 : actor.initials,
541
+ color: actor == null ? void 0 : actor.color,
542
+ radiusClass: "rounded-lg",
543
+ className: "ring-0"
544
+ }
545
+ ) }) : null,
546
+ /* @__PURE__ */ jsxs("div", { className: cx("max-w-[82%]", mine ? "items-end" : "items-start"), children: [
547
+ !mine && !system && showAvatar ? /* @__PURE__ */ jsx("div", { className: "mb-1 px-2 text-[0.6875rem] font-semibold text-[var(--muted)]", children: (_d2 = actor == null ? void 0 : actor.name) != null ? _d2 : brandName }) : null,
548
+ /* @__PURE__ */ jsxs("div", { className: cx("rounded-2xl border px-3.5 py-2.5 text-sm leading-6 shadow-sm transition-[background,border-color,box-shadow,transform] duration-200", bubbleClass, message.state === "error" ? "border-[color-mix(in_oklab,var(--danger)_35%,var(--border))]" : ""), children: [
549
+ message.state === "typing" ? /* @__PURE__ */ jsx(Dots, {}) : /* @__PURE__ */ jsx("span", { className: "whitespace-pre-wrap", children: message.content }),
550
+ ((_e2 = message.suggestedReplies) == null ? void 0 : _e2.length) ? /* @__PURE__ */ jsx("div", { className: "mt-3 flex flex-wrap gap-2", children: message.suggestedReplies.slice(0, 4).map((reply) => /* @__PURE__ */ jsx(
551
+ "button",
552
+ {
553
+ type: "button",
554
+ onClick: () => {
555
+ onQuickReply == null ? void 0 : onQuickReply(reply);
556
+ void send(reply);
557
+ },
558
+ className: "rounded-full border border-[var(--border)] bg-[var(--card)] px-3 py-1.5 text-xs font-semibold text-[var(--foreground)] shadow-sm transition hover:bg-[color-mix(in_oklab,var(--surface)_72%,transparent)]",
559
+ children: reply
560
+ },
561
+ `${message.id}:${reply}`
562
+ )) }) : null,
563
+ ((_f2 = message.serviceSuggestions) == null ? void 0 : _f2.length) ? /* @__PURE__ */ jsx("div", { className: "mt-3 space-y-2", children: message.serviceSuggestions.slice(0, 3).map(renderService) }) : null
564
+ ] }),
565
+ message.createdAt ? /* @__PURE__ */ jsx("div", { className: cx("mt-1 px-2 text-[0.6875rem] text-[var(--muted)]", mine ? "text-right" : "text-left"), children: /* @__PURE__ */ jsx(TimeAgo, { iso: message.createdAt }) }) : null
566
+ ] })
567
+ ]
568
+ },
569
+ message.id
570
+ );
525
571
  }),
526
- sending ? /* @__PURE__ */ jsxs("div", { className: "flex items-end gap-2", children: [
572
+ sending ? /* @__PURE__ */ jsxs("div", { className: "flex animate-[fx-chat-message_260ms_cubic-bezier(.2,.8,.2,1)_both] items-end gap-2 [--fx-chat-x:-10px]", children: [
527
573
  /* @__PURE__ */ jsx(
528
574
  AvatarSquare,
529
575
  {
@@ -543,16 +589,17 @@ function SupportChatWidget({
543
589
  const panel = open ? /* @__PURE__ */ jsxs(
544
590
  "div",
545
591
  {
592
+ ref: panelRef,
546
593
  className: cx(
547
594
  panelPositionClass,
548
- "flex h-[min(680px,calc(100vh-7rem))] w-[min(400px,calc(100vw-2rem))] flex-col overflow-hidden rounded-2xl border border-[var(--border)] bg-[var(--card)] text-[var(--foreground)] shadow-2xl",
595
+ "flex h-[min(680px,calc(100vh-7rem))] w-[min(400px,calc(100vw-2rem))] animate-[fx-chat-panel_180ms_cubic-bezier(.2,.8,.2,1)_both] flex-col overflow-hidden rounded-2xl border border-[var(--border)] bg-[color-mix(in_oklab,var(--card)_96%,transparent)] text-[var(--foreground)] shadow-[0_28px_80px_rgba(6,19,22,0.18)] backdrop-blur-xl dark:shadow-[0_28px_80px_rgba(0,0,0,0.38)]",
549
596
  panelClassName
550
597
  ),
551
598
  style: position === "inline" ? void 0 : { zIndex },
552
599
  role: "dialog",
553
600
  "aria-label": labelTitle,
554
601
  children: [
555
- /* @__PURE__ */ jsx("div", { className: "border-b border-[var(--border)] bg-[color-mix(in_oklab,var(--card)_96%,transparent)] px-4 py-3", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-3", children: [
602
+ /* @__PURE__ */ jsx("div", { className: "border-b border-[var(--border)] bg-[linear-gradient(135deg,color-mix(in_oklab,var(--card)_98%,transparent),color-mix(in_oklab,var(--primary)_8%,var(--surface)))] px-4 py-3", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-3", children: [
556
603
  /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 items-center gap-3", children: [
557
604
  /* @__PURE__ */ jsx(
558
605
  AvatarSquare,
@@ -47,8 +47,8 @@ function UploadCard({
47
47
  multiple = true,
48
48
  disabled,
49
49
  buttonLabel = "Seleccionar",
50
- dropLabel = /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "text-sm font-medium text-slate-700 dark:text-slate-200", children: "Arrastra y suelta archivos aqu\xED o haz clic" }),
51
- note = /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "text-xs text-slate-500 dark:text-slate-400", children: "M\xE1ximo recomendado 16MB por archivo" }),
50
+ dropLabel = /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "text-sm font-semibold text-[var(--foreground)]", children: "Arrastra y suelta archivos aqu\xED o haz clic" }),
51
+ note = /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "text-xs text-[var(--muted)]", children: "M\xE1ximo recomendado 16MB por archivo" }),
52
52
  uploads,
53
53
  className,
54
54
  onFilesSelected
@@ -95,9 +95,9 @@ function UploadCard({
95
95
  var _a;
96
96
  return (_a = inputRef.current) == null ? void 0 : _a.click();
97
97
  },
98
- className: "flex cursor-pointer flex-col items-center justify-center gap-2 rounded-2xl border border-dashed border-slate-300 bg-gradient-to-b from-white/90 to-white/60 p-8 text-center transition hover:border-slate-400 hover:bg-white/80 ring-1 ring-black/5 dark:border-white/10 dark:bg-[var(--fx-surface)] dark:from-white/10 dark:to-white/5 dark:hover:bg-white/10",
98
+ className: "group flex min-h-[190px] cursor-pointer flex-col items-center justify-center gap-3 rounded-lg border border-dashed border-[color-mix(in_oklab,var(--border)_82%,transparent)] bg-[color-mix(in_oklab,var(--surface)_86%,var(--card))] p-8 text-center transition hover:border-[color-mix(in_oklab,var(--primary)_54%,var(--border))] hover:bg-[color-mix(in_oklab,var(--primary)_5%,var(--surface))] focus:outline-none focus-visible:ring-1 focus-visible:ring-[var(--primary)]",
99
99
  children: [
100
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_iconos.ArrowUpFromBracketIcon, { "aria-hidden": true, className: "h-8 w-8 text-slate-400 dark:text-slate-400" }),
100
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "flex h-12 w-12 items-center justify-center rounded-lg border border-[var(--border)] bg-[var(--card)] text-[color-mix(in_oklab,var(--muted)_82%,transparent)] transition group-hover:text-[var(--primary)]", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_iconos.ArrowUpFromBracketIcon, { "aria-hidden": true, className: "h-6 w-6" }) }),
101
101
  dropLabel,
102
102
  note
103
103
  ]
@@ -114,7 +114,7 @@ function UploadCard({
114
114
  animate: { opacity: 1, y: 0 },
115
115
  exit: { opacity: 0, y: -6 },
116
116
  transition: import_animations.springSm,
117
- className: "flex items-center gap-3 rounded-xl border border-slate-200 bg-white/70 p-2 dark:border-white/10 dark:bg-[var(--fx-surface)]",
117
+ className: "flex items-center gap-3 rounded-lg border border-[var(--border)] bg-[var(--card)] p-2",
118
118
  children: [
119
119
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "min-w-0 flex-1", children: [
120
120
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex items-center justify-between gap-3", children: [
@@ -124,10 +124,10 @@ function UploadCard({
124
124
  "%"
125
125
  ] })
126
126
  ] }),
127
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "mt-1 h-2 w-full overflow-hidden rounded-full bg-slate-100 dark:bg-white/10", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
127
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "mt-1 h-2 w-full overflow-hidden rounded-full bg-[color-mix(in_oklab,var(--surface)_84%,transparent)]", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
128
128
  import_framer_motion.motion.div,
129
129
  {
130
- className: "h-full w-full origin-left bg-gradient-to-r from-emerald-500/70 to-emerald-500/20 will-change-transform",
130
+ className: "h-full w-full origin-left bg-[var(--primary)] will-change-transform",
131
131
  initial: { scaleX: 0 },
132
132
  animate: { scaleX: pct / 100 },
133
133
  transition: import_animations.springSm
@@ -14,8 +14,8 @@ function UploadCard({
14
14
  multiple = true,
15
15
  disabled,
16
16
  buttonLabel = "Seleccionar",
17
- dropLabel = /* @__PURE__ */ jsx("div", { className: "text-sm font-medium text-slate-700 dark:text-slate-200", children: "Arrastra y suelta archivos aqu\xED o haz clic" }),
18
- note = /* @__PURE__ */ jsx("div", { className: "text-xs text-slate-500 dark:text-slate-400", children: "M\xE1ximo recomendado 16MB por archivo" }),
17
+ dropLabel = /* @__PURE__ */ jsx("div", { className: "text-sm font-semibold text-[var(--foreground)]", children: "Arrastra y suelta archivos aqu\xED o haz clic" }),
18
+ note = /* @__PURE__ */ jsx("div", { className: "text-xs text-[var(--muted)]", children: "M\xE1ximo recomendado 16MB por archivo" }),
19
19
  uploads,
20
20
  className,
21
21
  onFilesSelected
@@ -62,9 +62,9 @@ function UploadCard({
62
62
  var _a;
63
63
  return (_a = inputRef.current) == null ? void 0 : _a.click();
64
64
  },
65
- className: "flex cursor-pointer flex-col items-center justify-center gap-2 rounded-2xl border border-dashed border-slate-300 bg-gradient-to-b from-white/90 to-white/60 p-8 text-center transition hover:border-slate-400 hover:bg-white/80 ring-1 ring-black/5 dark:border-white/10 dark:bg-[var(--fx-surface)] dark:from-white/10 dark:to-white/5 dark:hover:bg-white/10",
65
+ className: "group flex min-h-[190px] cursor-pointer flex-col items-center justify-center gap-3 rounded-lg border border-dashed border-[color-mix(in_oklab,var(--border)_82%,transparent)] bg-[color-mix(in_oklab,var(--surface)_86%,var(--card))] p-8 text-center transition hover:border-[color-mix(in_oklab,var(--primary)_54%,var(--border))] hover:bg-[color-mix(in_oklab,var(--primary)_5%,var(--surface))] focus:outline-none focus-visible:ring-1 focus-visible:ring-[var(--primary)]",
66
66
  children: [
67
- /* @__PURE__ */ jsx(ArrowUpFromBracketIcon, { "aria-hidden": true, className: "h-8 w-8 text-slate-400 dark:text-slate-400" }),
67
+ /* @__PURE__ */ jsx("span", { className: "flex h-12 w-12 items-center justify-center rounded-lg border border-[var(--border)] bg-[var(--card)] text-[color-mix(in_oklab,var(--muted)_82%,transparent)] transition group-hover:text-[var(--primary)]", children: /* @__PURE__ */ jsx(ArrowUpFromBracketIcon, { "aria-hidden": true, className: "h-6 w-6" }) }),
68
68
  dropLabel,
69
69
  note
70
70
  ]
@@ -81,7 +81,7 @@ function UploadCard({
81
81
  animate: { opacity: 1, y: 0 },
82
82
  exit: { opacity: 0, y: -6 },
83
83
  transition: springSm,
84
- className: "flex items-center gap-3 rounded-xl border border-slate-200 bg-white/70 p-2 dark:border-white/10 dark:bg-[var(--fx-surface)]",
84
+ className: "flex items-center gap-3 rounded-lg border border-[var(--border)] bg-[var(--card)] p-2",
85
85
  children: [
86
86
  /* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1", children: [
87
87
  /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-3", children: [
@@ -91,10 +91,10 @@ function UploadCard({
91
91
  "%"
92
92
  ] })
93
93
  ] }),
94
- /* @__PURE__ */ jsx("div", { className: "mt-1 h-2 w-full overflow-hidden rounded-full bg-slate-100 dark:bg-white/10", children: /* @__PURE__ */ jsx(
94
+ /* @__PURE__ */ jsx("div", { className: "mt-1 h-2 w-full overflow-hidden rounded-full bg-[color-mix(in_oklab,var(--surface)_84%,transparent)]", children: /* @__PURE__ */ jsx(
95
95
  motion.div,
96
96
  {
97
- className: "h-full w-full origin-left bg-gradient-to-r from-emerald-500/70 to-emerald-500/20 will-change-transform",
97
+ className: "h-full w-full origin-left bg-[var(--primary)] will-change-transform",
98
98
  initial: { scaleX: 0 },
99
99
  animate: { scaleX: pct / 100 },
100
100
  transition: springSm
package/dist/index.d.mts CHANGED
@@ -92,7 +92,7 @@ export { default as ColorPicker, ColorPickerProps } from './ColorPicker.mjs';
92
92
  export { Accent, Theme, ThemeProvider, useTheme } from './theme/ThemeProvider.mjs';
93
93
  export { ThemeScript } from './theme/ThemeScript.mjs';
94
94
  export { default as ThemeToggle } from './theme/ThemeToggle.mjs';
95
- export { default as MarkdownEditor, MarkdownEditorProps } from './MarkdownEditor.mjs';
95
+ export { default as MarkdownEditor, MarkdownEditorProps, MarkdownPreview, renderMarkdown } from './MarkdownEditor.mjs';
96
96
  export { default as QuoteEditor, QuoteEditorClientOption, QuoteEditorLabels, QuoteEditorProps } from './QuoteEditor.mjs';
97
97
  export { Currency, DiscountType, QuoteDraft, QuoteLine, QuoteStatus, QuoteTotals, TaxMethod, calcDiscountAmount, calcLineDiscount, calcLineSubtotal, calcQuoteTotals, calcSubtotal, clampIsrRetRate, clampIvaRetFactor, clampMoney, clampTaxRate, fmtMoney } from './quote.mjs';
98
98
  export { default as DocumentEditor, DocumentEditorLabels, DocumentEditorProps, DocumentPreview } from './DocumentEditor.mjs';
package/dist/index.d.ts CHANGED
@@ -92,7 +92,7 @@ export { default as ColorPicker, ColorPickerProps } from './ColorPicker.js';
92
92
  export { Accent, Theme, ThemeProvider, useTheme } from './theme/ThemeProvider.js';
93
93
  export { ThemeScript } from './theme/ThemeScript.js';
94
94
  export { default as ThemeToggle } from './theme/ThemeToggle.js';
95
- export { default as MarkdownEditor, MarkdownEditorProps } from './MarkdownEditor.js';
95
+ export { default as MarkdownEditor, MarkdownEditorProps, MarkdownPreview, renderMarkdown } from './MarkdownEditor.js';
96
96
  export { default as QuoteEditor, QuoteEditorClientOption, QuoteEditorLabels, QuoteEditorProps } from './QuoteEditor.js';
97
97
  export { Currency, DiscountType, QuoteDraft, QuoteLine, QuoteStatus, QuoteTotals, TaxMethod, calcDiscountAmount, calcLineDiscount, calcLineSubtotal, calcQuoteTotals, calcSubtotal, clampIsrRetRate, clampIvaRetFactor, clampMoney, clampTaxRate, fmtMoney } from './quote.js';
98
98
  export { default as DocumentEditor, DocumentEditorLabels, DocumentEditorProps, DocumentPreview } from './DocumentEditor.js';
package/dist/index.js CHANGED
@@ -98,6 +98,7 @@ __export(index_exports, {
98
98
  LoginTools: () => import_LoginTools.default,
99
99
  Map: () => import_Map.default,
100
100
  MarkdownEditor: () => import_MarkdownEditor.default,
101
+ MarkdownPreview: () => import_MarkdownEditor.MarkdownPreview,
101
102
  MediaCard: () => import_MediaCard.default,
102
103
  MediaLibraryLayout: () => import_MediaLibraryLayout.default,
103
104
  MediaSelector: () => import_MediaSelector.default,
@@ -163,6 +164,7 @@ __export(index_exports, {
163
164
  formatNumericValue: () => import_Numeric.formatNumericValue,
164
165
  formatPercentValue: () => import_Numeric.formatPercentValue,
165
166
  generatePassword: () => import_PasswordField2.generatePassword,
167
+ renderMarkdown: () => import_MarkdownEditor.renderMarkdown,
166
168
  useCarousel: () => import_Carousel.useCarousel,
167
169
  useI18n: () => import_i18n.useI18n,
168
170
  useOmniSearchRegistry: () => import_OmniSearch2.useOmniSearchRegistry,
@@ -360,6 +362,7 @@ var import_LoginGallery = __toESM(require("./LoginGallery"));
360
362
  LoginTools,
361
363
  Map,
362
364
  MarkdownEditor,
365
+ MarkdownPreview,
363
366
  MediaCard,
364
367
  MediaLibraryLayout,
365
368
  MediaSelector,
@@ -425,6 +428,7 @@ var import_LoginGallery = __toESM(require("./LoginGallery"));
425
428
  formatNumericValue,
426
429
  formatPercentValue,
427
430
  generatePassword,
431
+ renderMarkdown,
428
432
  useCarousel,
429
433
  useI18n,
430
434
  useOmniSearchRegistry,
package/dist/index.mjs CHANGED
@@ -112,7 +112,7 @@ import { default as default88 } from "./ColorPicker.mjs";
112
112
  import { default as default89, useTheme } from "./theme/ThemeProvider.mjs";
113
113
  import { default as default90 } from "./theme/ThemeScript.mjs";
114
114
  import { default as default91 } from "./theme/ThemeToggle.mjs";
115
- import { default as default92 } from "./MarkdownEditor.mjs";
115
+ import { default as default92, MarkdownPreview, renderMarkdown } from "./MarkdownEditor.mjs";
116
116
  import { default as default93 } from "./QuoteEditor.mjs";
117
117
  export * from "./quote.mjs";
118
118
  import { default as default94, DocumentPreview } from "./DocumentEditor.mjs";
@@ -196,6 +196,7 @@ export {
196
196
  default100 as LoginTools,
197
197
  default4 as Map,
198
198
  default92 as MarkdownEditor,
199
+ MarkdownPreview,
199
200
  default32 as MediaCard,
200
201
  default33 as MediaLibraryLayout,
201
202
  default35 as MediaSelector,
@@ -261,6 +262,7 @@ export {
261
262
  formatNumericValue,
262
263
  formatPercentValue,
263
264
  generatePassword,
265
+ renderMarkdown,
264
266
  useCarousel,
265
267
  useI18n,
266
268
  useOmniSearchRegistry,
package/dist/theme.css CHANGED
@@ -46,6 +46,47 @@
46
46
  }
47
47
  }
48
48
 
49
+ @keyframes fx-chat-message {
50
+ from {
51
+ opacity: 0;
52
+ transform: translate3d(var(--fx-chat-x, 0), 8px, 0) scale(0.98);
53
+ filter: blur(2px);
54
+ }
55
+ to {
56
+ opacity: 1;
57
+ transform: translate3d(0, 0, 0) scale(1);
58
+ filter: blur(0);
59
+ }
60
+ }
61
+
62
+ @keyframes fx-chat-panel {
63
+ from {
64
+ opacity: 0;
65
+ transform: translate3d(0, 10px, 0) scale(0.98);
66
+ filter: blur(2px);
67
+ }
68
+ to {
69
+ opacity: 1;
70
+ transform: translate3d(0, 0, 0) scale(1);
71
+ filter: blur(0);
72
+ }
73
+ }
74
+
75
+ @keyframes fx-chat-badge {
76
+ 0% {
77
+ opacity: 0;
78
+ transform: translate3d(4px, -4px, 0) scale(0.7);
79
+ }
80
+ 70% {
81
+ opacity: 1;
82
+ transform: translate3d(0, 0, 0) scale(1.08);
83
+ }
84
+ 100% {
85
+ opacity: 1;
86
+ transform: translate3d(0, 0, 0) scale(1);
87
+ }
88
+ }
89
+
49
90
  .fx-skeleton {
50
91
  position: relative;
51
92
  overflow: hidden;
@@ -85,6 +126,12 @@
85
126
  .fx-skeleton[data-animate="pulse"] {
86
127
  animation: none;
87
128
  }
129
+
130
+ [class*="fx-chat-message"],
131
+ [class*="fx-chat-panel"],
132
+ [class*="fx-chat-badge"] {
133
+ animation: none !important;
134
+ }
88
135
  }
89
136
 
90
137
  /* Color picker (react-colorful) */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "framepexls-ui-lib",
3
- "version": "2.2.8",
3
+ "version": "2.2.11",
4
4
  "private": false,
5
5
  "license": "MIT",
6
6
  "description": "Componentes UI de Framepexls para React/Next.",
@@ -50,6 +50,7 @@
50
50
  "dependencies": {
51
51
  "framer-motion": "12.23.26",
52
52
  "maplibre-gl": "^5.15.0",
53
+ "mermaid": "^11.16.0",
53
54
  "qrcode": "^1.5.4",
54
55
  "react-colorful": "^5.6.1"
55
56
  },
@@ -67,11 +68,6 @@
67
68
  "tsup": "^8.1.0",
68
69
  "typescript": "^5.9.3"
69
70
  },
70
- "scripts": {
71
- "build": "tsup && node scripts/fix-esm-extensions.mjs && node scripts/copy-css.mjs",
72
- "dev": "tsup --watch",
73
- "prepare": "npm run build"
74
- },
75
71
  "repository": {
76
72
  "type": "git",
77
73
  "url": "git+https://github.com/cponce-framepexls/ui-lib.git"
@@ -80,5 +76,9 @@
80
76
  "bugs": {
81
77
  "url": "https://github.com/cponce-framepexls/ui-lib/issues"
82
78
  },
83
- "homepage": "https://github.com/cponce-framepexls/ui-lib#readme"
84
- }
79
+ "homepage": "https://github.com/cponce-framepexls/ui-lib#readme",
80
+ "scripts": {
81
+ "build": "tsup && node scripts/fix-esm-extensions.mjs && node scripts/copy-css.mjs",
82
+ "dev": "tsup --watch"
83
+ }
84
+ }