@lindaui/blocks 0.4.2 → 0.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +67 -0
- package/dist/ai-cost-card-f403ypsg.js +99 -0
- package/dist/ai-performance-card-cggm09nc.js +191 -0
- package/dist/analytics/ai-cost-card.d.ts +3 -0
- package/dist/analytics/ai-cost-card.d.ts.map +1 -0
- package/dist/analytics/ai-performance-card.d.ts +3 -0
- package/dist/analytics/ai-performance-card.d.ts.map +1 -0
- package/dist/analytics/analytics-card.d.ts +3 -0
- package/dist/analytics/analytics-card.d.ts.map +1 -0
- package/dist/analytics/analytics-skeleton.d.ts +5 -0
- package/dist/analytics/analytics-skeleton.d.ts.map +1 -0
- package/dist/analytics/entity-health-card.d.ts +3 -0
- package/dist/analytics/entity-health-card.d.ts.map +1 -0
- package/dist/analytics/status-distribution-chart.d.ts +3 -0
- package/dist/analytics/status-distribution-chart.d.ts.map +1 -0
- package/dist/analytics/time-range-picker.d.ts +3 -0
- package/dist/analytics/time-range-picker.d.ts.map +1 -0
- package/dist/analytics/top-entities-table.d.ts +3 -0
- package/dist/analytics/top-entities-table.d.ts.map +1 -0
- package/dist/analytics/types.d.ts +104 -0
- package/dist/analytics/types.d.ts.map +1 -0
- package/dist/analytics-card-bcmijpt6.js +38 -0
- package/dist/analytics-skeleton-imve37du.js +164 -0
- package/dist/analytics.d.ts +11 -0
- package/dist/analytics.d.ts.map +1 -0
- package/dist/analytics.js +8 -0
- package/dist/app-shell/drawer-focus.d.ts +8 -0
- package/dist/app-shell/drawer-focus.d.ts.map +1 -0
- package/dist/app-shell/footer.d.ts +39 -0
- package/dist/app-shell/footer.d.ts.map +1 -0
- package/dist/app-shell/logout-dialog.d.ts +6 -0
- package/dist/app-shell/logout-dialog.d.ts.map +1 -0
- package/dist/app-shell/navigation.d.ts +15 -0
- package/dist/app-shell/navigation.d.ts.map +1 -0
- package/dist/app-shell/types.d.ts +55 -0
- package/dist/app-shell/types.d.ts.map +1 -0
- package/dist/app-shell.d.ts +7 -0
- package/dist/app-shell.d.ts.map +1 -0
- package/dist/app-shell.js +254 -0
- package/dist/channel-settings.d.ts +24 -0
- package/dist/channel-settings.d.ts.map +1 -0
- package/dist/channel-settings.js +60 -0
- package/dist/chat-avatar-jdmeyf2o.js +30 -0
- package/dist/chat-layout-fwx3x26g.js +25 -0
- package/dist/chat.d.ts +22 -0
- package/dist/chat.d.ts.map +1 -0
- package/dist/chat.js +66 -0
- package/dist/command-palette.d.ts +22 -0
- package/dist/command-palette.d.ts.map +1 -0
- package/dist/command-palette.js +141 -0
- package/dist/compat-chat/chat-avatar.d.ts +16 -0
- package/dist/compat-chat/chat-avatar.d.ts.map +1 -0
- package/dist/compat-chat/chat-header.d.ts +9 -0
- package/dist/compat-chat/chat-header.d.ts.map +1 -0
- package/dist/compat-chat/chat-layout.d.ts +10 -0
- package/dist/compat-chat/chat-layout.d.ts.map +1 -0
- package/dist/compat-chat/conversation-list-item.d.ts +10 -0
- package/dist/compat-chat/conversation-list-item.d.ts.map +1 -0
- package/dist/compat-chat/conversation-list.d.ts +24 -0
- package/dist/compat-chat/conversation-list.d.ts.map +1 -0
- package/dist/compat-chat/conversation-thread.d.ts +18 -0
- package/dist/compat-chat/conversation-thread.d.ts.map +1 -0
- package/dist/compat-chat/date-divider.d.ts +5 -0
- package/dist/compat-chat/date-divider.d.ts.map +1 -0
- package/dist/compat-chat/empty-chat-view.d.ts +6 -0
- package/dist/compat-chat/empty-chat-view.d.ts.map +1 -0
- package/dist/compat-chat/image-lightbox.d.ts +7 -0
- package/dist/compat-chat/image-lightbox.d.ts.map +1 -0
- package/dist/compat-chat/message-bubble.d.ts +6 -0
- package/dist/compat-chat/message-bubble.d.ts.map +1 -0
- package/dist/compat-chat/message-composer.d.ts +11 -0
- package/dist/compat-chat/message-composer.d.ts.map +1 -0
- package/dist/compat-chat/time-divider.d.ts +5 -0
- package/dist/compat-chat/time-divider.d.ts.map +1 -0
- package/dist/compat-chat/time-grouping.d.ts +5 -0
- package/dist/compat-chat/time-grouping.d.ts.map +1 -0
- package/dist/compat-chat/types.d.ts +35 -0
- package/dist/compat-chat/types.d.ts.map +1 -0
- package/dist/compat-chat/whatsapp-audio-player.d.ts +7 -0
- package/dist/compat-chat/whatsapp-audio-player.d.ts.map +1 -0
- package/dist/compat.d.ts +9 -0
- package/dist/compat.d.ts.map +1 -0
- package/dist/compat.js +4 -0
- package/dist/connected-panel-lhwy25yk.js +132 -0
- package/dist/connection-panel/connected-panel.d.ts +26 -0
- package/dist/connection-panel/connected-panel.d.ts.map +1 -0
- package/dist/connection-panel/disconnected-panel.d.ts +12 -0
- package/dist/connection-panel/disconnected-panel.d.ts.map +1 -0
- package/dist/connection-panel/instructions.d.ts +5 -0
- package/dist/connection-panel/instructions.d.ts.map +1 -0
- package/dist/connection-panel/qr-panel.d.ts +14 -0
- package/dist/connection-panel/qr-panel.d.ts.map +1 -0
- package/dist/connection-panel/use-qr-refresh.d.ts +18 -0
- package/dist/connection-panel/use-qr-refresh.d.ts.map +1 -0
- package/dist/connection-panel.d.ts +39 -0
- package/dist/connection-panel.d.ts.map +1 -0
- package/dist/connection-panel.js +328 -0
- package/dist/conversation-list-ekq2dzca.js +133 -0
- package/dist/conversation-list-item-fy6t5af2.js +55 -0
- package/dist/conversation-thread-c60431oa.js +205 -0
- package/dist/entity-card.d.ts +17 -0
- package/dist/entity-card.d.ts.map +1 -0
- package/dist/entity-card.js +69 -0
- package/dist/entity-health-card-ggnmrtrq.js +192 -0
- package/dist/entity-switcher.d.ts +33 -0
- package/dist/entity-switcher.d.ts.map +1 -0
- package/dist/entity-switcher.js +137 -0
- package/dist/filter-bar.d.ts +41 -0
- package/dist/filter-bar.d.ts.map +1 -0
- package/dist/filter-bar.js +155 -0
- package/dist/footer-f7r5e42b.js +192 -0
- package/dist/image-lightbox-ci3arb6z.js +62 -0
- package/dist/kanban/drop-utils.d.ts +18 -0
- package/dist/kanban/drop-utils.d.ts.map +1 -0
- package/dist/kanban/navigation-buttons.d.ts +9 -0
- package/dist/kanban/navigation-buttons.d.ts.map +1 -0
- package/dist/kanban/scroll-indicators.d.ts +6 -0
- package/dist/kanban/scroll-indicators.d.ts.map +1 -0
- package/dist/kanban/use-keyboard-navigation.d.ts +4 -0
- package/dist/kanban/use-keyboard-navigation.d.ts.map +1 -0
- package/dist/kanban-board.d.ts +31 -0
- package/dist/kanban-board.d.ts.map +1 -0
- package/dist/kanban-board.js +128 -0
- package/dist/kanban-column.d.ts +13 -0
- package/dist/kanban-column.d.ts.map +1 -0
- package/dist/kanban-column.js +58 -0
- package/dist/legacy-kpi-card.d.ts +16 -0
- package/dist/legacy-kpi-card.d.ts.map +1 -0
- package/dist/legacy-kpi-card.js +4 -0
- package/dist/logout-dialog-bbv59tbi.js +93 -0
- package/dist/message-bubble-odjvhroq.js +110 -0
- package/dist/message-composer-m2n6soo7.js +59 -0
- package/dist/navigation-buttons-e77mshh0.js +65 -0
- package/dist/navigation-hotcrqoq.js +115 -0
- package/dist/save-bar.d.ts +15 -0
- package/dist/save-bar.d.ts.map +1 -0
- package/dist/save-bar.js +38 -0
- package/dist/scroll-indicators-gvxapogd.js +39 -0
- package/dist/settings-layout.d.ts +20 -0
- package/dist/settings-layout.d.ts.map +1 -0
- package/dist/settings-layout.js +56 -0
- package/dist/source-selector.d.ts +18 -0
- package/dist/source-selector.d.ts.map +1 -0
- package/dist/source-selector.js +59 -0
- package/dist/status-distribution-chart-oucedzv7.js +87 -0
- package/dist/status-filters.d.ts +12 -0
- package/dist/status-filters.d.ts.map +1 -0
- package/dist/status-filters.js +31 -0
- package/dist/stepper.d.ts +15 -0
- package/dist/stepper.d.ts.map +1 -0
- package/dist/stepper.js +42 -0
- package/dist/time-range-picker-mu38hul4.js +123 -0
- package/dist/top-entities-table-o7xzpq96.js +78 -0
- package/dist/use-keyboard-navigation-he1xmriz.js +52 -0
- package/dist/whatsapp-audio-player-nooty6jb.js +277 -0
- package/llms.txt +98 -4
- package/package.json +191 -46
- package/src/analytics/ai-cost-card.tsx +31 -0
- package/src/analytics/ai-performance-card.tsx +48 -0
- package/src/analytics/analytics-card.tsx +14 -0
- package/src/analytics/analytics-skeleton.tsx +15 -0
- package/src/analytics/entity-health-card.tsx +50 -0
- package/src/analytics/status-distribution-chart.tsx +20 -0
- package/src/analytics/time-range-picker.tsx +43 -0
- package/src/analytics/top-entities-table.tsx +24 -0
- package/src/analytics/types.ts +43 -0
- package/src/analytics.ts +10 -0
- package/src/app-shell/drawer-focus.ts +42 -0
- package/src/app-shell/footer.tsx +160 -0
- package/src/app-shell/logout-dialog.tsx +68 -0
- package/src/app-shell/navigation.tsx +147 -0
- package/src/app-shell/types.ts +57 -0
- package/src/app-shell.tsx +166 -0
- package/src/area-chart.tsx +85 -0
- package/src/audio-player.tsx +74 -0
- package/src/auth-layout.tsx +132 -0
- package/src/auth-provider.tsx +29 -0
- package/src/bar-chart.tsx +78 -0
- package/src/channel-settings.tsx +76 -0
- package/src/chart-card.tsx +45 -0
- package/src/chat-avatar.tsx +57 -0
- package/src/chat-date-divider.tsx +18 -0
- package/src/chat-header.tsx +24 -0
- package/src/chat-layout.tsx +41 -0
- package/src/chat-time-divider.tsx +20 -0
- package/src/chat-types.ts +32 -0
- package/src/chat.ts +21 -0
- package/src/command-palette.tsx +110 -0
- package/src/compat-chat/chat-avatar.tsx +23 -0
- package/src/compat-chat/chat-header.tsx +12 -0
- package/src/compat-chat/chat-layout.tsx +14 -0
- package/src/compat-chat/conversation-list-item.tsx +27 -0
- package/src/compat-chat/conversation-list.tsx +49 -0
- package/src/compat-chat/conversation-thread.tsx +92 -0
- package/src/compat-chat/date-divider.tsx +7 -0
- package/src/compat-chat/empty-chat-view.tsx +9 -0
- package/src/compat-chat/image-lightbox.tsx +22 -0
- package/src/compat-chat/message-bubble.tsx +28 -0
- package/src/compat-chat/message-composer.tsx +26 -0
- package/src/compat-chat/time-divider.tsx +5 -0
- package/src/compat-chat/time-grouping.ts +19 -0
- package/src/compat-chat/types.ts +31 -0
- package/src/compat-chat/whatsapp-audio-player.tsx +100 -0
- package/src/compat.ts +8 -0
- package/src/confirm-dialog.tsx +85 -0
- package/src/connection-panel/connected-panel.tsx +106 -0
- package/src/connection-panel/disconnected-panel.tsx +48 -0
- package/src/connection-panel/instructions.tsx +9 -0
- package/src/connection-panel/qr-panel.tsx +62 -0
- package/src/connection-panel/use-qr-refresh.ts +52 -0
- package/src/connection-panel.tsx +141 -0
- package/src/conversation-list-item.tsx +53 -0
- package/src/conversation-list.tsx +67 -0
- package/src/conversation-thread.tsx +64 -0
- package/src/detail-panel.tsx +123 -0
- package/src/document-panel.tsx +109 -0
- package/src/document-reader.tsx +29 -0
- package/src/empty-chat-view.tsx +25 -0
- package/src/entity-card.tsx +74 -0
- package/src/entity-health-card.tsx +53 -0
- package/src/entity-switcher.tsx +126 -0
- package/src/fetching-indicator.tsx +16 -0
- package/src/field-array-form.tsx +166 -0
- package/src/filter-bar.tsx +203 -0
- package/src/image-viewer.tsx +129 -0
- package/src/kanban/drop-utils.ts +31 -0
- package/src/kanban/navigation-buttons.tsx +80 -0
- package/src/kanban/scroll-indicators.tsx +47 -0
- package/src/kanban/use-keyboard-navigation.ts +43 -0
- package/src/kanban-board.tsx +160 -0
- package/src/kanban-column.tsx +62 -0
- package/src/legacy-kpi-card.tsx +38 -0
- package/src/line-chart.tsx +58 -0
- package/src/list-item.tsx +63 -0
- package/src/list-pane.tsx +143 -0
- package/src/login-form.tsx +128 -0
- package/src/message-bubble.tsx +70 -0
- package/src/message-composer.tsx +52 -0
- package/src/pie-chart.tsx +70 -0
- package/src/radar-chart.tsx +55 -0
- package/src/radial-chart.tsx +103 -0
- package/src/recording-overlay.tsx +159 -0
- package/src/save-bar.tsx +35 -0
- package/src/settings-layout.tsx +79 -0
- package/src/source-selector.tsx +71 -0
- package/src/split-workspace.tsx +74 -0
- package/src/stat-card.tsx +166 -0
- package/src/stats-grid.tsx +23 -0
- package/src/status-filters.tsx +28 -0
- package/src/stepper.tsx +47 -0
- package/src/table-pane.tsx +194 -0
- package/src/time-range-picker.tsx +89 -0
- package/src/workspace-back-button.tsx +32 -0
- package/dist/chat-types.test.d.ts +0 -2
- 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).
|
|
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
|
-
##
|
|
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?:
|
|
@@ -193,3 +276,14 @@ const config = { sales: { label: "Sales", color: "var(--chart-1)" } };
|
|
|
193
276
|
{start,end}` (ISO date strings), `onRangeChange`.
|
|
194
277
|
- `@lindaui/blocks/fetching-indicator` — small "Actualizando…" pill with a spinner,
|
|
195
278
|
for overlaying on already-loaded content (distinct from a full loading state).
|
|
279
|
+
|
|
280
|
+
## Agent integration skill
|
|
281
|
+
|
|
282
|
+
For UI implementation, use the portable `lindaui` Agent Skill from
|
|
283
|
+
https://github.com/francocarballar/lindaui/tree/main/skills/lindaui .
|
|
284
|
+
Install with `npx skills add francocarballar/lindaui --skill lindaui`.
|
|
285
|
+
Its Node CLI supports Windows, macOS and Linux and reads the consumer-installed
|
|
286
|
+
API: doctor, api, setup, theme, scaffold, activate and check. Mutation commands
|
|
287
|
+
produce plans by default. Use activate when adopting LindaUI for all UI tasks.
|
|
288
|
+
Always prefer this installed package's exports, types and llms.txt over newer
|
|
289
|
+
unpublished GitHub APIs. See the skill for setup, recipes and verification.
|