@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.
- package/LICENSE +21 -0
- package/dist/components/activity-heatmap/activity-heatmap.js +168 -0
- package/dist/components/activity-heatmap/index.js +6 -0
- package/dist/components/activity-log/activity-log.js +256 -0
- package/dist/components/activity-log/index.js +6 -0
- package/dist/components/ai-chat-input/ai-chat-input.js +107 -0
- package/dist/components/ai-chat-input/index.js +4 -0
- package/dist/components/ai-message-bubble/ai-message-bubble.js +119 -0
- package/dist/components/ai-message-bubble/index.js +6 -0
- package/dist/components/ai-source-citation/ai-source-citation.js +39 -0
- package/dist/components/ai-source-citation/index.js +6 -0
- package/dist/components/ai-streaming-text/ai-streaming-text.js +41 -0
- package/dist/components/ai-streaming-text/index.js +6 -0
- package/dist/components/ai-tool-call-display/ai-tool-call-display.js +93 -0
- package/dist/components/ai-tool-call-display/index.js +6 -0
- package/dist/components/animated-text/animated-text.js +328 -0
- package/dist/components/animated-text/index.js +4 -0
- package/dist/components/annotation/annotation.js +49 -0
- package/dist/components/annotation/index.js +8 -0
- package/dist/components/avatar-group/avatar-group.js +82 -0
- package/dist/components/avatar-group/index.js +10 -0
- package/dist/components/border-beam/border-beam.js +51 -0
- package/dist/components/border-beam/index.js +4 -0
- package/dist/components/candlestick-chart/candlestick-chart.js +215 -0
- package/dist/components/candlestick-chart/index.js +6 -0
- package/dist/components/combobox/combobox.js +130 -0
- package/dist/components/combobox/index.js +4 -0
- package/dist/components/countdown-timer/countdown-timer.js +184 -0
- package/dist/components/countdown-timer/index.js +4 -0
- package/dist/components/credit-badge/credit-badge.js +59 -0
- package/dist/components/credit-badge/index.js +6 -0
- package/dist/components/data-list/data-list.js +99 -0
- package/dist/components/data-list/index.js +16 -0
- package/dist/components/data-table/data-table.js +242 -0
- package/dist/components/data-table/index.js +6 -0
- package/dist/components/date-picker/date-picker.js +74 -0
- package/dist/components/date-picker/index.js +4 -0
- package/dist/components/file-upload/file-upload.js +227 -0
- package/dist/components/file-upload/index.js +4 -0
- package/dist/components/flashcard/flashcard.js +66 -0
- package/dist/components/flashcard/index.js +4 -0
- package/dist/components/index.js +172 -1
- package/dist/components/live-feed/index.js +4 -0
- package/dist/components/live-feed/live-feed.js +168 -0
- package/dist/components/market-treemap/index.js +6 -0
- package/dist/components/market-treemap/market-treemap.js +100 -0
- package/dist/components/marquee/index.js +4 -0
- package/dist/components/marquee/marquee.js +98 -0
- package/dist/components/metric-gauge/index.js +6 -0
- package/dist/components/metric-gauge/metric-gauge.js +213 -0
- package/dist/components/model-selector/model-selector.js +11 -2
- package/dist/components/number-input/index.js +4 -0
- package/dist/components/number-input/number-input.js +167 -0
- package/dist/components/number-ticker/index.js +4 -0
- package/dist/components/number-ticker/number-ticker.js +63 -0
- package/dist/components/order-book/index.js +6 -0
- package/dist/components/order-book/order-book.js +128 -0
- package/dist/components/password-input/index.js +4 -0
- package/dist/components/password-input/password-input.js +45 -0
- package/dist/components/plan-badge/index.js +6 -0
- package/dist/components/plan-badge/plan-badge.js +67 -0
- package/dist/components/rating/index.js +4 -0
- package/dist/components/rating/rating.js +121 -0
- package/dist/components/role-badge/index.js +6 -0
- package/dist/components/role-badge/role-badge.js +50 -0
- package/dist/components/scope-selector/index.js +6 -0
- package/dist/components/scope-selector/scope-selector.js +336 -0
- package/dist/components/severity-badge/index.js +8 -0
- package/dist/components/severity-badge/severity-badge.js +163 -0
- package/dist/components/sparkline-grid/index.js +6 -0
- package/dist/components/sparkline-grid/sparkline-grid.js +92 -0
- package/dist/components/spinner/index.js +5 -1
- package/dist/components/spinner/unicode-spinner.js +708 -0
- package/dist/components/stat-card/index.js +5 -0
- package/dist/components/stat-card/stat-card.js +102 -0
- package/dist/components/status-board/index.js +6 -0
- package/dist/components/status-board/status-board.js +138 -0
- package/dist/components/status-indicator/index.js +10 -0
- package/dist/components/status-indicator/status-indicator.js +175 -0
- package/dist/components/stepper/index.js +4 -0
- package/dist/components/stepper/stepper.js +117 -0
- package/dist/components/subscription-card/index.js +6 -0
- package/dist/components/subscription-card/subscription-card.js +161 -0
- package/dist/components/ticker-tape/index.js +6 -0
- package/dist/components/ticker-tape/ticker-tape.js +106 -0
- package/dist/components/tour/index.js +4 -0
- package/dist/components/tour/tour.js +157 -0
- package/dist/components/usage-breakdown/index.js +6 -0
- package/dist/components/usage-breakdown/usage-breakdown.js +140 -0
- package/dist/components/wallet-card/index.js +4 -0
- package/dist/components/wallet-card/wallet-card.js +115 -0
- package/dist/components/watchlist/index.js +6 -0
- package/dist/components/watchlist/watchlist.js +110 -0
- package/dist/components/world-clock-bar/index.js +6 -0
- package/dist/components/world-clock-bar/world-clock-bar.js +101 -0
- package/dist/index.d.ts +1173 -7
- package/dist/test-setup.js +19 -0
- package/package.json +45 -41
- 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,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,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,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
|
+
};
|