@lindaui/blocks 0.4.1 → 0.5.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.
Files changed (255) hide show
  1. package/README.md +56 -0
  2. package/dist/ai-cost-card-f403ypsg.js +99 -0
  3. package/dist/ai-performance-card-cggm09nc.js +191 -0
  4. package/dist/analytics/ai-cost-card.d.ts +3 -0
  5. package/dist/analytics/ai-cost-card.d.ts.map +1 -0
  6. package/dist/analytics/ai-performance-card.d.ts +3 -0
  7. package/dist/analytics/ai-performance-card.d.ts.map +1 -0
  8. package/dist/analytics/analytics-card.d.ts +3 -0
  9. package/dist/analytics/analytics-card.d.ts.map +1 -0
  10. package/dist/analytics/analytics-skeleton.d.ts +5 -0
  11. package/dist/analytics/analytics-skeleton.d.ts.map +1 -0
  12. package/dist/analytics/entity-health-card.d.ts +3 -0
  13. package/dist/analytics/entity-health-card.d.ts.map +1 -0
  14. package/dist/analytics/status-distribution-chart.d.ts +3 -0
  15. package/dist/analytics/status-distribution-chart.d.ts.map +1 -0
  16. package/dist/analytics/time-range-picker.d.ts +3 -0
  17. package/dist/analytics/time-range-picker.d.ts.map +1 -0
  18. package/dist/analytics/top-entities-table.d.ts +3 -0
  19. package/dist/analytics/top-entities-table.d.ts.map +1 -0
  20. package/dist/analytics/types.d.ts +104 -0
  21. package/dist/analytics/types.d.ts.map +1 -0
  22. package/dist/analytics-card-bcmijpt6.js +38 -0
  23. package/dist/analytics-skeleton-imve37du.js +164 -0
  24. package/dist/analytics.d.ts +11 -0
  25. package/dist/analytics.d.ts.map +1 -0
  26. package/dist/analytics.js +8 -0
  27. package/dist/app-shell/drawer-focus.d.ts +8 -0
  28. package/dist/app-shell/drawer-focus.d.ts.map +1 -0
  29. package/dist/app-shell/footer.d.ts +39 -0
  30. package/dist/app-shell/footer.d.ts.map +1 -0
  31. package/dist/app-shell/logout-dialog.d.ts +6 -0
  32. package/dist/app-shell/logout-dialog.d.ts.map +1 -0
  33. package/dist/app-shell/navigation.d.ts +15 -0
  34. package/dist/app-shell/navigation.d.ts.map +1 -0
  35. package/dist/app-shell/types.d.ts +55 -0
  36. package/dist/app-shell/types.d.ts.map +1 -0
  37. package/dist/app-shell.d.ts +7 -0
  38. package/dist/app-shell.d.ts.map +1 -0
  39. package/dist/app-shell.js +254 -0
  40. package/dist/channel-settings.d.ts +24 -0
  41. package/dist/channel-settings.d.ts.map +1 -0
  42. package/dist/channel-settings.js +60 -0
  43. package/dist/chat-avatar-jdmeyf2o.js +30 -0
  44. package/dist/chat-layout-fwx3x26g.js +25 -0
  45. package/dist/chat.d.ts +22 -0
  46. package/dist/chat.d.ts.map +1 -0
  47. package/dist/chat.js +66 -0
  48. package/dist/command-palette.d.ts +22 -0
  49. package/dist/command-palette.d.ts.map +1 -0
  50. package/dist/command-palette.js +141 -0
  51. package/dist/compat-chat/chat-avatar.d.ts +16 -0
  52. package/dist/compat-chat/chat-avatar.d.ts.map +1 -0
  53. package/dist/compat-chat/chat-header.d.ts +9 -0
  54. package/dist/compat-chat/chat-header.d.ts.map +1 -0
  55. package/dist/compat-chat/chat-layout.d.ts +10 -0
  56. package/dist/compat-chat/chat-layout.d.ts.map +1 -0
  57. package/dist/compat-chat/conversation-list-item.d.ts +10 -0
  58. package/dist/compat-chat/conversation-list-item.d.ts.map +1 -0
  59. package/dist/compat-chat/conversation-list.d.ts +24 -0
  60. package/dist/compat-chat/conversation-list.d.ts.map +1 -0
  61. package/dist/compat-chat/conversation-thread.d.ts +18 -0
  62. package/dist/compat-chat/conversation-thread.d.ts.map +1 -0
  63. package/dist/compat-chat/date-divider.d.ts +5 -0
  64. package/dist/compat-chat/date-divider.d.ts.map +1 -0
  65. package/dist/compat-chat/empty-chat-view.d.ts +6 -0
  66. package/dist/compat-chat/empty-chat-view.d.ts.map +1 -0
  67. package/dist/compat-chat/image-lightbox.d.ts +7 -0
  68. package/dist/compat-chat/image-lightbox.d.ts.map +1 -0
  69. package/dist/compat-chat/message-bubble.d.ts +6 -0
  70. package/dist/compat-chat/message-bubble.d.ts.map +1 -0
  71. package/dist/compat-chat/message-composer.d.ts +11 -0
  72. package/dist/compat-chat/message-composer.d.ts.map +1 -0
  73. package/dist/compat-chat/time-divider.d.ts +5 -0
  74. package/dist/compat-chat/time-divider.d.ts.map +1 -0
  75. package/dist/compat-chat/time-grouping.d.ts +5 -0
  76. package/dist/compat-chat/time-grouping.d.ts.map +1 -0
  77. package/dist/compat-chat/types.d.ts +35 -0
  78. package/dist/compat-chat/types.d.ts.map +1 -0
  79. package/dist/compat-chat/whatsapp-audio-player.d.ts +7 -0
  80. package/dist/compat-chat/whatsapp-audio-player.d.ts.map +1 -0
  81. package/dist/compat.d.ts +9 -0
  82. package/dist/compat.d.ts.map +1 -0
  83. package/dist/compat.js +4 -0
  84. package/dist/connected-panel-lhwy25yk.js +132 -0
  85. package/dist/connection-panel/connected-panel.d.ts +26 -0
  86. package/dist/connection-panel/connected-panel.d.ts.map +1 -0
  87. package/dist/connection-panel/disconnected-panel.d.ts +12 -0
  88. package/dist/connection-panel/disconnected-panel.d.ts.map +1 -0
  89. package/dist/connection-panel/instructions.d.ts +5 -0
  90. package/dist/connection-panel/instructions.d.ts.map +1 -0
  91. package/dist/connection-panel/qr-panel.d.ts +14 -0
  92. package/dist/connection-panel/qr-panel.d.ts.map +1 -0
  93. package/dist/connection-panel/use-qr-refresh.d.ts +18 -0
  94. package/dist/connection-panel/use-qr-refresh.d.ts.map +1 -0
  95. package/dist/connection-panel.d.ts +39 -0
  96. package/dist/connection-panel.d.ts.map +1 -0
  97. package/dist/connection-panel.js +328 -0
  98. package/dist/conversation-list-ekq2dzca.js +133 -0
  99. package/dist/conversation-list-item-fy6t5af2.js +55 -0
  100. package/dist/conversation-thread-c60431oa.js +205 -0
  101. package/dist/entity-card.d.ts +17 -0
  102. package/dist/entity-card.d.ts.map +1 -0
  103. package/dist/entity-card.js +69 -0
  104. package/dist/entity-health-card-ggnmrtrq.js +192 -0
  105. package/dist/entity-switcher.d.ts +33 -0
  106. package/dist/entity-switcher.d.ts.map +1 -0
  107. package/dist/entity-switcher.js +137 -0
  108. package/dist/filter-bar.d.ts +41 -0
  109. package/dist/filter-bar.d.ts.map +1 -0
  110. package/dist/filter-bar.js +155 -0
  111. package/dist/footer-f7r5e42b.js +192 -0
  112. package/dist/image-lightbox-ci3arb6z.js +62 -0
  113. package/dist/kanban/drop-utils.d.ts +18 -0
  114. package/dist/kanban/drop-utils.d.ts.map +1 -0
  115. package/dist/kanban/navigation-buttons.d.ts +9 -0
  116. package/dist/kanban/navigation-buttons.d.ts.map +1 -0
  117. package/dist/kanban/scroll-indicators.d.ts +6 -0
  118. package/dist/kanban/scroll-indicators.d.ts.map +1 -0
  119. package/dist/kanban/use-keyboard-navigation.d.ts +4 -0
  120. package/dist/kanban/use-keyboard-navigation.d.ts.map +1 -0
  121. package/dist/kanban-board.d.ts +31 -0
  122. package/dist/kanban-board.d.ts.map +1 -0
  123. package/dist/kanban-board.js +128 -0
  124. package/dist/kanban-column.d.ts +13 -0
  125. package/dist/kanban-column.d.ts.map +1 -0
  126. package/dist/kanban-column.js +58 -0
  127. package/dist/legacy-kpi-card.d.ts +16 -0
  128. package/dist/legacy-kpi-card.d.ts.map +1 -0
  129. package/dist/legacy-kpi-card.js +4 -0
  130. package/dist/logout-dialog-bbv59tbi.js +93 -0
  131. package/dist/message-bubble-odjvhroq.js +110 -0
  132. package/dist/message-composer-m2n6soo7.js +59 -0
  133. package/dist/navigation-buttons-e77mshh0.js +65 -0
  134. package/dist/navigation-hotcrqoq.js +115 -0
  135. package/dist/save-bar.d.ts +15 -0
  136. package/dist/save-bar.d.ts.map +1 -0
  137. package/dist/save-bar.js +38 -0
  138. package/dist/scroll-indicators-gvxapogd.js +39 -0
  139. package/dist/settings-layout.d.ts +20 -0
  140. package/dist/settings-layout.d.ts.map +1 -0
  141. package/dist/settings-layout.js +56 -0
  142. package/dist/source-selector.d.ts +18 -0
  143. package/dist/source-selector.d.ts.map +1 -0
  144. package/dist/source-selector.js +59 -0
  145. package/dist/status-distribution-chart-oucedzv7.js +87 -0
  146. package/dist/status-filters.d.ts +12 -0
  147. package/dist/status-filters.d.ts.map +1 -0
  148. package/dist/status-filters.js +31 -0
  149. package/dist/stepper.d.ts +15 -0
  150. package/dist/stepper.d.ts.map +1 -0
  151. package/dist/stepper.js +42 -0
  152. package/dist/time-range-picker-mu38hul4.js +123 -0
  153. package/dist/top-entities-table-o7xzpq96.js +78 -0
  154. package/dist/use-keyboard-navigation-he1xmriz.js +52 -0
  155. package/dist/whatsapp-audio-player-nooty6jb.js +277 -0
  156. package/llms.txt +87 -4
  157. package/package.json +192 -47
  158. package/src/analytics/ai-cost-card.tsx +31 -0
  159. package/src/analytics/ai-performance-card.tsx +48 -0
  160. package/src/analytics/analytics-card.tsx +14 -0
  161. package/src/analytics/analytics-skeleton.tsx +15 -0
  162. package/src/analytics/entity-health-card.tsx +50 -0
  163. package/src/analytics/status-distribution-chart.tsx +20 -0
  164. package/src/analytics/time-range-picker.tsx +43 -0
  165. package/src/analytics/top-entities-table.tsx +24 -0
  166. package/src/analytics/types.ts +43 -0
  167. package/src/analytics.ts +10 -0
  168. package/src/app-shell/drawer-focus.ts +42 -0
  169. package/src/app-shell/footer.tsx +160 -0
  170. package/src/app-shell/logout-dialog.tsx +68 -0
  171. package/src/app-shell/navigation.tsx +147 -0
  172. package/src/app-shell/types.ts +57 -0
  173. package/src/app-shell.tsx +166 -0
  174. package/src/area-chart.tsx +85 -0
  175. package/src/audio-player.tsx +74 -0
  176. package/src/auth-layout.tsx +132 -0
  177. package/src/auth-provider.tsx +29 -0
  178. package/src/bar-chart.tsx +78 -0
  179. package/src/channel-settings.tsx +76 -0
  180. package/src/chart-card.tsx +45 -0
  181. package/src/chat-avatar.tsx +57 -0
  182. package/src/chat-date-divider.tsx +18 -0
  183. package/src/chat-header.tsx +24 -0
  184. package/src/chat-layout.tsx +41 -0
  185. package/src/chat-time-divider.tsx +20 -0
  186. package/src/chat-types.ts +32 -0
  187. package/src/chat.ts +21 -0
  188. package/src/command-palette.tsx +110 -0
  189. package/src/compat-chat/chat-avatar.tsx +23 -0
  190. package/src/compat-chat/chat-header.tsx +12 -0
  191. package/src/compat-chat/chat-layout.tsx +14 -0
  192. package/src/compat-chat/conversation-list-item.tsx +27 -0
  193. package/src/compat-chat/conversation-list.tsx +49 -0
  194. package/src/compat-chat/conversation-thread.tsx +92 -0
  195. package/src/compat-chat/date-divider.tsx +7 -0
  196. package/src/compat-chat/empty-chat-view.tsx +9 -0
  197. package/src/compat-chat/image-lightbox.tsx +22 -0
  198. package/src/compat-chat/message-bubble.tsx +28 -0
  199. package/src/compat-chat/message-composer.tsx +26 -0
  200. package/src/compat-chat/time-divider.tsx +5 -0
  201. package/src/compat-chat/time-grouping.ts +19 -0
  202. package/src/compat-chat/types.ts +31 -0
  203. package/src/compat-chat/whatsapp-audio-player.tsx +100 -0
  204. package/src/compat.ts +8 -0
  205. package/src/confirm-dialog.tsx +85 -0
  206. package/src/connection-panel/connected-panel.tsx +106 -0
  207. package/src/connection-panel/disconnected-panel.tsx +48 -0
  208. package/src/connection-panel/instructions.tsx +9 -0
  209. package/src/connection-panel/qr-panel.tsx +62 -0
  210. package/src/connection-panel/use-qr-refresh.ts +52 -0
  211. package/src/connection-panel.tsx +141 -0
  212. package/src/conversation-list-item.tsx +53 -0
  213. package/src/conversation-list.tsx +67 -0
  214. package/src/conversation-thread.tsx +64 -0
  215. package/src/detail-panel.tsx +123 -0
  216. package/src/document-panel.tsx +109 -0
  217. package/src/document-reader.tsx +29 -0
  218. package/src/empty-chat-view.tsx +25 -0
  219. package/src/entity-card.tsx +74 -0
  220. package/src/entity-health-card.tsx +53 -0
  221. package/src/entity-switcher.tsx +126 -0
  222. package/src/fetching-indicator.tsx +16 -0
  223. package/src/field-array-form.tsx +166 -0
  224. package/src/filter-bar.tsx +203 -0
  225. package/src/image-viewer.tsx +129 -0
  226. package/src/kanban/drop-utils.ts +31 -0
  227. package/src/kanban/navigation-buttons.tsx +80 -0
  228. package/src/kanban/scroll-indicators.tsx +47 -0
  229. package/src/kanban/use-keyboard-navigation.ts +43 -0
  230. package/src/kanban-board.tsx +160 -0
  231. package/src/kanban-column.tsx +62 -0
  232. package/src/legacy-kpi-card.tsx +38 -0
  233. package/src/line-chart.tsx +58 -0
  234. package/src/list-item.tsx +63 -0
  235. package/src/list-pane.tsx +143 -0
  236. package/src/login-form.tsx +128 -0
  237. package/src/message-bubble.tsx +70 -0
  238. package/src/message-composer.tsx +52 -0
  239. package/src/pie-chart.tsx +70 -0
  240. package/src/radar-chart.tsx +55 -0
  241. package/src/radial-chart.tsx +103 -0
  242. package/src/recording-overlay.tsx +159 -0
  243. package/src/save-bar.tsx +35 -0
  244. package/src/settings-layout.tsx +79 -0
  245. package/src/source-selector.tsx +71 -0
  246. package/src/split-workspace.tsx +74 -0
  247. package/src/stat-card.tsx +166 -0
  248. package/src/stats-grid.tsx +23 -0
  249. package/src/status-filters.tsx +28 -0
  250. package/src/stepper.tsx +47 -0
  251. package/src/table-pane.tsx +194 -0
  252. package/src/time-range-picker.tsx +89 -0
  253. package/src/workspace-back-button.tsx +32 -0
  254. package/dist/chat-types.test.d.ts +0 -2
  255. package/dist/chat-types.test.d.ts.map +0 -1
