@lindaui/blocks 0.5.1 → 0.5.2

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.
@@ -71,9 +71,9 @@ export interface AiVsHumanData {
71
71
  humanRate: number;
72
72
  }
73
73
  export interface AiPerformanceCardProps {
74
- aiVsHuman: AiVsHumanData;
75
- avgResponseTimeMinutes: number;
76
- containmentRate: number;
74
+ aiVsHuman?: AiVsHumanData;
75
+ avgResponseTimeMinutes?: number;
76
+ containmentRate?: number;
77
77
  t: (key: string, opts?: Record<string, unknown>) => string;
78
78
  }
79
79
  export interface AiCostData {
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/analytics/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAC;AACzD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AACtD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC/C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAEvC,+EAA+E;AAC/E,MAAM,WAAW,WAAW;IAAG,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;CAAE;AAC1F,MAAM,WAAW,4BAA4B;IAAG,IAAI,EAAE,WAAW,EAAE,CAAC;IAAC,UAAU,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAC;CAAE;AAC/H,MAAM,WAAW,UAAU;IACzB,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;IACzB,KAAK,CAAC,EAAE;QAAE,KAAK,EAAE,SAAS,GAAG,SAAS,GAAG,WAAW,GAAG,SAAS,GAAG,SAAS,GAAG,QAAQ,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;IACzG,KAAK,EAAE,MAAM,CAAC;CACf;AACD,MAAM,WAAW,YAAY;IAAG,KAAK,EAAE,UAAU,EAAE,CAAC;IAAC,EAAE,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;CAAE;AAChF,MAAM,WAAW,iBAAiB;IAAG,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;CAAE;AAC5F,MAAM,WAAW,qBAAqB;IAAG,OAAO,EAAE,iBAAiB,EAAE,CAAC;IAAC,UAAU,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,YAAY,EAAE,CAAC;IAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;CAAE;AACtI,MAAM,MAAM,YAAY,GAAG,SAAS,GAAG,UAAU,GAAG,SAAS,CAAC;AAC9D,MAAM,WAAW,YAAY;IAAG,EAAE,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,YAAY,CAAC;CAAE;AAChG,MAAM,WAAW,gBAAgB;IAC/B,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC;IACzC,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC;IACtC,WAAW,EAAE,CAAC,MAAM,EAAE,YAAY,KAAK,MAAM,CAAC;IAC9C,KAAK,EAAE,MAAM,CAAC;CACf;AACD,MAAM,WAAW,qBAAqB;IAAG,QAAQ,EAAE,YAAY,EAAE,CAAC;IAAC,MAAM,EAAE,gBAAgB,CAAC;IAAC,gBAAgB,CAAC,EAAE,CAAC,MAAM,EAAE,YAAY,EAAE,QAAQ,EAAE,SAAS,KAAK,SAAS,CAAC;CAAE;AAC3K,MAAM,WAAW,aAAa;IAAG,WAAW,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,cAAc,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;CAAE;AACvJ,MAAM,WAAW,sBAAsB;IAAG,SAAS,EAAE,aAAa,CAAC;IAAC,sBAAsB,EAAE,MAAM,CAAC;IAAC,eAAe,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,MAAM,CAAC;CAAE;AAC1L,MAAM,WAAW,UAAU;IAAG,SAAS,EAAE,OAAO,CAAC;IAAC,gBAAgB,EAAE,MAAM,CAAC;IAAC,iBAAiB,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,CAAC;CAAE;AAC9H,MAAM,WAAW,eAAe;IAAG,MAAM,EAAE,UAAU,GAAG,SAAS,CAAC;IAAC,SAAS,EAAE,OAAO,CAAC;IAAC,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,MAAM,CAAC;CAAE;AACpJ,MAAM,MAAM,MAAM,GAAG,KAAK,GAAG,IAAI,GAAG,KAAK,GAAG,QAAQ,CAAC;AACrD,MAAM,WAAW,oBAAoB;IACnC,WAAW,EAAE,UAAU,CAAC,SAAS,CAAC,GAAG,IAAI,CAAC;IAC1C,mBAAmB,EAAE,CAAC,CAAC,EAAE,UAAU,CAAC,SAAS,CAAC,GAAG,IAAI,KAAK,IAAI,CAAC;IAC/D,cAAc,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;IACpC,MAAM,EAAE,MAAM,CAAC;IACf,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,MAAM,CAAC;CAC5B;AACD,MAAM,WAAW,kBAAkB;IAAG,MAAM,CAAC,EAAE,SAAS,CAAC;IAAC,QAAQ,EAAE,SAAS,CAAC;IAAC,IAAI,EAAE,UAAU,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;CAAE"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/analytics/types.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"drawer-focus.d.ts","sourceRoot":"","sources":["../../src/app-shell/drawer-focus.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,OAAO,CAAC;AAE3C,4FAA4F;AAC5F,wBAAgB,cAAc,CAAC,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,IAAI;;;uBAcvC,aAAa,CAAC,WAAW,CAAC;EAuBrD"}
1
+ {"version":3,"file":"drawer-focus.d.ts","sourceRoot":"","sources":["../../src/app-shell/drawer-focus.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,OAAO,CAAC;AAE3C,4FAA4F;AAC5F,wBAAgB,cAAc,CAAC,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,IAAI;;;uBAcvC,aAAa,CAAC,WAAW,CAAC;EAwBrD"}
package/dist/app-shell.js CHANGED
@@ -36,7 +36,8 @@ export { A as AppShellFooter } from './footer-f7r5e42b.js';
36
36
  const activeElement = event.currentTarget.ownerDocument.activeElement;
37
37
  if (event.shiftKey && activeElement === first || !event.shiftKey && activeElement === last) {
38
38
  event.preventDefault();
39
- (event.shiftKey ? last : first)?.focus();
39
+ // Matching a real activeElement to either boundary proves the list is nonempty.
40
+ (event.shiftKey ? last : first).focus();
40
41
  }
41
42
  };
42
43
  return {
@@ -106,7 +107,7 @@ const defaultLabels = {
106
107
  href: brand.href,
107
108
  renderLink: renderLink,
108
109
  children: [
109
- logo(32, cn("rounded-lg", isCollapsed ? "h-7 w-7" : "h-8 w-8") ?? ""),
110
+ logo(32, `rounded-lg ${isCollapsed ? "h-7 w-7" : "h-8 w-8"}`),
110
111
  isCollapsed ? null : /*#__PURE__*/ jsx("span", {
111
112
  className: "font-extrabold text-sm uppercase",
112
113
  children: brand.label
package/dist/chat.js CHANGED
@@ -3,9 +3,9 @@ import { jsxs, jsx } from 'react/jsx-runtime';
3
3
  export { C as ChatLayout } from './chat-layout-fwx3x26g.js';
4
4
  export { C as ConversationList } from './conversation-list-ekq2dzca.js';
5
5
  export { C as ConversationListItem } from './conversation-list-item-fy6t5af2.js';
6
- export { C as ConversationThread } from './conversation-thread-c60431oa.js';
6
+ export { C as ConversationThread } from './conversation-thread-f4ruu4kn.js';
7
7
  import { MessageCircle } from 'lucide-react';
8
- export { M as MessageBubble } from './message-bubble-odjvhroq.js';
8
+ export { M as MessageBubble } from './message-bubble-ivgt1l7t.js';
9
9
  export { M as MessageComposer } from './message-composer-m2n6soo7.js';
10
10
 
11
11
  function ChatHeader({ title, subtitle, avatar, actions }) {
@@ -1 +1 @@
1
- {"version":3,"file":"conversation-thread.d.ts","sourceRoot":"","sources":["../../src/compat-chat/conversation-thread.tsx"],"names":[],"mappings":"AAMA,OAAO,EAA4D,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAGjG,OAAO,KAAK,EAAE,wBAAwB,EAAE,cAAc,EAAE,MAAM,YAAS,CAAC;AAExE,MAAM,WAAW,uBAAuB;IACtC,mFAAmF;IACnF,eAAe,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IACzD,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,MAAM,EAAE,wBAAwB,CAAC;IACjC,QAAQ,EAAE,cAAc,EAAE,CAAC;IAC3B,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B,+EAA+E;IAC/E,cAAc,CAAC,EAAE,CAAC,QAAQ,EAAE,cAAc,EAAE,EAAE,aAAa,EAAE,CAAC,OAAO,EAAE,cAAc,KAAK,SAAS,KAAK,SAAS,CAAC;CACnH;AAED,wBAAgB,kBAAkB,CAAC,EAAE,QAAQ,EAAE,SAAiB,EAAE,OAAe,EAAE,MAAM,EAAE,eAAe,EAAE,mBAA2B,EAAE,cAAc,EAAE,EAAE,uBAAuB,+BAoEjL"}
1
+ {"version":3,"file":"conversation-thread.d.ts","sourceRoot":"","sources":["../../src/compat-chat/conversation-thread.tsx"],"names":[],"mappings":"AAMA,OAAO,EAA4D,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAGjG,OAAO,KAAK,EAAE,wBAAwB,EAAE,cAAc,EAAE,MAAM,YAAS,CAAC;AAExE,MAAM,WAAW,uBAAuB;IACtC,mFAAmF;IACnF,eAAe,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IACzD,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,MAAM,EAAE,wBAAwB,CAAC;IACjC,QAAQ,EAAE,cAAc,EAAE,CAAC;IAC3B,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B,+EAA+E;IAC/E,cAAc,CAAC,EAAE,CAAC,QAAQ,EAAE,cAAc,EAAE,EAAE,aAAa,EAAE,CAAC,OAAO,EAAE,cAAc,KAAK,SAAS,KAAK,SAAS,CAAC;CACnH;AAED,wBAAgB,kBAAkB,CAAC,EAAE,QAAQ,EAAE,SAAiB,EAAE,OAAe,EAAE,MAAM,EAAE,eAAe,EAAE,mBAA2B,EAAE,cAAc,EAAE,EAAE,uBAAuB,+BAqEjL"}
package/dist/compat.js CHANGED
@@ -1,4 +1,4 @@
1
1
  export * from '@lindaui/ui/compat';
2
2
  export { K as KPICard } from './analytics-skeleton-imve37du.js';
3
3
  export { StatusFilters } from './status-filters.js';
4
- export { D as DateDivider } from './conversation-thread-c60431oa.js';
4
+ export { D as DateDivider } from './conversation-thread-f4ruu4kn.js';
@@ -6,7 +6,7 @@ import dayjs from 'dayjs';
6
6
  import { AlertCircle } from 'lucide-react';
7
7
  import { useRef, useMemo, useCallback, useLayoutEffect, useEffect } from 'react';
8
8
  import { isToday, isYesterday } from '@lindaui/ui/compat';
9
- import { M as MessageBubble } from './message-bubble-odjvhroq.js';
9
+ import { M as MessageBubble } from './message-bubble-ivgt1l7t.js';
10
10
 
11
11
  function DateDivider({ date }) {
12
12
  return /*#__PURE__*/ jsx("div", {
@@ -71,12 +71,11 @@ function ConversationThread({ messages, isLoading = false, isError = false, labe
71
71
  if (!el || !listEl) return;
72
72
  let settleTimeout = null;
73
73
  let settled = false;
74
- const scrollNow = ()=>{
75
- el.scrollTop = el.scrollHeight;
76
- };
74
+ const scrollNow = ()=>scrollToBottom("auto");
77
75
  const scheduleSettle = ()=>{
78
76
  if (settleTimeout) clearTimeout(settleTimeout);
79
77
  settleTimeout = setTimeout(()=>{
78
+ settleTimeout = null;
80
79
  settled = true;
81
80
  hasScrolledOnLoad.current = true;
82
81
  resizeObserver.disconnect();
@@ -103,6 +102,7 @@ function ConversationThread({ messages, isLoading = false, isError = false, labe
103
102
  subtree: true
104
103
  });
105
104
  return ()=>{
105
+ settled = true;
106
106
  if (settleTimeout) clearTimeout(settleTimeout);
107
107
  resizeObserver.disconnect();
108
108
  mutationObserver.disconnect();
@@ -110,7 +110,8 @@ function ConversationThread({ messages, isLoading = false, isError = false, labe
110
110
  }, [
111
111
  isLoading,
112
112
  messages.length,
113
- firstId
113
+ firstId,
114
+ scrollToBottom
114
115
  ]);
115
116
  const messageCount = messages.length;
116
117
  const prevMessageCount = useRef(messageCount);
@@ -24,22 +24,17 @@ function FieldArrayForm({ onSubmit, options, categoryLabel = "Categoría", nameL
24
24
  children: /*#__PURE__*/ jsxs("div", {
25
25
  className: "flex flex-col gap-4 px-4 py-4 sm:px-6 sm:py-5",
26
26
  children: [
27
- /*#__PURE__*/ jsxs("div", {
28
- children: [
29
- /*#__PURE__*/ jsx("label", {
30
- className: "text-sm font-medium",
31
- children: categoryLabel
32
- }),
33
- /*#__PURE__*/ jsx(Controller, {
34
- control: control,
35
- name: "category",
36
- render: ({ field })=>/*#__PURE__*/ jsx(Select, {
37
- items: options,
38
- value: field.value,
39
- onChange: (val)=>field.onChange(val)
40
- })
41
- })
42
- ]
27
+ /*#__PURE__*/ jsx("div", {
28
+ children: /*#__PURE__*/ jsx(Controller, {
29
+ control: control,
30
+ name: "category",
31
+ render: ({ field })=>/*#__PURE__*/ jsx(Select, {
32
+ label: categoryLabel,
33
+ items: options,
34
+ value: field.value,
35
+ onChange: (val)=>field.onChange(val)
36
+ })
37
+ })
43
38
  }),
44
39
  /*#__PURE__*/ jsx(Controller, {
45
40
  control: control,
@@ -36,6 +36,7 @@ function ImageLightbox({ src, alt = "Media", className }) {
36
36
  className: "overflow-hidden",
37
37
  size: "full",
38
38
  children: /*#__PURE__*/ jsxs(Modal.Dialog, {
39
+ "aria-label": alt,
39
40
  className: "m-0 rounded-none shadow-none",
40
41
  children: [
41
42
  /*#__PURE__*/ jsx(Modal.CloseTrigger, {
@@ -1 +1 @@
1
- {"version":3,"file":"image-viewer.d.ts","sourceRoot":"","sources":["../src/image-viewer.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAGvC,MAAM,WAAW,gBAAgB;IAC/B,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,SAAS,CAAC;IACxB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,wBAAgB,WAAW,CAAC,EAC1B,GAAG,EACH,GAAQ,EACR,WAAW,EACX,OAAO,EACP,gBAAgB,EAChB,GAAO,EACP,YAAoB,EACpB,OAAW,EACX,OAAa,GACd,EAAE,gBAAgB,+BAoGlB"}
1
+ {"version":3,"file":"image-viewer.d.ts","sourceRoot":"","sources":["../src/image-viewer.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAGvC,MAAM,WAAW,gBAAgB;IAC/B,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,SAAS,CAAC;IACxB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,wBAAgB,WAAW,CAAC,EAC1B,GAAG,EACH,GAAQ,EACR,WAAW,EACX,OAAO,EACP,gBAAgB,EAChB,GAAO,EACP,YAAoB,EACpB,OAAW,EACX,OAAa,GACd,EAAE,gBAAgB,+BAsGlB"}
@@ -5,7 +5,8 @@ import { Scan, ZoomOut, ZoomIn, Frame } from 'lucide-react';
5
5
 
6
6
  function ImageViewer({ src, alt = "", annotations, caption, placeholderLabel, dim = 0, showControls = false, minZoom = 1, maxZoom = 2.6 }) {
7
7
  const [zoom, setZoom] = useState(1);
8
- const [imgOk, setImgOk] = useState(false);
8
+ const [loadedSrc, setLoadedSrc] = useState(null);
9
+ const imgOk = loadedSrc === src;
9
10
  const showPlaceholder = !src || !imgOk;
10
11
  return /*#__PURE__*/ jsxs("div", {
11
12
  className: "absolute inset-0 overflow-hidden bg-viewer-bg text-viewer-foreground",
@@ -19,13 +20,13 @@ function ImageViewer({ src, alt = "", annotations, caption, placeholderLabel, di
19
20
  src && /*#__PURE__*/ jsx("img", {
20
21
  src: src,
21
22
  alt: alt,
22
- onLoad: ()=>setImgOk(true),
23
- onError: ()=>setImgOk(false),
23
+ onLoad: ()=>setLoadedSrc(src),
24
+ onError: ()=>setLoadedSrc(null),
24
25
  className: "max-h-full max-w-full object-contain",
25
26
  style: {
26
27
  display: imgOk ? "block" : "none"
27
28
  }
28
- }),
29
+ }, src),
29
30
  showPlaceholder && /*#__PURE__*/ jsx("div", {
30
31
  className: "grid place-items-center rounded-lg border border-dashed",
31
32
  children: /*#__PURE__*/ jsxs("div", {
@@ -3,7 +3,7 @@ import { jsx, jsxs } from 'react/jsx-runtime';
3
3
  import dayjs from 'dayjs';
4
4
  import { motion } from 'framer-motion';
5
5
  import { cn } from '@lindaui/ui/cn';
6
- import { I as ImageLightbox } from './image-lightbox-ci3arb6z.js';
6
+ import { I as ImageLightbox } from './image-lightbox-vtovz1gj.js';
7
7
  import { W as WhatsAppAudioPlayer } from './whatsapp-audio-player-nooty6jb.js';
8
8
 
9
9
  function formatTime(date) {
@@ -1 +1 @@
1
- {"version":3,"file":"message-bubble.d.ts","sourceRoot":"","sources":["../src/message-bubble.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,cAAc,EAAiB,MAAM,iBAAiB,CAAC;AAErE,MAAM,WAAW,kBAAkB;IACjC,OAAO,EAAE,cAAc,CAAC;IACxB,YAAY,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;CACtC;AAUD,wBAAgB,aAAa,CAAC,EAAE,OAAO,EAAE,YAAY,EAAE,EAAE,kBAAkB,+BAkD1E"}
1
+ {"version":3,"file":"message-bubble.d.ts","sourceRoot":"","sources":["../src/message-bubble.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,cAAc,EAAiB,MAAM,iBAAiB,CAAC;AAErE,MAAM,WAAW,kBAAkB;IACjC,OAAO,EAAE,cAAc,CAAC;IACxB,YAAY,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;CACtC;AAUD,wBAAgB,aAAa,CAAC,EAAE,OAAO,EAAE,YAAY,EAAE,EAAE,kBAAkB,+BAmD1E"}
@@ -37,6 +37,7 @@ function MessageBubble({ message, onImageClick }) {
37
37
  children: [
38
38
  message.media?.kind === "image" && /*#__PURE__*/ jsx("button", {
39
39
  type: "button",
40
+ "aria-label": message.media.alt || "Abrir imagen",
40
41
  onClick: ()=>onImageClick?.(message.media.url),
41
42
  className: "-mx-1 -mt-1 mb-1 block overflow-hidden rounded-xl",
42
43
  children: /*#__PURE__*/ jsx("img", {
package/llms.txt CHANGED
@@ -97,7 +97,9 @@ StatusDatum: key/label/count/color. HealthEntity: id/name/score and status
97
97
  TimeRangePicker uses preset `24h|7d|30d|custom`, customRange
98
98
  `RangeValue<DateValue> | null`, onPresetChange/onCustomRangeChange and translation
99
99
  function `t`. This is NOT the unchanged flat `/time-range-picker` documented below.
100
- SDK mapping, tenant policy, fetching and navigation stay in the consumer.
100
+ AiPerformanceCard accepts omitted metrics: containment/response time and AI-vs-human
101
+ values default to zero. SDK mapping, tenant policy, fetching and navigation stay
102
+ in the consumer.
101
103
 
102
104
  Dayjs, @internationalized/date, @react-types/shared, motion and drag/drop are
103
105
  runtime dependencies (including public date types), not workspace-only tooling.
@@ -215,6 +217,8 @@ emptyDescription />` — auto-scrolls to the last message; groups by calendar da
215
217
  ### @lindaui/blocks/message-bubble
216
218
  Renders one `GenericMessage`: text, image (click → `onImageClick(url)`), audio (via
217
219
  `audio-player`), or pdf (link). Side-aligned; outgoing messages show a status icon.
220
+ Image buttons use the media alt text as their accessible name, falling back to
221
+ "Abrir imagen" when it is omitted or empty.
218
222
 
219
223
  ### @lindaui/blocks/message-composer
220
224
  `<MessageComposer value onValueChange onSend placeholder isSending isDisabled
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lindaui/blocks",
3
- "version": "0.5.1",
3
+ "version": "0.5.2",
4
4
  "description": "Composed UI sections (auth, lists, master-detail, charts) built on @lindaui/ui — importable npm package, not a copy-paste registry.",
5
5
  "author": "Franco Carballar <francocarballar@gmail.com>",
6
6
  "license": "MIT",
@@ -350,6 +350,7 @@
350
350
  "framer-motion": "14.0.0"
351
351
  },
352
352
  "devDependencies": {
353
+ "@vitest/coverage-v8": "5.0.3",
353
354
  "@hookform/resolvers": "^5.9.1",
354
355
  "@testing-library/jest-dom": "^7.0.1",
355
356
  "@testing-library/react": "^16.3.3",
@@ -369,14 +370,15 @@
369
370
  "vite": "^8.3.4",
370
371
  "vitest": "^5.0.3",
371
372
  "zod": "^4.6.5",
372
- "@lindaui/tokens": "0.4.1",
373
- "@lindaui/ui": "0.5.1"
373
+ "@lindaui/tokens": "0.4.2",
374
+ "@lindaui/ui": "0.5.2"
374
375
  },
375
376
  "scripts": {
376
377
  "build": "bunchee --no-dts && tsc -p tsconfig.build.json && node ../../scripts/normalize-declarations.mjs dist",
377
378
  "dev": "bunchee --watch",
378
379
  "typecheck": "tsc -p tsconfig.json --noEmit",
379
380
  "test": "vitest run",
381
+ "test:coverage": "vitest run --coverage",
380
382
  "test:watch": "vitest"
381
383
  }
382
384
  }
@@ -29,7 +29,7 @@ export interface HealthCardLabels {
29
29
  }
30
30
  export interface EntityHealthCardProps { entities: HealthEntity[]; labels: HealthCardLabels; renderEntityLink?: (entity: HealthEntity, children: ReactNode) => ReactNode; }
31
31
  export interface AiVsHumanData { aiConverted: number; aiLeads: number; aiRate: number; humanConverted: number; humanLeads: number; humanRate: number; }
32
- export interface AiPerformanceCardProps { aiVsHuman: AiVsHumanData; avgResponseTimeMinutes: number; containmentRate: number; t: (key: string, opts?: Record<string, unknown>) => string; }
32
+ export interface AiPerformanceCardProps { aiVsHuman?: AiVsHumanData; avgResponseTimeMinutes?: number; containmentRate?: number; t: (key: string, opts?: Record<string, unknown>) => string; }
33
33
  export interface AiCostData { available: boolean; conversionsCount: number; costPerConversion: number; totalCostUsd: number; }
34
34
  export interface AiCostCardProps { aiCost: AiCostData | undefined; isLoading: boolean; t: (key: string, opts?: Record<string, unknown>) => string; }
35
35
  export type Preset = "24h" | "7d" | "30d" | "custom";
@@ -35,7 +35,8 @@ export function useDrawerFocus(isOpen: boolean, onClose: () => void) {
35
35
  if ((event.shiftKey && activeElement === first)
36
36
  || (!event.shiftKey && activeElement === last)) {
37
37
  event.preventDefault();
38
- (event.shiftKey ? last : first)?.focus();
38
+ // Matching a real activeElement to either boundary proves the list is nonempty.
39
+ (event.shiftKey ? last : first)!.focus();
39
40
  }
40
41
  };
41
42
  return { drawerRef, triggerRef, onKeyDown };
package/src/app-shell.tsx CHANGED
@@ -69,7 +69,7 @@ export function AppShell({
69
69
  href={brand.href}
70
70
  renderLink={renderLink}
71
71
  >
72
- {logo(32, cn("rounded-lg", isCollapsed ? "h-7 w-7" : "h-8 w-8") ?? "")}
72
+ {logo(32, `rounded-lg ${isCollapsed ? "h-7 w-7" : "h-8 w-8"}`)}
73
73
  {isCollapsed ? null : <span className="font-extrabold text-sm uppercase">{brand.label}</span>}
74
74
  </ShellLink>
75
75
  {isCollapsed ? null : (
@@ -51,10 +51,11 @@ export function ConversationThread({ messages, isLoading = false, isError = fals
51
51
  if (!el || !listEl) return;
52
52
  let settleTimeout: ReturnType<typeof setTimeout> | null = null;
53
53
  let settled = false;
54
- const scrollNow = () => { el.scrollTop = el.scrollHeight; };
54
+ const scrollNow = () => scrollToBottom("auto");
55
55
  const scheduleSettle = () => {
56
56
  if (settleTimeout) clearTimeout(settleTimeout);
57
57
  settleTimeout = setTimeout(() => {
58
+ settleTimeout = null;
58
59
  settled = true;
59
60
  hasScrolledOnLoad.current = true;
60
61
  resizeObserver.disconnect();
@@ -67,8 +68,8 @@ export function ConversationThread({ messages, isLoading = false, isError = fals
67
68
  const mutationObserver = new MutationObserver(() => { if (!settled) { scrollNow(); scheduleSettle(); } });
68
69
  resizeObserver.observe(listEl);
69
70
  mutationObserver.observe(listEl, { childList: true, subtree: true });
70
- return () => { if (settleTimeout) clearTimeout(settleTimeout); resizeObserver.disconnect(); mutationObserver.disconnect(); };
71
- }, [isLoading, messages.length, firstId]);
71
+ return () => { settled = true; if (settleTimeout) clearTimeout(settleTimeout); resizeObserver.disconnect(); mutationObserver.disconnect(); };
72
+ }, [isLoading, messages.length, firstId, scrollToBottom]);
72
73
  const messageCount = messages.length;
73
74
  const prevMessageCount = useRef(messageCount);
74
75
  useEffect(() => {
@@ -13,7 +13,7 @@ export function ImageLightbox({ src, alt = "Media", className }: ImageLightboxPr
13
13
  <img alt={alt} className="h-44 w-full max-w-[220px] rounded-lg object-cover" height={600} loading="lazy" src={src} width={800} />
14
14
  </button>
15
15
  <Modal.Backdrop className="bg-black/80" isOpen={isOpen} onOpenChange={handleOpenChange} variant="blur">
16
- <Modal.Container className="overflow-hidden" size="full"><Modal.Dialog className="m-0 rounded-none shadow-none">
16
+ <Modal.Container className="overflow-hidden" size="full"><Modal.Dialog aria-label={alt} className="m-0 rounded-none shadow-none">
17
17
  <Modal.CloseTrigger className="top-4 right-4 z-10 rounded-full bg-black/50 text-white hover:bg-black/70" />
18
18
  <Modal.Body className="flex h-full items-center justify-center overflow-hidden p-4"><img alt={alt} className="max-h-full max-w-full rounded-lg object-contain" height={1080} src={src} width={1920} /></Modal.Body>
19
19
  </Modal.Dialog></Modal.Container>
@@ -63,12 +63,12 @@ export function FieldArrayForm({
63
63
  <div className="flex flex-col gap-4 px-4 py-4 sm:px-6 sm:py-5">
64
64
  {/* Category Select via Controller */}
65
65
  <div>
66
- <label className="text-sm font-medium">{categoryLabel}</label>
67
66
  <Controller
68
67
  control={control}
69
68
  name="category"
70
69
  render={({ field }) => (
71
70
  <Select
71
+ label={categoryLabel}
72
72
  items={options}
73
73
  value={field.value}
74
74
  onChange={(val) => field.onChange(val)}
@@ -28,7 +28,8 @@ export function ImageViewer({
28
28
  maxZoom = 2.6,
29
29
  }: ImageViewerProps) {
30
30
  const [zoom, setZoom] = useState(1);
31
- const [imgOk, setImgOk] = useState(false);
31
+ const [loadedSrc, setLoadedSrc] = useState<string | null>(null);
32
+ const imgOk = loadedSrc === src;
32
33
 
33
34
  const showPlaceholder = !src || !imgOk;
34
35
 
@@ -40,10 +41,11 @@ export function ImageViewer({
40
41
  >
41
42
  {src && (
42
43
  <img
44
+ key={src}
43
45
  src={src}
44
46
  alt={alt}
45
- onLoad={() => setImgOk(true)}
46
- onError={() => setImgOk(false)}
47
+ onLoad={() => setLoadedSrc(src)}
48
+ onError={() => setLoadedSrc(null)}
47
49
  className="max-h-full max-w-full object-contain"
48
50
  style={{ display: imgOk ? "block" : "none" }}
49
51
  />
@@ -32,6 +32,7 @@ export function MessageBubble({ message, onImageClick }: MessageBubbleProps) {
32
32
  {message.media?.kind === "image" && (
33
33
  <button
34
34
  type="button"
35
+ aria-label={message.media.alt || "Abrir imagen"}
35
36
  onClick={() => onImageClick?.(message.media!.url)}
36
37
  className="-mx-1 -mt-1 mb-1 block overflow-hidden rounded-xl"
37
38
  >