agentfit 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/settings.local.json +26 -0
- package/.prettierignore +7 -0
- package/.prettierrc +11 -0
- package/CONTRIBUTING.md +209 -0
- package/LICENSE +21 -0
- package/README.md +109 -0
- package/app/(dashboard)/coach/page.tsx +11 -0
- package/app/(dashboard)/commands/page.tsx +7 -0
- package/app/(dashboard)/community/[slug]/page.tsx +23 -0
- package/app/(dashboard)/community/page.tsx +71 -0
- package/app/(dashboard)/daily/page.tsx +19 -0
- package/app/(dashboard)/images/page.tsx +5 -0
- package/app/(dashboard)/layout.tsx +12 -0
- package/app/(dashboard)/page.tsx +23 -0
- package/app/(dashboard)/personality/page.tsx +11 -0
- package/app/(dashboard)/projects/page.tsx +11 -0
- package/app/(dashboard)/sessions/page.tsx +11 -0
- package/app/(dashboard)/tokens/page.tsx +11 -0
- package/app/(dashboard)/tools/page.tsx +11 -0
- package/app/api/check/route.ts +13 -0
- package/app/api/commands/route.ts +16 -0
- package/app/api/images/[...path]/route.ts +33 -0
- package/app/api/images-analysis/route.ts +177 -0
- package/app/api/sync/route.ts +14 -0
- package/app/api/usage/route.ts +117 -0
- package/app/favicon.ico +0 -0
- package/app/globals.css +144 -0
- package/app/icon.svg +3 -0
- package/app/layout.tsx +35 -0
- package/bin/agentfit.mjs +69 -0
- package/components/.gitkeep +0 -0
- package/components/agent-coach.tsx +248 -0
- package/components/app-sidebar.tsx +161 -0
- package/components/command-usage.tsx +294 -0
- package/components/daily-chart.tsx +118 -0
- package/components/daily-table.tsx +115 -0
- package/components/dashboard-shell.tsx +149 -0
- package/components/data-provider.tsx +213 -0
- package/components/fitness-score.tsx +95 -0
- package/components/overview-cards.tsx +198 -0
- package/components/pagination-controls.tsx +104 -0
- package/components/personality-fit.tsx +446 -0
- package/components/projects-table.tsx +70 -0
- package/components/screenshots-analysis.tsx +359 -0
- package/components/sessions-table.tsx +97 -0
- package/components/theme-provider.tsx +71 -0
- package/components/token-breakdown.tsx +179 -0
- package/components/tool-usage-chart.tsx +63 -0
- package/components/ui/badge.tsx +52 -0
- package/components/ui/button.tsx +60 -0
- package/components/ui/card.tsx +103 -0
- package/components/ui/chart.tsx +373 -0
- package/components/ui/dialog.tsx +160 -0
- package/components/ui/input.tsx +20 -0
- package/components/ui/scroll-area.tsx +55 -0
- package/components/ui/select.tsx +201 -0
- package/components/ui/separator.tsx +25 -0
- package/components/ui/sheet.tsx +138 -0
- package/components/ui/sidebar.tsx +723 -0
- package/components/ui/skeleton.tsx +13 -0
- package/components/ui/table.tsx +116 -0
- package/components/ui/tabs.tsx +82 -0
- package/components/ui/tooltip.tsx +66 -0
- package/components.json +25 -0
- package/generated/prisma/browser.ts +34 -0
- package/generated/prisma/client.ts +58 -0
- package/generated/prisma/commonInputTypes.ts +237 -0
- package/generated/prisma/enums.ts +15 -0
- package/generated/prisma/internal/class.ts +224 -0
- package/generated/prisma/internal/prismaNamespace.ts +920 -0
- package/generated/prisma/internal/prismaNamespaceBrowser.ts +130 -0
- package/generated/prisma/models/Image.ts +1310 -0
- package/generated/prisma/models/Session.ts +1695 -0
- package/generated/prisma/models/SyncLog.ts +1203 -0
- package/generated/prisma/models.ts +14 -0
- package/hooks/.gitkeep +0 -0
- package/hooks/use-mobile.ts +19 -0
- package/hooks/use-pagination.ts +60 -0
- package/lib/.gitkeep +0 -0
- package/lib/coach.ts +425 -0
- package/lib/commands.ts +239 -0
- package/lib/db.ts +15 -0
- package/lib/format.ts +26 -0
- package/lib/parse-codex.ts +201 -0
- package/lib/parse-logs.ts +369 -0
- package/lib/personality.ts +481 -0
- package/lib/plugins.ts +107 -0
- package/lib/pricing.ts +112 -0
- package/lib/queries-codex.ts +130 -0
- package/lib/queries.ts +154 -0
- package/lib/resolve-icon.ts +12 -0
- package/lib/sync.ts +335 -0
- package/lib/utils.ts +6 -0
- package/next.config.mjs +4 -0
- package/package.json +73 -0
- package/plugins/cost-heatmap/component.test.tsx +52 -0
- package/plugins/cost-heatmap/component.tsx +227 -0
- package/plugins/cost-heatmap/manifest.ts +13 -0
- package/plugins/index.ts +18 -0
- package/prisma/migrations/20260328152517_init/migration.sql +41 -0
- package/prisma/migrations/20260328153801_add_image_model/migration.sql +18 -0
- package/prisma/migrations/migration_lock.toml +3 -0
- package/prisma/schema.prisma +57 -0
- package/prisma.config.ts +14 -0
- package/public/.gitkeep +0 -0
- package/public/logo.svg +3 -0
- package/setup.sh +73 -0
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
Area,
|
|
5
|
+
AreaChart,
|
|
6
|
+
CartesianGrid,
|
|
7
|
+
XAxis,
|
|
8
|
+
YAxis,
|
|
9
|
+
Pie,
|
|
10
|
+
PieChart,
|
|
11
|
+
Label,
|
|
12
|
+
} from 'recharts'
|
|
13
|
+
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
|
|
14
|
+
import {
|
|
15
|
+
ChartContainer,
|
|
16
|
+
ChartTooltip,
|
|
17
|
+
ChartTooltipContent,
|
|
18
|
+
ChartLegend,
|
|
19
|
+
ChartLegendContent,
|
|
20
|
+
type ChartConfig,
|
|
21
|
+
} from '@/components/ui/chart'
|
|
22
|
+
import type { DailyUsage, OverviewStats } from '@/lib/parse-logs'
|
|
23
|
+
import { formatTokens } from '@/lib/format'
|
|
24
|
+
import { useMemo } from 'react'
|
|
25
|
+
|
|
26
|
+
const pieConfig = {
|
|
27
|
+
input: { label: 'Input', color: 'var(--chart-1)' },
|
|
28
|
+
output: { label: 'Output', color: 'var(--chart-7)' },
|
|
29
|
+
cacheWrite: { label: 'Cache Write', color: 'var(--chart-4)' },
|
|
30
|
+
cacheRead: { label: 'Cache Read', color: 'var(--chart-8)' },
|
|
31
|
+
} satisfies ChartConfig
|
|
32
|
+
|
|
33
|
+
const areaConfig = {
|
|
34
|
+
input: { label: 'Input', color: 'var(--chart-1)' },
|
|
35
|
+
output: { label: 'Output', color: 'var(--chart-7)' },
|
|
36
|
+
cacheWrite: { label: 'Cache Write', color: 'var(--chart-4)' },
|
|
37
|
+
cacheRead: { label: 'Cache Read', color: 'var(--chart-8)' },
|
|
38
|
+
} satisfies ChartConfig
|
|
39
|
+
|
|
40
|
+
export function TokenBreakdown({
|
|
41
|
+
daily,
|
|
42
|
+
overview,
|
|
43
|
+
}: {
|
|
44
|
+
daily: DailyUsage[]
|
|
45
|
+
overview: OverviewStats
|
|
46
|
+
}) {
|
|
47
|
+
const pieData = useMemo(() => [
|
|
48
|
+
{ name: 'input', value: overview.totalInputTokens, fill: 'var(--color-input)' },
|
|
49
|
+
{ name: 'output', value: overview.totalOutputTokens, fill: 'var(--color-output)' },
|
|
50
|
+
{ name: 'cacheWrite', value: overview.totalCacheCreationTokens, fill: 'var(--color-cacheWrite)' },
|
|
51
|
+
{ name: 'cacheRead', value: overview.totalCacheReadTokens, fill: 'var(--color-cacheRead)' },
|
|
52
|
+
].filter((d) => d.value > 0), [overview])
|
|
53
|
+
|
|
54
|
+
const totalTokens = useMemo(
|
|
55
|
+
() => pieData.reduce((sum, d) => sum + d.value, 0),
|
|
56
|
+
[pieData]
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
const areaData = daily.map((d) => ({
|
|
60
|
+
date: d.date.slice(5),
|
|
61
|
+
input: d.inputTokens,
|
|
62
|
+
output: d.outputTokens,
|
|
63
|
+
cacheWrite: d.cacheCreationTokens,
|
|
64
|
+
cacheRead: d.cacheReadTokens,
|
|
65
|
+
}))
|
|
66
|
+
|
|
67
|
+
return (
|
|
68
|
+
<div className="grid gap-4 lg:grid-cols-3">
|
|
69
|
+
<Card>
|
|
70
|
+
<CardHeader>
|
|
71
|
+
<CardTitle>Token Distribution</CardTitle>
|
|
72
|
+
<CardDescription>Breakdown by token type</CardDescription>
|
|
73
|
+
</CardHeader>
|
|
74
|
+
<CardContent>
|
|
75
|
+
<ChartContainer config={pieConfig} className="mx-auto aspect-square max-h-[350px]">
|
|
76
|
+
<PieChart>
|
|
77
|
+
<ChartTooltip
|
|
78
|
+
content={
|
|
79
|
+
<ChartTooltipContent
|
|
80
|
+
nameKey="name"
|
|
81
|
+
formatter={(value) => formatTokens(Number(value))}
|
|
82
|
+
/>
|
|
83
|
+
}
|
|
84
|
+
/>
|
|
85
|
+
<Pie
|
|
86
|
+
data={pieData}
|
|
87
|
+
dataKey="value"
|
|
88
|
+
nameKey="name"
|
|
89
|
+
innerRadius={60}
|
|
90
|
+
outerRadius={110}
|
|
91
|
+
paddingAngle={2}
|
|
92
|
+
strokeWidth={2}
|
|
93
|
+
>
|
|
94
|
+
<Label
|
|
95
|
+
content={({ viewBox }) => {
|
|
96
|
+
if (viewBox && 'cx' in viewBox && 'cy' in viewBox) {
|
|
97
|
+
return (
|
|
98
|
+
<text x={viewBox.cx} y={viewBox.cy} textAnchor="middle" dominantBaseline="middle">
|
|
99
|
+
<tspan x={viewBox.cx} y={viewBox.cy} className="fill-foreground text-2xl font-bold">
|
|
100
|
+
{formatTokens(totalTokens)}
|
|
101
|
+
</tspan>
|
|
102
|
+
<tspan x={viewBox.cx} y={(viewBox.cy || 0) + 20} className="fill-muted-foreground text-xs">
|
|
103
|
+
Total
|
|
104
|
+
</tspan>
|
|
105
|
+
</text>
|
|
106
|
+
)
|
|
107
|
+
}
|
|
108
|
+
}}
|
|
109
|
+
/>
|
|
110
|
+
</Pie>
|
|
111
|
+
<ChartLegend content={<ChartLegendContent nameKey="name" />} />
|
|
112
|
+
</PieChart>
|
|
113
|
+
</ChartContainer>
|
|
114
|
+
</CardContent>
|
|
115
|
+
</Card>
|
|
116
|
+
|
|
117
|
+
<Card className="lg:col-span-2">
|
|
118
|
+
<CardHeader>
|
|
119
|
+
<CardTitle>Daily Token Usage</CardTitle>
|
|
120
|
+
<CardDescription>Stacked token breakdown over time</CardDescription>
|
|
121
|
+
</CardHeader>
|
|
122
|
+
<CardContent>
|
|
123
|
+
<ChartContainer config={areaConfig} className="min-h-[300px] w-full">
|
|
124
|
+
<AreaChart data={areaData} accessibilityLayer>
|
|
125
|
+
<CartesianGrid vertical={false} />
|
|
126
|
+
<XAxis dataKey="date" tickLine={false} axisLine={false} tickMargin={8} />
|
|
127
|
+
<YAxis
|
|
128
|
+
tickLine={false}
|
|
129
|
+
axisLine={false}
|
|
130
|
+
tickFormatter={(v) => formatTokens(v)}
|
|
131
|
+
/>
|
|
132
|
+
<ChartTooltip
|
|
133
|
+
content={
|
|
134
|
+
<ChartTooltipContent
|
|
135
|
+
indicator="line"
|
|
136
|
+
formatter={(value) => formatTokens(Number(value))}
|
|
137
|
+
/>
|
|
138
|
+
}
|
|
139
|
+
/>
|
|
140
|
+
<ChartLegend content={<ChartLegendContent />} />
|
|
141
|
+
<Area
|
|
142
|
+
type="monotone"
|
|
143
|
+
dataKey="cacheRead"
|
|
144
|
+
stackId="1"
|
|
145
|
+
fill="var(--color-cacheRead)"
|
|
146
|
+
stroke="var(--color-cacheRead)"
|
|
147
|
+
fillOpacity={0.4}
|
|
148
|
+
/>
|
|
149
|
+
<Area
|
|
150
|
+
type="monotone"
|
|
151
|
+
dataKey="cacheWrite"
|
|
152
|
+
stackId="1"
|
|
153
|
+
fill="var(--color-cacheWrite)"
|
|
154
|
+
stroke="var(--color-cacheWrite)"
|
|
155
|
+
fillOpacity={0.4}
|
|
156
|
+
/>
|
|
157
|
+
<Area
|
|
158
|
+
type="monotone"
|
|
159
|
+
dataKey="output"
|
|
160
|
+
stackId="1"
|
|
161
|
+
fill="var(--color-output)"
|
|
162
|
+
stroke="var(--color-output)"
|
|
163
|
+
fillOpacity={0.4}
|
|
164
|
+
/>
|
|
165
|
+
<Area
|
|
166
|
+
type="monotone"
|
|
167
|
+
dataKey="input"
|
|
168
|
+
stackId="1"
|
|
169
|
+
fill="var(--color-input)"
|
|
170
|
+
stroke="var(--color-input)"
|
|
171
|
+
fillOpacity={0.4}
|
|
172
|
+
/>
|
|
173
|
+
</AreaChart>
|
|
174
|
+
</ChartContainer>
|
|
175
|
+
</CardContent>
|
|
176
|
+
</Card>
|
|
177
|
+
</div>
|
|
178
|
+
)
|
|
179
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import { Bar, BarChart, XAxis, YAxis } from 'recharts'
|
|
4
|
+
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
|
|
5
|
+
import {
|
|
6
|
+
ChartContainer,
|
|
7
|
+
ChartTooltip,
|
|
8
|
+
ChartTooltipContent,
|
|
9
|
+
type ChartConfig,
|
|
10
|
+
} from '@/components/ui/chart'
|
|
11
|
+
import { formatNumber } from '@/lib/format'
|
|
12
|
+
|
|
13
|
+
const chartConfig = {
|
|
14
|
+
count: { label: 'Calls', color: 'var(--chart-6)' },
|
|
15
|
+
} satisfies ChartConfig
|
|
16
|
+
|
|
17
|
+
export function ToolUsageChart({ toolUsage }: { toolUsage: Record<string, number> }) {
|
|
18
|
+
const data = Object.entries(toolUsage)
|
|
19
|
+
.sort((a, b) => b[1] - a[1])
|
|
20
|
+
.slice(0, 20)
|
|
21
|
+
.map(([name, count]) => ({
|
|
22
|
+
name: name.length > 25 ? name.slice(0, 22) + '...' : name,
|
|
23
|
+
fullName: name,
|
|
24
|
+
count,
|
|
25
|
+
}))
|
|
26
|
+
|
|
27
|
+
return (
|
|
28
|
+
<Card>
|
|
29
|
+
<CardHeader>
|
|
30
|
+
<CardTitle>Tool Usage</CardTitle>
|
|
31
|
+
<CardDescription>Top 20 tools by invocation count</CardDescription>
|
|
32
|
+
</CardHeader>
|
|
33
|
+
<CardContent>
|
|
34
|
+
<ChartContainer
|
|
35
|
+
config={chartConfig}
|
|
36
|
+
className="w-full"
|
|
37
|
+
style={{ minHeight: Math.max(400, data.length * 32) }}
|
|
38
|
+
>
|
|
39
|
+
<BarChart data={data} layout="vertical" margin={{ left: 8 }} accessibilityLayer>
|
|
40
|
+
<XAxis type="number" tickLine={false} axisLine={false} />
|
|
41
|
+
<YAxis
|
|
42
|
+
type="category"
|
|
43
|
+
dataKey="name"
|
|
44
|
+
tickLine={false}
|
|
45
|
+
axisLine={false}
|
|
46
|
+
width={120}
|
|
47
|
+
tickFormatter={(v) => v}
|
|
48
|
+
/>
|
|
49
|
+
<ChartTooltip
|
|
50
|
+
content={
|
|
51
|
+
<ChartTooltipContent
|
|
52
|
+
formatter={(value) => formatNumber(Number(value))}
|
|
53
|
+
labelFormatter={(_, payload) => payload[0]?.payload?.fullName || ''}
|
|
54
|
+
/>
|
|
55
|
+
}
|
|
56
|
+
/>
|
|
57
|
+
<Bar dataKey="count" fill="var(--color-count)" radius={[0, 4, 4, 0]} />
|
|
58
|
+
</BarChart>
|
|
59
|
+
</ChartContainer>
|
|
60
|
+
</CardContent>
|
|
61
|
+
</Card>
|
|
62
|
+
)
|
|
63
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { mergeProps } from "@base-ui/react/merge-props"
|
|
2
|
+
import { useRender } from "@base-ui/react/use-render"
|
|
3
|
+
import { cva, type VariantProps } from "class-variance-authority"
|
|
4
|
+
|
|
5
|
+
import { cn } from "@/lib/utils"
|
|
6
|
+
|
|
7
|
+
const badgeVariants = cva(
|
|
8
|
+
"group/badge inline-flex h-5 w-fit shrink-0 items-center justify-center gap-1 overflow-hidden rounded-4xl border border-transparent px-2 py-0.5 text-xs font-medium whitespace-nowrap transition-all focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 [&>svg]:pointer-events-none [&>svg]:size-3!",
|
|
9
|
+
{
|
|
10
|
+
variants: {
|
|
11
|
+
variant: {
|
|
12
|
+
default: "bg-primary text-primary-foreground [a]:hover:bg-primary/80",
|
|
13
|
+
secondary:
|
|
14
|
+
"bg-secondary text-secondary-foreground [a]:hover:bg-secondary/80",
|
|
15
|
+
destructive:
|
|
16
|
+
"bg-destructive/10 text-destructive focus-visible:ring-destructive/20 dark:bg-destructive/20 dark:focus-visible:ring-destructive/40 [a]:hover:bg-destructive/20",
|
|
17
|
+
outline:
|
|
18
|
+
"border-border text-foreground [a]:hover:bg-muted [a]:hover:text-muted-foreground",
|
|
19
|
+
ghost:
|
|
20
|
+
"hover:bg-muted hover:text-muted-foreground dark:hover:bg-muted/50",
|
|
21
|
+
link: "text-primary underline-offset-4 hover:underline",
|
|
22
|
+
},
|
|
23
|
+
},
|
|
24
|
+
defaultVariants: {
|
|
25
|
+
variant: "default",
|
|
26
|
+
},
|
|
27
|
+
}
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
function Badge({
|
|
31
|
+
className,
|
|
32
|
+
variant = "default",
|
|
33
|
+
render,
|
|
34
|
+
...props
|
|
35
|
+
}: useRender.ComponentProps<"span"> & VariantProps<typeof badgeVariants>) {
|
|
36
|
+
return useRender({
|
|
37
|
+
defaultTagName: "span",
|
|
38
|
+
props: mergeProps<"span">(
|
|
39
|
+
{
|
|
40
|
+
className: cn(badgeVariants({ variant }), className),
|
|
41
|
+
},
|
|
42
|
+
props
|
|
43
|
+
),
|
|
44
|
+
render,
|
|
45
|
+
state: {
|
|
46
|
+
slot: "badge",
|
|
47
|
+
variant,
|
|
48
|
+
},
|
|
49
|
+
})
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export { Badge, badgeVariants }
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import { Button as ButtonPrimitive } from "@base-ui/react/button"
|
|
4
|
+
import { cva, type VariantProps } from "class-variance-authority"
|
|
5
|
+
|
|
6
|
+
import { cn } from "@/lib/utils"
|
|
7
|
+
|
|
8
|
+
const buttonVariants = cva(
|
|
9
|
+
"group/button inline-flex shrink-0 items-center justify-center rounded-lg border border-transparent bg-clip-padding text-sm font-medium whitespace-nowrap transition-all outline-none select-none focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 active:not-aria-[haspopup]:translate-y-px disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
|
10
|
+
{
|
|
11
|
+
variants: {
|
|
12
|
+
variant: {
|
|
13
|
+
default: "bg-primary text-primary-foreground [a]:hover:bg-primary/80",
|
|
14
|
+
outline:
|
|
15
|
+
"border-border bg-background hover:bg-muted hover:text-foreground aria-expanded:bg-muted aria-expanded:text-foreground dark:border-input dark:bg-input/30 dark:hover:bg-input/50",
|
|
16
|
+
secondary:
|
|
17
|
+
"bg-secondary text-secondary-foreground hover:bg-secondary/80 aria-expanded:bg-secondary aria-expanded:text-secondary-foreground",
|
|
18
|
+
ghost:
|
|
19
|
+
"hover:bg-muted hover:text-foreground aria-expanded:bg-muted aria-expanded:text-foreground dark:hover:bg-muted/50",
|
|
20
|
+
destructive:
|
|
21
|
+
"bg-destructive/10 text-destructive hover:bg-destructive/20 focus-visible:border-destructive/40 focus-visible:ring-destructive/20 dark:bg-destructive/20 dark:hover:bg-destructive/30 dark:focus-visible:ring-destructive/40",
|
|
22
|
+
link: "text-primary underline-offset-4 hover:underline",
|
|
23
|
+
},
|
|
24
|
+
size: {
|
|
25
|
+
default:
|
|
26
|
+
"h-8 gap-1.5 px-2.5 has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2",
|
|
27
|
+
xs: "h-6 gap-1 rounded-[min(var(--radius-md),10px)] px-2 text-xs in-data-[slot=button-group]:rounded-lg has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 [&_svg:not([class*='size-'])]:size-3",
|
|
28
|
+
sm: "h-7 gap-1 rounded-[min(var(--radius-md),12px)] px-2.5 text-[0.8rem] in-data-[slot=button-group]:rounded-lg has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 [&_svg:not([class*='size-'])]:size-3.5",
|
|
29
|
+
lg: "h-9 gap-1.5 px-2.5 has-data-[icon=inline-end]:pr-3 has-data-[icon=inline-start]:pl-3",
|
|
30
|
+
icon: "size-8",
|
|
31
|
+
"icon-xs":
|
|
32
|
+
"size-6 rounded-[min(var(--radius-md),10px)] in-data-[slot=button-group]:rounded-lg [&_svg:not([class*='size-'])]:size-3",
|
|
33
|
+
"icon-sm":
|
|
34
|
+
"size-7 rounded-[min(var(--radius-md),12px)] in-data-[slot=button-group]:rounded-lg",
|
|
35
|
+
"icon-lg": "size-9",
|
|
36
|
+
},
|
|
37
|
+
},
|
|
38
|
+
defaultVariants: {
|
|
39
|
+
variant: "default",
|
|
40
|
+
size: "default",
|
|
41
|
+
},
|
|
42
|
+
}
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
function Button({
|
|
46
|
+
className,
|
|
47
|
+
variant = "default",
|
|
48
|
+
size = "default",
|
|
49
|
+
...props
|
|
50
|
+
}: ButtonPrimitive.Props & VariantProps<typeof buttonVariants>) {
|
|
51
|
+
return (
|
|
52
|
+
<ButtonPrimitive
|
|
53
|
+
data-slot="button"
|
|
54
|
+
className={cn(buttonVariants({ variant, size, className }))}
|
|
55
|
+
{...props}
|
|
56
|
+
/>
|
|
57
|
+
)
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export { Button, buttonVariants }
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import * as React from "react"
|
|
2
|
+
|
|
3
|
+
import { cn } from "@/lib/utils"
|
|
4
|
+
|
|
5
|
+
function Card({
|
|
6
|
+
className,
|
|
7
|
+
size = "default",
|
|
8
|
+
...props
|
|
9
|
+
}: React.ComponentProps<"div"> & { size?: "default" | "sm" }) {
|
|
10
|
+
return (
|
|
11
|
+
<div
|
|
12
|
+
data-slot="card"
|
|
13
|
+
data-size={size}
|
|
14
|
+
className={cn(
|
|
15
|
+
"group/card flex flex-col gap-4 overflow-hidden rounded-xl bg-card py-4 text-sm text-card-foreground ring-1 ring-foreground/10 has-data-[slot=card-footer]:pb-0 has-[>img:first-child]:pt-0 data-[size=sm]:gap-3 data-[size=sm]:py-3 data-[size=sm]:has-data-[slot=card-footer]:pb-0 *:[img:first-child]:rounded-t-xl *:[img:last-child]:rounded-b-xl",
|
|
16
|
+
className
|
|
17
|
+
)}
|
|
18
|
+
{...props}
|
|
19
|
+
/>
|
|
20
|
+
)
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function CardHeader({ className, ...props }: React.ComponentProps<"div">) {
|
|
24
|
+
return (
|
|
25
|
+
<div
|
|
26
|
+
data-slot="card-header"
|
|
27
|
+
className={cn(
|
|
28
|
+
"group/card-header @container/card-header grid auto-rows-min items-start gap-1 rounded-t-xl px-4 group-data-[size=sm]/card:px-3 has-data-[slot=card-action]:grid-cols-[1fr_auto] has-data-[slot=card-description]:grid-rows-[auto_auto] [.border-b]:pb-4 group-data-[size=sm]/card:[.border-b]:pb-3",
|
|
29
|
+
className
|
|
30
|
+
)}
|
|
31
|
+
{...props}
|
|
32
|
+
/>
|
|
33
|
+
)
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function CardTitle({ className, ...props }: React.ComponentProps<"div">) {
|
|
37
|
+
return (
|
|
38
|
+
<div
|
|
39
|
+
data-slot="card-title"
|
|
40
|
+
className={cn(
|
|
41
|
+
"font-heading text-base leading-snug font-medium group-data-[size=sm]/card:text-sm",
|
|
42
|
+
className
|
|
43
|
+
)}
|
|
44
|
+
{...props}
|
|
45
|
+
/>
|
|
46
|
+
)
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function CardDescription({ className, ...props }: React.ComponentProps<"div">) {
|
|
50
|
+
return (
|
|
51
|
+
<div
|
|
52
|
+
data-slot="card-description"
|
|
53
|
+
className={cn("text-sm text-muted-foreground", className)}
|
|
54
|
+
{...props}
|
|
55
|
+
/>
|
|
56
|
+
)
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function CardAction({ className, ...props }: React.ComponentProps<"div">) {
|
|
60
|
+
return (
|
|
61
|
+
<div
|
|
62
|
+
data-slot="card-action"
|
|
63
|
+
className={cn(
|
|
64
|
+
"col-start-2 row-span-2 row-start-1 self-start justify-self-end",
|
|
65
|
+
className
|
|
66
|
+
)}
|
|
67
|
+
{...props}
|
|
68
|
+
/>
|
|
69
|
+
)
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function CardContent({ className, ...props }: React.ComponentProps<"div">) {
|
|
73
|
+
return (
|
|
74
|
+
<div
|
|
75
|
+
data-slot="card-content"
|
|
76
|
+
className={cn("px-4 group-data-[size=sm]/card:px-3", className)}
|
|
77
|
+
{...props}
|
|
78
|
+
/>
|
|
79
|
+
)
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function CardFooter({ className, ...props }: React.ComponentProps<"div">) {
|
|
83
|
+
return (
|
|
84
|
+
<div
|
|
85
|
+
data-slot="card-footer"
|
|
86
|
+
className={cn(
|
|
87
|
+
"flex items-center rounded-b-xl border-t bg-muted/50 p-4 group-data-[size=sm]/card:p-3",
|
|
88
|
+
className
|
|
89
|
+
)}
|
|
90
|
+
{...props}
|
|
91
|
+
/>
|
|
92
|
+
)
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export {
|
|
96
|
+
Card,
|
|
97
|
+
CardHeader,
|
|
98
|
+
CardFooter,
|
|
99
|
+
CardTitle,
|
|
100
|
+
CardAction,
|
|
101
|
+
CardDescription,
|
|
102
|
+
CardContent,
|
|
103
|
+
}
|