@@ -0,0 +1,123 @@
1
+ 'use client';
2
+ import { jsxs, jsx } from 'react/jsx-runtime';
3
+ import { DateRangePicker, Label, DateField, RangeCalendar } from '@lindaui/ui/compound';
4
+ import { today, getLocalTimeZone } from '@internationalized/date';
5
+ import { memo, useState } from 'react';
6
+
7
+ const PRESETS = [
8
+ "24h",
9
+ "7d",
10
+ "30d",
11
+ "custom"
12
+ ];
13
+ const TimeRangePicker = /*#__PURE__*/ memo(({ preset, customRange, onPresetChange, onCustomRangeChange, t })=>{
14
+ const [isPickerOpen, setIsPickerOpen] = useState(false);
15
+ const maxDate = today(getLocalTimeZone());
16
+ function handlePresetChange(p) {
17
+ onPresetChange(p);
18
+ if (p === "custom") setIsPickerOpen(true);
19
+ }
20
+ const handlePresetClick = (p)=>()=>handlePresetChange(p);
21
+ return /*#__PURE__*/ jsxs("div", {
22
+ className: "flex items-center gap-3",
23
+ children: [
24
+ /*#__PURE__*/ jsx("div", {
25
+ className: "flex items-center gap-0.5 rounded-xl border border-border/60 bg-surface p-1 shadow-sm",
26
+ children: PRESETS.map((p)=>/*#__PURE__*/ jsx("button", {
27
+ className: `cursor-pointer rounded-lg px-3.5 py-1.5 font-medium text-sm transition-all duration-150 ${preset === p ? "bg-accent text-accent-foreground shadow-sm" : "text-muted hover:bg-surface-secondary hover:text-foreground"}`,
28
+ onClick: handlePresetClick(p),
29
+ type: "button",
30
+ children: t(`analytics.timeRange.${p}`)
31
+ }, p))
32
+ }),
33
+ preset === "custom" && /*#__PURE__*/ jsxs(DateRangePicker, {
34
+ className: "max-w-xs",
35
+ isOpen: isPickerOpen,
36
+ maxValue: maxDate,
37
+ onChange: onCustomRangeChange,
38
+ onOpenChange: setIsPickerOpen,
39
+ value: customRange,
40
+ children: [
41
+ /*#__PURE__*/ jsx(Label, {
42
+ className: "sr-only",
43
+ children: t("analytics.timeRange.label")
44
+ }),
45
+ /*#__PURE__*/ jsxs(DateField.Group, {
46
+ className: "shadow-sm",
47
+ children: [
48
+ /*#__PURE__*/ jsxs(DateField.InputContainer, {
49
+ children: [
50
+ /*#__PURE__*/ jsx(DateField.Input, {
51
+ slot: "start",
52
+ children: (segment)=>/*#__PURE__*/ jsx(DateField.Segment, {
53
+ segment: segment
54
+ })
55
+ }),
56
+ /*#__PURE__*/ jsx(DateRangePicker.RangeSeparator, {}),
57
+ /*#__PURE__*/ jsx(DateField.Input, {
58
+ slot: "end",
59
+ children: (segment)=>/*#__PURE__*/ jsx(DateField.Segment, {
60
+ segment: segment
61
+ })
62
+ })
63
+ ]
64
+ }),
65
+ /*#__PURE__*/ jsx(DateField.Suffix, {
66
+ children: /*#__PURE__*/ jsx(DateRangePicker.Trigger, {
67
+ children: /*#__PURE__*/ jsx(DateRangePicker.TriggerIndicator, {})
68
+ })
69
+ })
70
+ ]
71
+ }),
72
+ /*#__PURE__*/ jsx(DateRangePicker.Popover, {
73
+ children: /*#__PURE__*/ jsxs(RangeCalendar, {
74
+ "aria-label": t("analytics.timeRange.label"),
75
+ pageBehavior: "single",
76
+ children: [
77
+ /*#__PURE__*/ jsxs(RangeCalendar.Header, {
78
+ children: [
79
+ /*#__PURE__*/ jsxs(RangeCalendar.YearPickerTrigger, {
80
+ children: [
81
+ /*#__PURE__*/ jsx(RangeCalendar.YearPickerTriggerHeading, {}),
82
+ /*#__PURE__*/ jsx(RangeCalendar.YearPickerTriggerIndicator, {})
83
+ ]
84
+ }),
85
+ /*#__PURE__*/ jsx(RangeCalendar.NavButton, {
86
+ slot: "previous"
87
+ }),
88
+ /*#__PURE__*/ jsx(RangeCalendar.NavButton, {
89
+ slot: "next"
90
+ })
91
+ ]
92
+ }),
93
+ /*#__PURE__*/ jsxs(RangeCalendar.Grid, {
94
+ children: [
95
+ /*#__PURE__*/ jsx(RangeCalendar.GridHeader, {
96
+ children: (day)=>/*#__PURE__*/ jsx(RangeCalendar.HeaderCell, {
97
+ children: day
98
+ })
99
+ }),
100
+ /*#__PURE__*/ jsx(RangeCalendar.GridBody, {
101
+ children: (date)=>/*#__PURE__*/ jsx(RangeCalendar.Cell, {
102
+ date: date
103
+ })
104
+ })
105
+ ]
106
+ }),
107
+ /*#__PURE__*/ jsx(RangeCalendar.YearPickerGrid, {
108
+ children: /*#__PURE__*/ jsx(RangeCalendar.YearPickerGridBody, {
109
+ children: ({ year })=>/*#__PURE__*/ jsx(RangeCalendar.YearPickerCell, {
110
+ year: year
111
+ })
112
+ })
113
+ })
114
+ ]
115
+ })
116
+ })
117
+ ]
118
+ })
119
+ ]
120
+ });
121
+ });
122
+
123
+ export { TimeRangePicker as T };
@@ -0,0 +1,78 @@
1
+ 'use client';
2
+ import { jsxs, jsx } from 'react/jsx-runtime';
3
+ import { BarChart2 } from 'lucide-react';
4
+ import { Badge } from '@lindaui/ui/compat';
5
+
6
+ function TopEntitiesTable({ columns, rows, emptyLabel, showRank = true }) {
7
+ if (!rows.length) return /*#__PURE__*/ jsxs("div", {
8
+ className: "flex flex-col items-center justify-center gap-2 py-12",
9
+ children: [
10
+ /*#__PURE__*/ jsx(BarChart2, {
11
+ className: "h-8 w-8 text-muted"
12
+ }),
13
+ /*#__PURE__*/ jsx("p", {
14
+ className: "text-muted text-sm",
15
+ children: emptyLabel
16
+ })
17
+ ]
18
+ });
19
+ return /*#__PURE__*/ jsx("div", {
20
+ className: "overflow-x-auto",
21
+ children: /*#__PURE__*/ jsxs("table", {
22
+ className: "w-full text-sm",
23
+ children: [
24
+ /*#__PURE__*/ jsx("thead", {
25
+ children: /*#__PURE__*/ jsxs("tr", {
26
+ className: "border-border border-b",
27
+ children: [
28
+ showRank ? /*#__PURE__*/ jsx("th", {
29
+ className: "pr-3 pb-3 text-left font-medium text-muted text-xs uppercase tracking-wider",
30
+ children: "#"
31
+ }) : null,
32
+ columns.map((col)=>/*#__PURE__*/ jsx("th", {
33
+ className: `pr-3 pb-3 font-medium text-muted text-xs uppercase tracking-wider ${col.align === "right" ? "text-right" : "text-left"}`,
34
+ children: col.label
35
+ }, col.key))
36
+ ]
37
+ })
38
+ }),
39
+ /*#__PURE__*/ jsx("tbody", {
40
+ className: "divide-y divide-border",
41
+ children: rows.map((row, i)=>/*#__PURE__*/ jsxs("tr", {
42
+ className: "group transition-colors duration-100 hover:bg-surface-secondary/60",
43
+ children: [
44
+ showRank ? /*#__PURE__*/ jsx("td", {
45
+ className: "py-3 pr-3",
46
+ children: /*#__PURE__*/ jsx("span", {
47
+ className: "font-mono text-muted text-xs tabular-nums",
48
+ children: String(i + 1).padStart(2, "0")
49
+ })
50
+ }) : null,
51
+ row.cells.map((cell, ci)=>/*#__PURE__*/ jsx(Cell, {
52
+ cell: cell
53
+ }, columns[ci]?.key ?? String(ci)))
54
+ ]
55
+ }, row.id))
56
+ })
57
+ ]
58
+ })
59
+ });
60
+ }
61
+ function Cell({ cell }) {
62
+ const isRight = cell.align === "right";
63
+ if (cell.badge) return /*#__PURE__*/ jsx("td", {
64
+ className: `py-3 ${isRight ? "text-right" : "pr-3"}`,
65
+ children: /*#__PURE__*/ jsx(Badge, {
66
+ color: cell.badge.color,
67
+ size: "sm",
68
+ variant: "flat",
69
+ children: cell.badge.label
70
+ })
71
+ });
72
+ return /*#__PURE__*/ jsx("td", {
73
+ className: `py-3 pr-3 text-sm ${isRight ? "text-right text-muted tabular-nums" : "font-medium text-foreground"}`,
74
+ children: cell.value
75
+ });
76
+ }
77
+
78
+ export { TopEntitiesTable as T };
@@ -0,0 +1,52 @@
1
+ 'use client';
2
+ import { useEffect } from 'react';
3
+
4
+ /** Preserve horizontal board navigation, without intercepting text editing. */ function useKeyboardNavigation(scrollRef, columnWidth = 320) {
5
+ useEffect(()=>{
6
+ const element = scrollRef.current;
7
+ if (!element) return;
8
+ const handleKeyDown = (event)=>{
9
+ const target = event.target;
10
+ if (target.tagName === "INPUT" || target.tagName === "TEXTAREA" || target.isContentEditable) {
11
+ return;
12
+ }
13
+ switch(event.key){
14
+ case "ArrowLeft":
15
+ event.preventDefault();
16
+ element.scrollBy({
17
+ behavior: "smooth",
18
+ left: -columnWidth
19
+ });
20
+ break;
21
+ case "ArrowRight":
22
+ event.preventDefault();
23
+ element.scrollBy({
24
+ behavior: "smooth",
25
+ left: columnWidth
26
+ });
27
+ break;
28
+ case "Home":
29
+ event.preventDefault();
30
+ element.scrollTo({
31
+ behavior: "smooth",
32
+ left: 0
33
+ });
34
+ break;
35
+ case "End":
36
+ event.preventDefault();
37
+ element.scrollTo({
38
+ behavior: "smooth",
39
+ left: element.scrollWidth
40
+ });
41
+ break;
42
+ }
43
+ };
44
+ window.addEventListener("keydown", handleKeyDown);
45
+ return ()=>window.removeEventListener("keydown", handleKeyDown);
46
+ }, [
47
+ scrollRef,
48
+ columnWidth
49
+ ]);
50
+ }
51
+
52
+ export { useKeyboardNavigation as u };
@@ -0,0 +1,277 @@
1
+ 'use client';
2
+ import { jsxs, jsx } from 'react/jsx-runtime';
3
+ import { useRef, useState, useMemo, useEffect, useCallback } from 'react';
4
+
5
+ /** URL-seeded waveform: deterministic across renders and server/client. */ function generateWaveform(seed, count = 60) {
6
+ let h = 0;
7
+ for(let i = 0; i < seed.length; i += 1)h = Math.imul(31, h) + seed.charCodeAt(i) | 0;
8
+ return Array.from({
9
+ length: count
10
+ }, ()=>{
11
+ h = Math.imul(1_664_525, h) + 1_013_904_223 | 0;
12
+ return 0.12 + (h >>> 0) / 4_294_967_295 * 0.88;
13
+ });
14
+ }
15
+ function formatSeconds(s) {
16
+ if (!Number.isFinite(s) || s < 0) return "0:00";
17
+ return `${Math.floor(s / 60)}:${Math.floor(s % 60).toString().padStart(2, "0")}`;
18
+ }
19
+ function MicIcon({ color }) {
20
+ return /*#__PURE__*/ jsxs("svg", {
21
+ "aria-hidden": "true",
22
+ fill: color,
23
+ focusable: "false",
24
+ height: "18",
25
+ role: "img",
26
+ viewBox: "0 0 24 24",
27
+ width: "18",
28
+ xmlns: "http://www.w3.org/2000/svg",
29
+ children: [
30
+ /*#__PURE__*/ jsx("title", {
31
+ children: "Micrófono"
32
+ }),
33
+ /*#__PURE__*/ jsx("path", {
34
+ d: "M12 1a4 4 0 0 1 4 4v7a4 4 0 0 1-8 0V5a4 4 0 0 1 4-4zm0 2a2 2 0 0 0-2 2v7a2 2 0 1 0 4 0V5a2 2 0 0 0-2-2zm-7 8a1 1 0 0 1 .993.883L6 12a6 6 0 0 0 11.996.2L18 12a1 1 0 0 1 2 0 8 8 0 0 1-7 7.938V21h2a1 1 0 0 1 0 2H9a1 1 0 0 1 0-2h2v-1.062A8 8 0 0 1 4 12a1 1 0 0 1 1-1z"
35
+ })
36
+ ]
37
+ });
38
+ }
39
+ function PlayIcon({ color }) {
40
+ return /*#__PURE__*/ jsxs("svg", {
41
+ "aria-hidden": "true",
42
+ fill: color,
43
+ focusable: "false",
44
+ height: "16",
45
+ role: "img",
46
+ viewBox: "0 0 24 24",
47
+ width: "16",
48
+ children: [
49
+ /*#__PURE__*/ jsx("title", {
50
+ children: "Reproducir"
51
+ }),
52
+ /*#__PURE__*/ jsx("path", {
53
+ d: "M8 5.14v14l11-7-11-7z"
54
+ })
55
+ ]
56
+ });
57
+ }
58
+ function PauseIcon({ color }) {
59
+ return /*#__PURE__*/ jsxs("svg", {
60
+ "aria-hidden": "true",
61
+ fill: color,
62
+ focusable: "false",
63
+ height: "16",
64
+ role: "img",
65
+ viewBox: "0 0 24 24",
66
+ width: "16",
67
+ children: [
68
+ /*#__PURE__*/ jsx("title", {
69
+ children: "Pausar"
70
+ }),
71
+ /*#__PURE__*/ jsx("rect", {
72
+ height: "14",
73
+ rx: "1.5",
74
+ width: "4",
75
+ x: "6",
76
+ y: "5"
77
+ }),
78
+ /*#__PURE__*/ jsx("rect", {
79
+ height: "14",
80
+ rx: "1.5",
81
+ width: "4",
82
+ x: "14",
83
+ y: "5"
84
+ })
85
+ ]
86
+ });
87
+ }
88
+ function SpinnerIcon({ color }) {
89
+ return /*#__PURE__*/ jsxs("svg", {
90
+ "aria-hidden": "true",
91
+ className: "animate-spin",
92
+ fill: "none",
93
+ focusable: "false",
94
+ height: "16",
95
+ role: "img",
96
+ stroke: color,
97
+ strokeWidth: "2.5",
98
+ viewBox: "0 0 24 24",
99
+ width: "16",
100
+ children: [
101
+ /*#__PURE__*/ jsx("title", {
102
+ children: "Cargando"
103
+ }),
104
+ /*#__PURE__*/ jsx("circle", {
105
+ className: "opacity-25",
106
+ cx: "12",
107
+ cy: "12",
108
+ r: "10",
109
+ stroke: color
110
+ }),
111
+ /*#__PURE__*/ jsx("path", {
112
+ className: "opacity-75",
113
+ d: "M4 12a8 8 0 018-8",
114
+ stroke: color
115
+ })
116
+ ]
117
+ });
118
+ }
119
+ function ButtonIcon({ isLoading, isPlaying, color }) {
120
+ if (isLoading) return /*#__PURE__*/ jsx(SpinnerIcon, {
121
+ color: color
122
+ });
123
+ return isPlaying ? /*#__PURE__*/ jsx(PauseIcon, {
124
+ color: color
125
+ }) : /*#__PURE__*/ jsx(PlayIcon, {
126
+ color: color
127
+ });
128
+ }
129
+ /** Voice-note playback, buffering, duration tracking and click-to-seek. */ function WhatsAppAudioPlayer({ src, isBot = false }) {
130
+ const audioRef = useRef(null);
131
+ const [isPlaying, setIsPlaying] = useState(false);
132
+ const [progress, setProgress] = useState(0);
133
+ const [duration, setDuration] = useState(0);
134
+ const [currentTime, setCurrentTime] = useState(0);
135
+ const [isLoading, setIsLoading] = useState(false);
136
+ const bars = useMemo(()=>generateWaveform(src), [
137
+ src
138
+ ]);
139
+ useEffect(()=>{
140
+ const audio = audioRef.current;
141
+ if (!audio) return;
142
+ const onTimeUpdate = ()=>{
143
+ setCurrentTime(audio.currentTime);
144
+ setProgress(audio.duration > 0 ? audio.currentTime / audio.duration : 0);
145
+ };
146
+ const onDurationChange = ()=>{
147
+ if (Number.isFinite(audio.duration)) setDuration(audio.duration);
148
+ };
149
+ const onEnded = ()=>{
150
+ setIsPlaying(false);
151
+ setProgress(0);
152
+ setCurrentTime(0);
153
+ audio.currentTime = 0;
154
+ };
155
+ const onWaiting = ()=>setIsLoading(true);
156
+ const onCanPlay = ()=>setIsLoading(false);
157
+ const onPlay = ()=>setIsPlaying(true);
158
+ const onPause = ()=>setIsPlaying(false);
159
+ audio.addEventListener("timeupdate", onTimeUpdate);
160
+ audio.addEventListener("durationchange", onDurationChange);
161
+ audio.addEventListener("loadedmetadata", onDurationChange);
162
+ audio.addEventListener("ended", onEnded);
163
+ audio.addEventListener("waiting", onWaiting);
164
+ audio.addEventListener("canplay", onCanPlay);
165
+ audio.addEventListener("play", onPlay);
166
+ audio.addEventListener("pause", onPause);
167
+ return ()=>{
168
+ audio.removeEventListener("timeupdate", onTimeUpdate);
169
+ audio.removeEventListener("durationchange", onDurationChange);
170
+ audio.removeEventListener("loadedmetadata", onDurationChange);
171
+ audio.removeEventListener("ended", onEnded);
172
+ audio.removeEventListener("waiting", onWaiting);
173
+ audio.removeEventListener("canplay", onCanPlay);
174
+ audio.removeEventListener("play", onPlay);
175
+ audio.removeEventListener("pause", onPause);
176
+ };
177
+ }, []);
178
+ const togglePlay = useCallback(async ()=>{
179
+ const audio = audioRef.current;
180
+ if (!audio) return;
181
+ if (isPlaying) audio.pause();
182
+ else await audio.play();
183
+ }, [
184
+ isPlaying
185
+ ]);
186
+ const handleWaveformClick = useCallback((e)=>{
187
+ const audio = audioRef.current;
188
+ if (!audio || !Number.isFinite(audio.duration)) return;
189
+ const rect = e.currentTarget.getBoundingClientRect();
190
+ const ratio = Math.max(0, Math.min(1, (e.clientX - rect.left) / rect.width));
191
+ audio.currentTime = ratio * audio.duration;
192
+ setProgress(ratio);
193
+ }, []);
194
+ const colors = isBot ? {
195
+ barPlayed: "rgba(255,255,255,0.9)",
196
+ barUnplayed: "rgba(255,255,255,0.32)",
197
+ btnBg: "rgba(0,0,0,0.15)",
198
+ btnIcon: "#ffffff",
199
+ micBg: "rgba(0,0,0,0.12)",
200
+ micIcon: "rgba(255,255,255,0.85)",
201
+ timer: "rgba(255,255,255,0.7)"
202
+ } : {
203
+ barPlayed: "#00a884",
204
+ barUnplayed: "#c8d3d8",
205
+ btnBg: "#00a884",
206
+ btnIcon: "#ffffff",
207
+ micBg: "#d9fdd3",
208
+ micIcon: "#00a884",
209
+ timer: "#8696a0"
210
+ };
211
+ const displayTime = isPlaying || progress > 0 ? currentTime : duration;
212
+ return /*#__PURE__*/ jsxs("div", {
213
+ className: "flex w-full items-center gap-2 py-0.5 sm:min-w-[440px]",
214
+ children: [
215
+ /*#__PURE__*/ jsx("audio", {
216
+ preload: "metadata",
217
+ ref: audioRef,
218
+ src: src
219
+ }),
220
+ /*#__PURE__*/ jsx("div", {
221
+ "aria-hidden": "true",
222
+ className: "flex h-10 w-10 shrink-0 items-center justify-center rounded-full",
223
+ style: {
224
+ backgroundColor: colors.micBg
225
+ },
226
+ children: /*#__PURE__*/ jsx(MicIcon, {
227
+ color: colors.micIcon
228
+ })
229
+ }),
230
+ /*#__PURE__*/ jsx("button", {
231
+ "aria-label": isPlaying ? "Pausar audio" : "Reproducir audio",
232
+ className: "flex h-9 w-9 shrink-0 cursor-pointer items-center justify-center rounded-full transition-opacity hover:opacity-80 active:scale-95 focus:outline-none focus:ring-2 focus:ring-offset-1",
233
+ onClick: togglePlay,
234
+ style: {
235
+ backgroundColor: colors.btnBg
236
+ },
237
+ type: "button",
238
+ children: /*#__PURE__*/ jsx(ButtonIcon, {
239
+ color: colors.btnIcon,
240
+ isLoading: isLoading,
241
+ isPlaying: isPlaying
242
+ })
243
+ }),
244
+ /*#__PURE__*/ jsxs("div", {
245
+ className: "flex min-w-0 flex-1 flex-col gap-1",
246
+ children: [
247
+ /*#__PURE__*/ jsx("button", {
248
+ "aria-label": "Cambiar posición del audio",
249
+ className: "flex h-8 w-full items-center gap-[2px] focus:outline-none",
250
+ onClick: handleWaveformClick,
251
+ type: "button",
252
+ children: bars.map((height, i)=>/*#__PURE__*/ jsx("span", {
253
+ className: "rounded-full transition-colors duration-75",
254
+ style: {
255
+ backgroundColor: i / bars.length < progress ? colors.barPlayed : colors.barUnplayed,
256
+ flex: "1",
257
+ height: `${Math.round(height * 28)}px`,
258
+ maxHeight: "28px",
259
+ minHeight: "3px",
260
+ width: "2px"
261
+ }
262
+ }, i))
263
+ }),
264
+ /*#__PURE__*/ jsx("span", {
265
+ className: "select-none text-[10px] leading-none tabular-nums",
266
+ style: {
267
+ color: colors.timer
268
+ },
269
+ children: formatSeconds(displayTime)
270
+ })
271
+ ]
272
+ })
273
+ ]
274
+ });
275
+ }
276
+
277
+ export { WhatsAppAudioPlayer as W };
package/llms.txt CHANGED
@@ -2,7 +2,8 @@
2
2
 
