@vllnt/ui 0.1.8 → 0.1.11-canary.54f8a77

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 (99) hide show
  1. package/LICENSE +21 -0
  2. package/dist/components/activity-heatmap/activity-heatmap.js +168 -0
  3. package/dist/components/activity-heatmap/index.js +6 -0
  4. package/dist/components/activity-log/activity-log.js +256 -0
  5. package/dist/components/activity-log/index.js +6 -0
  6. package/dist/components/ai-chat-input/ai-chat-input.js +107 -0
  7. package/dist/components/ai-chat-input/index.js +4 -0
  8. package/dist/components/ai-message-bubble/ai-message-bubble.js +119 -0
  9. package/dist/components/ai-message-bubble/index.js +6 -0
  10. package/dist/components/ai-source-citation/ai-source-citation.js +39 -0
  11. package/dist/components/ai-source-citation/index.js +6 -0
  12. package/dist/components/ai-streaming-text/ai-streaming-text.js +41 -0
  13. package/dist/components/ai-streaming-text/index.js +6 -0
  14. package/dist/components/ai-tool-call-display/ai-tool-call-display.js +93 -0
  15. package/dist/components/ai-tool-call-display/index.js +6 -0
  16. package/dist/components/animated-text/animated-text.js +328 -0
  17. package/dist/components/animated-text/index.js +4 -0
  18. package/dist/components/annotation/annotation.js +49 -0
  19. package/dist/components/annotation/index.js +8 -0
  20. package/dist/components/avatar-group/avatar-group.js +82 -0
  21. package/dist/components/avatar-group/index.js +10 -0
  22. package/dist/components/border-beam/border-beam.js +51 -0
  23. package/dist/components/border-beam/index.js +4 -0
  24. package/dist/components/candlestick-chart/candlestick-chart.js +215 -0
  25. package/dist/components/candlestick-chart/index.js +6 -0
  26. package/dist/components/combobox/combobox.js +130 -0
  27. package/dist/components/combobox/index.js +4 -0
  28. package/dist/components/countdown-timer/countdown-timer.js +184 -0
  29. package/dist/components/countdown-timer/index.js +4 -0
  30. package/dist/components/credit-badge/credit-badge.js +59 -0
  31. package/dist/components/credit-badge/index.js +6 -0
  32. package/dist/components/data-list/data-list.js +99 -0
  33. package/dist/components/data-list/index.js +16 -0
  34. package/dist/components/data-table/data-table.js +242 -0
  35. package/dist/components/data-table/index.js +6 -0
  36. package/dist/components/date-picker/date-picker.js +74 -0
  37. package/dist/components/date-picker/index.js +4 -0
  38. package/dist/components/file-upload/file-upload.js +227 -0
  39. package/dist/components/file-upload/index.js +4 -0
  40. package/dist/components/flashcard/flashcard.js +66 -0
  41. package/dist/components/flashcard/index.js +4 -0
  42. package/dist/components/index.js +172 -1
  43. package/dist/components/live-feed/index.js +4 -0
  44. package/dist/components/live-feed/live-feed.js +168 -0
  45. package/dist/components/market-treemap/index.js +6 -0
  46. package/dist/components/market-treemap/market-treemap.js +100 -0
  47. package/dist/components/marquee/index.js +4 -0
  48. package/dist/components/marquee/marquee.js +98 -0
  49. package/dist/components/metric-gauge/index.js +6 -0
  50. package/dist/components/metric-gauge/metric-gauge.js +213 -0
  51. package/dist/components/model-selector/model-selector.js +11 -2
  52. package/dist/components/number-input/index.js +4 -0
  53. package/dist/components/number-input/number-input.js +167 -0
  54. package/dist/components/number-ticker/index.js +4 -0
  55. package/dist/components/number-ticker/number-ticker.js +63 -0
  56. package/dist/components/order-book/index.js +6 -0
  57. package/dist/components/order-book/order-book.js +128 -0
  58. package/dist/components/password-input/index.js +4 -0
  59. package/dist/components/password-input/password-input.js +45 -0
  60. package/dist/components/plan-badge/index.js +6 -0
  61. package/dist/components/plan-badge/plan-badge.js +67 -0
  62. package/dist/components/rating/index.js +4 -0
  63. package/dist/components/rating/rating.js +121 -0
  64. package/dist/components/role-badge/index.js +6 -0
  65. package/dist/components/role-badge/role-badge.js +50 -0
  66. package/dist/components/scope-selector/index.js +6 -0
  67. package/dist/components/scope-selector/scope-selector.js +336 -0
  68. package/dist/components/severity-badge/index.js +8 -0
  69. package/dist/components/severity-badge/severity-badge.js +163 -0
  70. package/dist/components/sparkline-grid/index.js +6 -0
  71. package/dist/components/sparkline-grid/sparkline-grid.js +92 -0
  72. package/dist/components/spinner/index.js +5 -1
  73. package/dist/components/spinner/unicode-spinner.js +708 -0
  74. package/dist/components/stat-card/index.js +5 -0
  75. package/dist/components/stat-card/stat-card.js +102 -0
  76. package/dist/components/status-board/index.js +6 -0
  77. package/dist/components/status-board/status-board.js +138 -0
  78. package/dist/components/status-indicator/index.js +10 -0
  79. package/dist/components/status-indicator/status-indicator.js +175 -0
  80. package/dist/components/stepper/index.js +4 -0
  81. package/dist/components/stepper/stepper.js +117 -0
  82. package/dist/components/subscription-card/index.js +6 -0
  83. package/dist/components/subscription-card/subscription-card.js +161 -0
  84. package/dist/components/ticker-tape/index.js +6 -0
  85. package/dist/components/ticker-tape/ticker-tape.js +106 -0
  86. package/dist/components/tour/index.js +4 -0
  87. package/dist/components/tour/tour.js +157 -0
  88. package/dist/components/usage-breakdown/index.js +6 -0
  89. package/dist/components/usage-breakdown/usage-breakdown.js +140 -0
  90. package/dist/components/wallet-card/index.js +4 -0
  91. package/dist/components/wallet-card/wallet-card.js +115 -0
  92. package/dist/components/watchlist/index.js +6 -0
  93. package/dist/components/watchlist/watchlist.js +110 -0
  94. package/dist/components/world-clock-bar/index.js +6 -0
  95. package/dist/components/world-clock-bar/world-clock-bar.js +101 -0
  96. package/dist/index.d.ts +1173 -7
  97. package/dist/test-setup.js +19 -0
  98. package/package.json +45 -41
  99. package/styles.css +55 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 vllnt
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,168 @@
1
+ import { jsx, jsxs } from "react/jsx-runtime";
2
+ import * as React from "react";
3
+ import { cn } from "../../lib/utils";
4
+ const WEEKDAY_LABELS = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"];
5
+ const VISIBLE_DAY_LABELS = /* @__PURE__ */ new Set(["Mon", "Wed", "Fri"]);
6
+ const LEVEL_CLASS_NAMES = [
7
+ "bg-muted",
8
+ "bg-emerald-500/25",
9
+ "bg-emerald-500/45",
10
+ "bg-emerald-500/65",
11
+ "bg-emerald-500"
12
+ ];
13
+ function normalizeDate(input) {
14
+ if (input instanceof Date) {
15
+ return new Date(input.getTime());
16
+ }
17
+ return new Date(input);
18
+ }
19
+ function toUtcDate(date) {
20
+ return new Date(
21
+ Date.UTC(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate())
22
+ );
23
+ }
24
+ function addUtcDays(date, days) {
25
+ return new Date(
26
+ Date.UTC(
27
+ date.getUTCFullYear(),
28
+ date.getUTCMonth(),
29
+ date.getUTCDate() + days
30
+ )
31
+ );
32
+ }
33
+ function formatDayKey(date) {
34
+ return `${date.getUTCFullYear()}-${String(date.getUTCMonth() + 1).padStart(2, "0")}-${String(date.getUTCDate()).padStart(2, "0")}`;
35
+ }
36
+ function getIntensityLevel(count, maxCount) {
37
+ if (count <= 0 || maxCount <= 0) {
38
+ return 0;
39
+ }
40
+ const ratio = count / maxCount;
41
+ if (ratio < 0.25) {
42
+ return 1;
43
+ }
44
+ if (ratio < 0.5) {
45
+ return 2;
46
+ }
47
+ if (ratio < 0.75) {
48
+ return 3;
49
+ }
50
+ return 4;
51
+ }
52
+ function getGridData(data, endDate, weeks) {
53
+ const normalizedEnd = toUtcDate(endDate);
54
+ const startDate = addUtcDays(normalizedEnd, -(weeks * 7 - 1));
55
+ const countsByDate = /* @__PURE__ */ new Map();
56
+ data.forEach((item) => {
57
+ const normalizedDate = toUtcDate(normalizeDate(item.date));
58
+ countsByDate.set(formatDayKey(normalizedDate), item.count);
59
+ });
60
+ const maxCount = Math.max(...data.map((item) => item.count), 0);
61
+ return Array.from({ length: weeks }, (_, weekIndex) => {
62
+ return Array.from({ length: 7 }, (_2, dayIndex) => {
63
+ const date = addUtcDays(startDate, weekIndex * 7 + dayIndex);
64
+ const key = formatDayKey(date);
65
+ const count = countsByDate.get(key) ?? 0;
66
+ return {
67
+ count,
68
+ date,
69
+ key,
70
+ level: getIntensityLevel(count, maxCount)
71
+ };
72
+ });
73
+ });
74
+ }
75
+ function formatMonthLabel(date) {
76
+ return new Intl.DateTimeFormat("en-US", {
77
+ month: "short",
78
+ timeZone: "UTC"
79
+ }).format(date);
80
+ }
81
+ function formatTooltip(date, count) {
82
+ const formattedDate = new Intl.DateTimeFormat("en-US", {
83
+ day: "numeric",
84
+ month: "short",
85
+ timeZone: "UTC",
86
+ year: "numeric"
87
+ }).format(date);
88
+ return `${count} activity ${count === 1 ? "event" : "events"} on ${formattedDate}`;
89
+ }
90
+ function HeatmapGrid({
91
+ gridData,
92
+ weeks
93
+ }) {
94
+ return /* @__PURE__ */ jsxs("div", { className: "min-w-[640px] space-y-3", children: [
95
+ /* @__PURE__ */ jsxs(
96
+ "div",
97
+ {
98
+ className: "grid gap-2 text-xs text-muted-foreground",
99
+ style: { gridTemplateColumns: `40px repeat(${weeks}, minmax(0, 1fr))` },
100
+ children: [
101
+ /* @__PURE__ */ jsx("span", {}),
102
+ gridData.map((week) => /* @__PURE__ */ jsx("span", { className: "text-center", children: week[0] && week[0].date.getUTCDate() <= 7 ? formatMonthLabel(week[0].date) : "" }, `month-${week[0]?.key}`))
103
+ ]
104
+ }
105
+ ),
106
+ /* @__PURE__ */ jsxs(
107
+ "div",
108
+ {
109
+ className: "grid gap-2",
110
+ style: { gridTemplateColumns: `40px repeat(${weeks}, minmax(0, 1fr))` },
111
+ children: [
112
+ /* @__PURE__ */ jsx("div", { className: "grid grid-rows-7 gap-2 pt-1 text-xs text-muted-foreground", children: WEEKDAY_LABELS.map((label) => /* @__PURE__ */ jsx("span", { className: "h-4 leading-4", children: VISIBLE_DAY_LABELS.has(label) ? label : "" }, label)) }),
113
+ gridData.map((week) => /* @__PURE__ */ jsx("div", { className: "grid grid-rows-7 gap-2", children: week.map((day) => /* @__PURE__ */ jsx(
114
+ "div",
115
+ {
116
+ className: cn(
117
+ "h-4 rounded-sm border border-border/40 transition-colors",
118
+ LEVEL_CLASS_NAMES[day.level]
119
+ ),
120
+ role: "img",
121
+ title: formatTooltip(day.date, day.count)
122
+ },
123
+ day.key
124
+ )) }, `week-${week[0]?.key}`))
125
+ ]
126
+ }
127
+ ),
128
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-end gap-2 text-xs text-muted-foreground", children: [
129
+ /* @__PURE__ */ jsx("span", { children: "Less" }),
130
+ LEVEL_CLASS_NAMES.map((className, index) => /* @__PURE__ */ jsx(
131
+ "span",
132
+ {
133
+ className: cn(
134
+ "h-3 w-3 rounded-[3px] border border-border/40",
135
+ className
136
+ )
137
+ },
138
+ `legend-${index}`
139
+ )),
140
+ /* @__PURE__ */ jsx("span", { children: "More" })
141
+ ] })
142
+ ] });
143
+ }
144
+ const ActivityHeatmap = React.forwardRef(
145
+ ({
146
+ className,
147
+ data,
148
+ description,
149
+ endDate = /* @__PURE__ */ new Date(),
150
+ title = "Activity heatmap",
151
+ weeks = 12,
152
+ ...props
153
+ }, ref) => {
154
+ const normalizedEndDate = normalizeDate(endDate);
155
+ const gridData = getGridData(data, normalizedEndDate, weeks);
156
+ return /* @__PURE__ */ jsxs("div", { className: cn("space-y-4", className), ref, ...props, children: [
157
+ /* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
158
+ /* @__PURE__ */ jsx("h2", { className: "text-lg font-semibold tracking-tight", children: title }),
159
+ description ? /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: description }) : null
160
+ ] }),
161
+ /* @__PURE__ */ jsx("div", { className: "overflow-x-auto rounded-lg border bg-card p-4 shadow-sm", children: /* @__PURE__ */ jsx(HeatmapGrid, { gridData, weeks }) })
162
+ ] });
163
+ }
164
+ );
165
+ ActivityHeatmap.displayName = "ActivityHeatmap";
166
+ export {
167
+ ActivityHeatmap
168
+ };
@@ -0,0 +1,6 @@
1
+ import {
2
+ ActivityHeatmap
3
+ } from "./activity-heatmap";
4
+ export {
5
+ ActivityHeatmap
6
+ };
@@ -0,0 +1,256 @@
1
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
2
+ import { forwardRef, useMemo, useState } from "react";
3
+ import { ArrowRight, ChevronLeft, ChevronRight } from "lucide-react";
4
+ import { cn } from "../../lib/utils";
5
+ import { Avatar, AvatarFallback } from "../avatar";
6
+ import { Badge } from "../badge";
7
+ import { Button } from "../button";
8
+ import {
9
+ Card,
10
+ CardContent,
11
+ CardDescription,
12
+ CardHeader,
13
+ CardTitle
14
+ } from "../card";
15
+ import { ScrollArea } from "../scroll-area";
16
+ import { Separator } from "../separator";
17
+ const toneConfig = {
18
+ danger: {
19
+ badgeClassName: "border-destructive/20 bg-destructive/10 text-destructive dark:text-destructive",
20
+ markerClassName: "bg-destructive"
21
+ },
22
+ default: {
23
+ badgeClassName: "border-border bg-muted text-muted-foreground",
24
+ markerClassName: "bg-primary"
25
+ },
26
+ success: {
27
+ badgeClassName: "border-emerald-500/20 bg-emerald-500/10 text-emerald-700 dark:text-emerald-300",
28
+ markerClassName: "bg-emerald-500"
29
+ },
30
+ warning: {
31
+ badgeClassName: "border-amber-500/20 bg-amber-500/10 text-amber-700 dark:text-amber-300",
32
+ markerClassName: "bg-amber-500"
33
+ }
34
+ };
35
+ function getInitials(name) {
36
+ return name.split(" ").map((segment) => segment[0]).join("").slice(0, 2).toUpperCase();
37
+ }
38
+ function buildPageNumbers(currentPage, totalPages) {
39
+ if (totalPages <= 1) return [1];
40
+ const start = Math.max(1, currentPage - 1);
41
+ const end = Math.min(totalPages, start + 2);
42
+ const normalizedStart = Math.max(1, end - 2);
43
+ return Array.from(
44
+ { length: end - normalizedStart + 1 },
45
+ (_, index) => normalizedStart + index
46
+ );
47
+ }
48
+ function ActivityLogHeader({
49
+ currentPage,
50
+ description,
51
+ title,
52
+ totalPages
53
+ }) {
54
+ return /* @__PURE__ */ jsx(CardHeader, { children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-3 sm:flex-row sm:items-start sm:justify-between", children: [
55
+ /* @__PURE__ */ jsxs("div", { children: [
56
+ /* @__PURE__ */ jsx(CardTitle, { children: title }),
57
+ description ? /* @__PURE__ */ jsx(CardDescription, { children: description }) : null
58
+ ] }),
59
+ /* @__PURE__ */ jsxs(Badge, { className: "w-fit", variant: "outline", children: [
60
+ "Page ",
61
+ currentPage,
62
+ " of ",
63
+ totalPages
64
+ ] })
65
+ ] }) });
66
+ }
67
+ function PaginationControls({
68
+ currentPage,
69
+ onPageChange,
70
+ pageNumbers,
71
+ totalPages
72
+ }) {
73
+ return /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [
74
+ /* @__PURE__ */ jsxs(
75
+ Button,
76
+ {
77
+ disabled: currentPage === 1,
78
+ onClick: () => {
79
+ onPageChange(currentPage - 1);
80
+ },
81
+ size: "sm",
82
+ variant: "outline",
83
+ children: [
84
+ /* @__PURE__ */ jsx(ChevronLeft, { className: "h-4 w-4" }),
85
+ "Previous"
86
+ ]
87
+ }
88
+ ),
89
+ pageNumbers.map((pageNumber) => /* @__PURE__ */ jsx(
90
+ Button,
91
+ {
92
+ "aria-label": `Go to page ${pageNumber}`,
93
+ onClick: () => {
94
+ onPageChange(pageNumber);
95
+ },
96
+ size: "sm",
97
+ variant: pageNumber === currentPage ? "default" : "outline",
98
+ children: pageNumber
99
+ },
100
+ pageNumber
101
+ )),
102
+ /* @__PURE__ */ jsxs(
103
+ Button,
104
+ {
105
+ disabled: currentPage === totalPages,
106
+ onClick: () => {
107
+ onPageChange(currentPage + 1);
108
+ },
109
+ size: "sm",
110
+ variant: "outline",
111
+ children: [
112
+ "Next",
113
+ /* @__PURE__ */ jsx(ChevronRight, { className: "h-4 w-4" })
114
+ ]
115
+ }
116
+ )
117
+ ] });
118
+ }
119
+ function ActivityRow({ item }) {
120
+ const palette = toneConfig[item.tone ?? "default"];
121
+ return /* @__PURE__ */ jsxs("li", { className: "relative pl-12", children: [
122
+ /* @__PURE__ */ jsx(
123
+ "span",
124
+ {
125
+ "aria-hidden": "true",
126
+ className: "absolute bottom-[-1.5rem] left-[18px] top-11 w-px bg-border last:hidden"
127
+ }
128
+ ),
129
+ /* @__PURE__ */ jsx(
130
+ "span",
131
+ {
132
+ "aria-hidden": "true",
133
+ className: cn(
134
+ "absolute left-4 top-3 h-3 w-3 rounded-full ring-4 ring-background",
135
+ palette.markerClassName
136
+ )
137
+ }
138
+ ),
139
+ /* @__PURE__ */ jsx("div", { className: "rounded-lg border bg-background/70 p-4", children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-3 sm:flex-row sm:items-start sm:justify-between", children: [
140
+ /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 items-start gap-3", children: [
141
+ /* @__PURE__ */ jsx(Avatar, { className: "h-9 w-9 border bg-muted", children: /* @__PURE__ */ jsx(AvatarFallback, { children: getInitials(item.actor) }) }),
142
+ /* @__PURE__ */ jsxs("div", { className: "min-w-0 space-y-1", children: [
143
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [
144
+ /* @__PURE__ */ jsx("span", { className: "font-medium text-foreground", children: item.actor }),
145
+ /* @__PURE__ */ jsx(ArrowRight, { className: "h-3.5 w-3.5 text-muted-foreground" }),
146
+ /* @__PURE__ */ jsx("span", { className: "text-sm text-muted-foreground", children: item.action }),
147
+ item.target ? /* @__PURE__ */ jsx("span", { className: "truncate text-sm font-medium text-foreground", children: item.target }) : null
148
+ ] }),
149
+ item.description ? /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: item.description }) : null
150
+ ] })
151
+ ] }),
152
+ /* @__PURE__ */ jsxs("div", { className: "flex shrink-0 flex-wrap items-center gap-2 sm:justify-end", children: [
153
+ item.scope ? /* @__PURE__ */ jsx(Badge, { className: palette.badgeClassName, variant: "outline", children: item.scope }) : null,
154
+ /* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground", children: item.timestamp })
155
+ ] })
156
+ ] }) })
157
+ ] });
158
+ }
159
+ function ActivityLogBody({
160
+ currentPage,
161
+ emptyMessage,
162
+ items,
163
+ onPageChange,
164
+ pageNumbers,
165
+ pageSize,
166
+ totalPages
167
+ }) {
168
+ const visibleItems = useMemo(() => {
169
+ const start = (currentPage - 1) * pageSize;
170
+ return items.slice(start, start + pageSize);
171
+ }, [currentPage, items, pageSize]);
172
+ if (items.length === 0) {
173
+ return /* @__PURE__ */ jsx("div", { className: "rounded-lg border border-dashed px-4 py-8 text-center text-sm text-muted-foreground", children: emptyMessage });
174
+ }
175
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
176
+ /* @__PURE__ */ jsx(ScrollArea, { className: "max-h-[26rem] pr-4", children: /* @__PURE__ */ jsx("ol", { className: "space-y-4 pb-2", children: visibleItems.map((item) => /* @__PURE__ */ jsx(ActivityRow, { item }, item.id)) }) }),
177
+ /* @__PURE__ */ jsx(Separator, {}),
178
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between", children: [
179
+ /* @__PURE__ */ jsxs("p", { className: "text-sm text-muted-foreground", children: [
180
+ "Showing ",
181
+ (currentPage - 1) * pageSize + 1,
182
+ " - ",
183
+ (currentPage - 1) * pageSize + visibleItems.length,
184
+ " of ",
185
+ items.length
186
+ ] }),
187
+ /* @__PURE__ */ jsx(
188
+ PaginationControls,
189
+ {
190
+ currentPage,
191
+ onPageChange,
192
+ pageNumbers,
193
+ totalPages
194
+ }
195
+ )
196
+ ] })
197
+ ] });
198
+ }
199
+ const ActivityLog = forwardRef(
200
+ ({
201
+ className,
202
+ defaultPage = 1,
203
+ description,
204
+ emptyMessage = "No activity recorded yet.",
205
+ items,
206
+ onPageChange,
207
+ page,
208
+ pageSize = 5,
209
+ title = "Activity log",
210
+ ...props
211
+ }, ref) => {
212
+ const totalPages = Math.max(1, Math.ceil(items.length / pageSize));
213
+ const [uncontrolledPage, setUncontrolledPage] = useState(defaultPage);
214
+ const currentPage = Math.min(
215
+ Math.max(page ?? uncontrolledPage, 1),
216
+ totalPages
217
+ );
218
+ const pageNumbers = useMemo(
219
+ () => buildPageNumbers(currentPage, totalPages),
220
+ [currentPage, totalPages]
221
+ );
222
+ function handlePageChange(nextPage) {
223
+ if (page === void 0) {
224
+ setUncontrolledPage(nextPage);
225
+ }
226
+ onPageChange?.(nextPage);
227
+ }
228
+ return /* @__PURE__ */ jsxs(Card, { className: cn("w-full", className), ref, ...props, children: [
229
+ /* @__PURE__ */ jsx(
230
+ ActivityLogHeader,
231
+ {
232
+ currentPage,
233
+ description,
234
+ title,
235
+ totalPages
236
+ }
237
+ ),
238
+ /* @__PURE__ */ jsx(CardContent, { className: "space-y-4", children: /* @__PURE__ */ jsx(
239
+ ActivityLogBody,
240
+ {
241
+ currentPage,
242
+ emptyMessage,
243
+ items,
244
+ onPageChange: handlePageChange,
245
+ pageNumbers,
246
+ pageSize,
247
+ totalPages
248
+ }
249
+ ) })
250
+ ] });
251
+ }
252
+ );
253
+ ActivityLog.displayName = "ActivityLog";
254
+ export {
255
+ ActivityLog
256
+ };
@@ -0,0 +1,6 @@
1
+ import {
2
+ ActivityLog
3
+ } from "./activity-log";
4
+ export {
5
+ ActivityLog
6
+ };
@@ -0,0 +1,107 @@
1
+ import { jsx, jsxs } from "react/jsx-runtime";
2
+ import { forwardRef } from "react";
3
+ import { cva } from "class-variance-authority";
4
+ import { LoaderCircle, SendHorizontal } from "lucide-react";
5
+ import { cn } from "../../lib/utils";
6
+ import { Button } from "../button";
7
+ import { Textarea } from "../textarea";
8
+ const formShellVariants = cva(
9
+ "rounded-2xl border border-border/70 bg-background shadow-sm"
10
+ );
11
+ function AIChatInputFooter({
12
+ currentLength,
13
+ helperText,
14
+ isSubmitDisabled,
15
+ isSubmitting,
16
+ maxLength,
17
+ status,
18
+ submitLabel
19
+ }) {
20
+ return /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-3 border-t border-border/60 pt-3 sm:flex-row sm:items-end sm:justify-between", children: [
21
+ /* @__PURE__ */ jsxs("div", { className: "space-y-1 text-xs text-muted-foreground", children: [
22
+ helperText ? /* @__PURE__ */ jsx("p", { children: helperText }) : null,
23
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [
24
+ status ? /* @__PURE__ */ jsx("span", { children: status }) : null,
25
+ typeof maxLength === "number" ? /* @__PURE__ */ jsxs("span", { children: [
26
+ currentLength,
27
+ "/",
28
+ maxLength
29
+ ] }) : null
30
+ ] })
31
+ ] }),
32
+ /* @__PURE__ */ jsxs(
33
+ Button,
34
+ {
35
+ className: "self-start rounded-full px-4 sm:self-auto",
36
+ disabled: isSubmitDisabled,
37
+ type: "submit",
38
+ children: [
39
+ isSubmitting ? /* @__PURE__ */ jsx(LoaderCircle, { className: "mr-2 h-4 w-4 animate-spin" }) : /* @__PURE__ */ jsx(SendHorizontal, { className: "mr-2 h-4 w-4" }),
40
+ submitLabel
41
+ ]
42
+ }
43
+ )
44
+ ] });
45
+ }
46
+ const AIChatInput = forwardRef(
47
+ ({
48
+ className,
49
+ disabled = false,
50
+ helperText,
51
+ isSubmitting = false,
52
+ onSubmit,
53
+ onValueChange,
54
+ status,
55
+ submitLabel = "Send",
56
+ textareaProps,
57
+ toolbar,
58
+ value,
59
+ ...props
60
+ }, ref) => {
61
+ const currentValue = value ?? "";
62
+ const maxLength = textareaProps?.maxLength;
63
+ const isSubmitDisabled = disabled || isSubmitting || currentValue.trim().length === 0;
64
+ return /* @__PURE__ */ jsx(
65
+ "form",
66
+ {
67
+ className: cn(formShellVariants(), "w-full p-3", className),
68
+ onSubmit,
69
+ ref,
70
+ ...props,
71
+ children: /* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
72
+ /* @__PURE__ */ jsx(
73
+ Textarea,
74
+ {
75
+ className: "min-h-[120px] resize-none rounded-xl border-0 bg-transparent px-1 py-1 shadow-none focus-visible:ring-0 focus-visible:ring-offset-0",
76
+ disabled,
77
+ onChange: (event) => {
78
+ textareaProps?.onChange?.(event);
79
+ onValueChange?.(event.target.value);
80
+ },
81
+ placeholder: "Ask a follow-up question, paste context, or describe what you need...",
82
+ value,
83
+ ...textareaProps
84
+ }
85
+ ),
86
+ toolbar ? /* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-2", children: toolbar }) : null,
87
+ /* @__PURE__ */ jsx(
88
+ AIChatInputFooter,
89
+ {
90
+ currentLength: currentValue.length,
91
+ helperText,
92
+ isSubmitDisabled,
93
+ isSubmitting,
94
+ maxLength,
95
+ status,
96
+ submitLabel
97
+ }
98
+ )
99
+ ] })
100
+ }
101
+ );
102
+ }
103
+ );
104
+ AIChatInput.displayName = "AIChatInput";
105
+ export {
106
+ AIChatInput
107
+ };
@@ -0,0 +1,4 @@
1
+ import { AIChatInput } from "./ai-chat-input";
2
+ export {
3
+ AIChatInput
4
+ };
@@ -0,0 +1,119 @@
1
+ import { jsx, jsxs } from "react/jsx-runtime";
2
+ import { forwardRef } from "react";
3
+ import { cva } from "class-variance-authority";
4
+ import { cn } from "../../lib/utils";
5
+ import { Avatar, AvatarFallback } from "../avatar";
6
+ import { Badge } from "../badge";
7
+ const bubbleVariants = cva(
8
+ "rounded-2xl border px-4 py-3 shadow-sm transition-colors",
9
+ {
10
+ defaultVariants: {
11
+ messageRole: "assistant"
12
+ },
13
+ variants: {
14
+ messageRole: {
15
+ assistant: "border-border bg-card text-card-foreground",
16
+ system: "border-border/80 bg-muted/60 text-foreground",
17
+ tool: "border-border bg-muted/40 text-foreground",
18
+ user: "border-primary/20 bg-primary/10 text-foreground"
19
+ }
20
+ }
21
+ }
22
+ );
23
+ function AIMessageMeta({
24
+ author,
25
+ isUser,
26
+ status,
27
+ timestamp
28
+ }) {
29
+ return /* @__PURE__ */ jsxs(
30
+ "div",
31
+ {
32
+ className: cn(
33
+ "flex flex-wrap items-center gap-2 text-xs text-muted-foreground",
34
+ isUser ? "justify-end" : "justify-start"
35
+ ),
36
+ children: [
37
+ author ? /* @__PURE__ */ jsx("span", { className: "font-medium text-foreground", children: author }) : null,
38
+ timestamp ? /* @__PURE__ */ jsx("span", { children: timestamp }) : null,
39
+ status ? /* @__PURE__ */ jsx(
40
+ Badge,
41
+ {
42
+ className: "rounded-full px-2 py-0 text-[10px]",
43
+ variant: "secondary",
44
+ children: status
45
+ }
46
+ ) : null
47
+ ]
48
+ }
49
+ );
50
+ }
51
+ const AIMessageBubble = forwardRef(
52
+ ({
53
+ author,
54
+ children,
55
+ className,
56
+ messageRole = "assistant",
57
+ status,
58
+ timestamp,
59
+ ...props
60
+ }, ref) => {
61
+ const resolvedMessageRole = messageRole ?? "assistant";
62
+ const isUser = resolvedMessageRole === "user";
63
+ const fallbackLabel = (author ?? resolvedMessageRole).charAt(0).toUpperCase();
64
+ return /* @__PURE__ */ jsx(
65
+ "div",
66
+ {
67
+ className: cn("flex w-full", isUser ? "justify-end" : "justify-start"),
68
+ children: /* @__PURE__ */ jsxs(
69
+ "div",
70
+ {
71
+ className: cn(
72
+ "flex w-full max-w-3xl gap-3",
73
+ isUser ? "flex-row-reverse text-right" : "flex-row"
74
+ ),
75
+ children: [
76
+ /* @__PURE__ */ jsx(Avatar, { className: "mt-0.5 h-8 w-8 border border-border/70", children: /* @__PURE__ */ jsx(AvatarFallback, { className: "bg-muted text-[11px] font-medium uppercase text-muted-foreground", children: fallbackLabel }) }),
77
+ /* @__PURE__ */ jsxs(
78
+ "div",
79
+ {
80
+ className: cn(
81
+ "min-w-0 space-y-2",
82
+ isUser ? "items-end" : "items-start"
83
+ ),
84
+ children: [
85
+ /* @__PURE__ */ jsx(
86
+ AIMessageMeta,
87
+ {
88
+ author,
89
+ isUser,
90
+ status,
91
+ timestamp
92
+ }
93
+ ),
94
+ /* @__PURE__ */ jsx(
95
+ "div",
96
+ {
97
+ className: cn(
98
+ bubbleVariants({ messageRole: resolvedMessageRole }),
99
+ className
100
+ ),
101
+ ref,
102
+ ...props,
103
+ children: /* @__PURE__ */ jsx("div", { className: "text-sm leading-6 whitespace-pre-wrap", children })
104
+ }
105
+ )
106
+ ]
107
+ }
108
+ )
109
+ ]
110
+ }
111
+ )
112
+ }
113
+ );
114
+ }
115
+ );
116
+ AIMessageBubble.displayName = "AIMessageBubble";
117
+ export {
118
+ AIMessageBubble
119
+ };