3
3
  > Composed UI sections built on **@lindaui/ui** primitives — auth forms, master-detail,
4
4
  > lists, dialogs, media viewers, and charts. Importable npm package (NOT a copy-paste
5
- > registry). Each block has its OWN flat API and does not leak HeroUI/RAC types.
5
+ > registry). Existing flat APIs are unchanged; opt-in compatibility namespaces
6
+ > have separate, explicit presentation contracts.
6
7
  > This file is for coding agents: every entry, import path, and public API.
7
8
 
8
9
  All blocks are generic, controlled/mobile-first, and contain zero data-fetching, no
@@ -15,14 +16,96 @@ type of any export, read `node_modules/@lindaui/blocks/dist/<name>.d.ts`.
15
16
  pnpm add @lindaui/blocks @lindaui/ui @lindaui/tokens
16
17
  ```
17
18
 
18
- Peers you provide: `react >=19`, `react-dom >=19`, `react-hook-form >=7`, `recharts >=3`
19
- (only for chart blocks), plus `@lindaui/ui` and `@lindaui/tokens`. Import the CSS once
19
+ Peers you provide: `react >=19`, `react-dom >=19`, `react-hook-form >=7`, `lucide-react >=0.4`, `recharts >=3`
20
+ (only for chart blocks), plus `@lindaui/ui >=0.5.0` and `@lindaui/tokens >=0.4.0`. Import the CSS once
20
21
  at app root: `import "@lindaui/tokens/css";`.
21
22
 
23
+ This is the blocks 0.5.0 migration surface. Earlier UI 0.4.1 lacks `/compound`
24
+ and `/compat`; earlier tokens 0.3.1 lacks the new style entries/utility layer.
25
+ The peer floors intentionally reject those published versions.
26
+
22
27
  ## Import convention
23
28
 
24
29
  One block per import path: `@lindaui/blocks/<name>`.
25
30
 
31
+ ## New controlled application blocks
32
+
33
+ - `/app-shell`: `AppShell` takes `brand`, `navItems`, `children`, translated
34
+ `labels`, collapsed/visiting flags and sidebar/header/content slots. Inject
35
+ `renderLink`/`renderImage` for framework integration. `navItems` supply stable
36
+ keys, labels, URLs, icons, active/mobile/center-button flags and optional actions.
37
+ `AppShellFooter` is exported from the same entry, not a separate subpath.
38
+ - `/entity-switcher`: `entities`, `currentEntity`, `onSelect`, `labels`, optional
39
+ actions/avatar renderer and collapsed/visiting presentation.
40
+ - `/command-palette`: controlled `isOpen`, `items`, `onClose`, `onSelect`,
41
+ recent IDs, labels and optional avatar/empty-state renderers.
42
+ - `/connection-panel`: `state` is checking/disconnected/generating,
43
+ awaiting_scan (`qrCode` slot, optional `qrFormat`), or connected (`content`).
44
+ Supply `labels`, `onConnect`/`onRefreshQR` promises, optional error/isActive
45
+ and QR expiry. QR timers are presentation state; auth/auto-connect stays in app.
46
+ `ConnectedPanel` is exported from this entry for detail/disconnect presentation.
47
+ - `/channel-settings`: `channels`, navigationLabel, selected/defaultSelected,
48
+ onSelect and optional class names; channels supply their own content.
49
+ - `/settings-layout`: controlled `sections`, `selected`, `onSelect`, `panels`,
50
+ `navigationLabel`, optional footer/title/compact/idPrefix. Panels stay mounted.
51
+ - `/save-bar`: `dirty`, `saving`, `form` id, translated labels, optional embedded.
52
+ - `/stepper`: `value`, accessible `label`, optional stepLabel/description/complete,
53
+ `variant="steps"|"compact"`, className and formatValue. Only presents percentage;
54
+ the consumer calculates progress/completion. Preserves both original anatomies.
55
+ - `/filter-bar`: searchValue/onSearchChange, controlled selectedFilters and
56
+ selectedSort plus options/callbacks/labels. Optional renderSearchInput and
57
+ renderIconButton preserve source-specific presentations. Does not filter data.
58
+ - `/source-selector`: controlled source/options/onSourceChange, isLoading/ariaLabel.
59
+ - `/kanban-board`: entities/columns, getEntityId/getColumnId, renderCard,
60
+ onColumnChange(entityId,columnId,entity), optional renderColumn/loading/empty
61
+ content/accessibility labels. Entity/column IDs must be globally unique/stable.
62
+ - `/kanban-column`: id/title/entities/getEntityId/renderCard, optional color class
63
+ and empty content. `/entity-card`: id/entity/title, optional avatar/subtitle/
64
+ badges/time/cardClassName/isDragging/onActivate.
65
+
66
+ ## Explicit migration namespaces (never mix with flat APIs below)
67
+
68
+ ### @lindaui/blocks/compat
69
+
70
+ Re-exports `@lindaui/ui/compat` controls/utilities with their legacy props.
71
+ Adds `KPICard` (title/value/icon/color/trend/isLoading), `StatusFilters`
72
+ (availableStatuses/selectedStatuses/onStatusToggle/counts/labels/colors) and
73
+ `DateDivider`. The existing `/stat-card` and flat component APIs are untouched.
74
+
75
+ ### @lindaui/blocks/chat
76
+
77
+ Exports ChatAvatar, ChatHeader, ChatLayout, ConversationList, ConversationListItem,
78
+ ConversationThread, EmptyChatView, MessageBubble, MessageComposer and their types.
79
+ Messages: id/text/timestamp, side `left|right|center`, optional authorTag and media
80
+ `{kind:"image"|"audio"|"pdf",url,label?}`. ConversationSummary: id/title, optional
81
+ timestamp/preview/subtitle/badge/isSelected/avatarUrl/unreadCount.
82
+
83
+ ConversationList uses `items`, `searchValue`/`onSearchChange`, `onItemSelect`, labels,
84
+ optional filters/isLoading/emptyMessage/renderAvatar/renderItems. Search filtering
85
+ stays in the consumer. MessageComposer uses value/onValueChange/onSend, isSending,
86
+ `disabled` and placeholder; Enter sends, Shift+Enter inserts a newline.
87
+ Do NOT import these types from `/chat-types`: that unchanged flat entry has
88
+ incoming/outgoing sides and sentAt instead.
89
+
90
+ ### @lindaui/blocks/analytics
91
+
92
+ Exports AiCostCard, AiPerformanceCard, AnalyticsCard, AnalyticsSkeleton,
93
+ FetchingIndicator, EntityHealthCard, StatusDistributionChart, TimeRangePicker,
94
+ TopEntitiesTable and corresponding generic presentation types.
95
+ StatusDatum: key/label/count/color. HealthEntity: id/name/score and status
96
+ `healthy|at_risk|critical`; labels and link renderers are injected.
97
+ TimeRangePicker uses preset `24h|7d|30d|custom`, customRange
98
+ `RangeValue<DateValue> | null`, onPresetChange/onCustomRangeChange and translation
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.
101
+
102
+ Dayjs, @internationalized/date, @react-types/shared, motion and drag/drop are
103
+ runtime dependencies (including public date types), not workspace-only tooling.
104
+ Compatibility stories are embedded under src/compat-chat and src/analytics and
105
+ registered in Storybook; they are excluded from exports, declarations and tarballs.
106
+ For original LeadConverter styling use the separate token source
107
+ `@lindaui/tokens/lead-converter.css` after Tailwind; do not also import teal `/css`.
108
+
26
109
  ---
27
110
 
28
111
  ## Auth & forms
@@ -98,7 +181,7 @@ on a fixed dark `viewer-*` surface (for use over media); `inline` drops the abso
98
181
  positioning + forced dark bg, fills its container and inherits the panel surface with
99
182
  token-driven (legible) text. Content is identical across variants.
100
183
 
101
- ## Chat (generic — data-agnostic, WhatsApp-style presentation)
184
+ ## Existing flat chat (unchanged — distinct from /chat)
102
185
 
103
186
  Types live in `@lindaui/blocks/chat-types` (no runtime exports, just shapes):
104
187
  `GenericMessage` (`id`, `side: "incoming"|"outgoing"`, `text?`, `media?: