ltcai 8.0.0 → 8.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.
Files changed (103) hide show
  1. package/README.md +32 -50
  2. package/docs/CHANGELOG.md +14 -0
  3. package/docs/V3_FRONTEND.md +11 -5
  4. package/lattice_brain/__init__.py +1 -1
  5. package/lattice_brain/embeddings.py +9 -1
  6. package/lattice_brain/runtime/multi_agent.py +1 -1
  7. package/latticeai/__init__.py +1 -1
  8. package/latticeai/app_factory.py +13 -134
  9. package/latticeai/core/local_embeddings.py +8 -0
  10. package/latticeai/core/marketplace.py +1 -1
  11. package/latticeai/core/mcp_registry.py +140 -1
  12. package/latticeai/core/workspace_os.py +1 -1
  13. package/latticeai/services/architecture_readiness.py +6 -5
  14. package/latticeai/services/model_runtime.py +36 -2
  15. package/latticeai/services/product_readiness.py +18 -18
  16. package/package.json +2 -2
  17. package/scripts/build_frontend_assets.mjs +11 -2
  18. package/src-tauri/Cargo.lock +1 -1
  19. package/src-tauri/Cargo.toml +1 -1
  20. package/src-tauri/tauri.conf.json +1 -1
  21. package/static/app/asset-manifest.json +11 -102
  22. package/static/app/assets/Act-BOO66G-c.js +1 -0
  23. package/static/app/assets/Brain-C6lEYiD7.js +321 -0
  24. package/static/app/assets/{Capture-B9Tlhzqr.js → Capture-TATXBRDw.js} +1 -2
  25. package/static/app/assets/Library-DK4FIp8a.js +1 -0
  26. package/static/app/assets/System-Bgs6Ql7x.js +1 -0
  27. package/static/app/assets/core-CwxXejkd.js +1 -2
  28. package/static/app/assets/index-Bvv79nre.js +16 -0
  29. package/static/app/assets/index-Dslqglia.css +2 -0
  30. package/static/app/assets/primitives-B70WOra0.js +1 -0
  31. package/static/app/assets/{textarea-BZk6ybp5.js → textarea-Czrd9gwM.js} +1 -2
  32. package/static/app/index.html +3 -10
  33. package/static/app/theme-boot.js +8 -0
  34. package/static/sw.js +1 -1
  35. package/frontend/index.html +0 -24
  36. package/frontend/openapi.json +0 -15425
  37. package/frontend/src/App.tsx +0 -243
  38. package/frontend/src/api/client.ts +0 -580
  39. package/frontend/src/api/openapi.ts +0 -17892
  40. package/frontend/src/components/AdminAccessGate.tsx +0 -70
  41. package/frontend/src/components/FeedbackState.tsx +0 -45
  42. package/frontend/src/components/LanguageSwitcher.tsx +0 -23
  43. package/frontend/src/components/LivingBrain.tsx +0 -220
  44. package/frontend/src/components/ProductFlow.tsx +0 -171
  45. package/frontend/src/components/WorkspaceProfileSwitcher.tsx +0 -176
  46. package/frontend/src/components/onboarding/AnalysisScreen.tsx +0 -135
  47. package/frontend/src/components/onboarding/DownloadConsentPanel.tsx +0 -29
  48. package/frontend/src/components/onboarding/InstallScreen.tsx +0 -263
  49. package/frontend/src/components/onboarding/LanguageChooser.tsx +0 -23
  50. package/frontend/src/components/onboarding/LoginScreen.tsx +0 -131
  51. package/frontend/src/components/onboarding/ProductFlowScreens.tsx +0 -13
  52. package/frontend/src/components/onboarding/RecommendationScreen.tsx +0 -116
  53. package/frontend/src/components/onboarding/recommendationModel.ts +0 -189
  54. package/frontend/src/components/primitives.tsx +0 -392
  55. package/frontend/src/components/ui/badge.tsx +0 -27
  56. package/frontend/src/components/ui/button.tsx +0 -37
  57. package/frontend/src/components/ui/card.tsx +0 -22
  58. package/frontend/src/components/ui/input.tsx +0 -16
  59. package/frontend/src/components/ui/textarea.tsx +0 -16
  60. package/frontend/src/features/admin/AdminConsole.tsx +0 -334
  61. package/frontend/src/features/brain/BrainCarePanel.tsx +0 -254
  62. package/frontend/src/features/brain/BrainComposer.tsx +0 -85
  63. package/frontend/src/features/brain/BrainConversation.tsx +0 -688
  64. package/frontend/src/features/brain/BrainGraphLayer.tsx +0 -365
  65. package/frontend/src/features/brain/BrainHome.tsx +0 -624
  66. package/frontend/src/features/brain/BrainMemoryLayer.tsx +0 -45
  67. package/frontend/src/features/brain/BrainOverviewPanel.tsx +0 -149
  68. package/frontend/src/features/brain/BrainRelationshipLayer.tsx +0 -43
  69. package/frontend/src/features/brain/DepthEmergence.tsx +0 -53
  70. package/frontend/src/features/brain/brainData.ts +0 -246
  71. package/frontend/src/features/brain/graphLayout.ts +0 -37
  72. package/frontend/src/features/brain/types.ts +0 -150
  73. package/frontend/src/features/review/ReviewCard.tsx +0 -100
  74. package/frontend/src/features/review/ReviewInbox.tsx +0 -127
  75. package/frontend/src/features/review/reviewHelpers.ts +0 -69
  76. package/frontend/src/i18n.ts +0 -1303
  77. package/frontend/src/lib/utils.ts +0 -33
  78. package/frontend/src/main.tsx +0 -23
  79. package/frontend/src/pages/Act.tsx +0 -458
  80. package/frontend/src/pages/Ask.tsx +0 -14
  81. package/frontend/src/pages/Brain.tsx +0 -914
  82. package/frontend/src/pages/Capture.tsx +0 -258
  83. package/frontend/src/pages/Library.tsx +0 -486
  84. package/frontend/src/pages/System.tsx +0 -621
  85. package/frontend/src/routes.ts +0 -92
  86. package/frontend/src/store/appStore.ts +0 -94
  87. package/frontend/src/styles.css +0 -6579
  88. package/static/app/assets/Act-DOvf59ru.js +0 -2
  89. package/static/app/assets/Act-DOvf59ru.js.map +0 -1
  90. package/static/app/assets/Brain-C7_0mEiI.js +0 -322
  91. package/static/app/assets/Brain-C7_0mEiI.js.map +0 -1
  92. package/static/app/assets/Capture-B9Tlhzqr.js.map +0 -1
  93. package/static/app/assets/Library-BJPEEm5O.js +0 -2
  94. package/static/app/assets/Library-BJPEEm5O.js.map +0 -1
  95. package/static/app/assets/System-D6t9jo9V.js +0 -2
  96. package/static/app/assets/System-D6t9jo9V.js.map +0 -1
  97. package/static/app/assets/core-CwxXejkd.js.map +0 -1
  98. package/static/app/assets/index-C7g26IF6.css +0 -2
  99. package/static/app/assets/index-DbcEYJQ2.js +0 -17
  100. package/static/app/assets/index-DbcEYJQ2.js.map +0 -1
  101. package/static/app/assets/primitives-CD38lt4n.js +0 -2
  102. package/static/app/assets/primitives-CD38lt4n.js.map +0 -1
  103. package/static/app/assets/textarea-BZk6ybp5.js.map +0 -1
@@ -1,392 +0,0 @@
1
- import * as React from "react";
2
- import { useMutation, useQueryClient } from "@tanstack/react-query";
3
- import { AlertCircle, CheckCircle2, Loader2, LockKeyhole, Sparkles } from "lucide-react";
4
- import type { ApiResult } from "@/api/client";
5
- import { Badge } from "@/components/ui/badge";
6
- import { Button } from "@/components/ui/button";
7
- import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
8
- import { useAppStore } from "@/store/appStore";
9
- import { cn, asArray, fmtNumber, shortId, titleize } from "@/lib/utils";
10
-
11
- export function SourceBadge({ result }: { result?: Pick<ApiResult, "source" | "ok" | "status"> }) {
12
- const mode = useAppStore((state) => state.mode);
13
- if (!result) return <Badge variant="muted">not loaded</Badge>;
14
- if (result.source === "live" && result.ok) return <Badge variant="success">{mode === "basic" ? "ready" : "connected"}</Badge>;
15
- return <Badge variant="warning">{mode === "basic" ? "needs setup" : "unavailable"}</Badge>;
16
- }
17
-
18
- export function EmptyState({ title = "Unavailable", detail }: { title?: string; detail?: React.ReactNode }) {
19
- return (
20
- <div className="flex min-h-36 flex-col items-center justify-center gap-2 rounded-lg border border-dashed border-border bg-muted/24 p-6 text-center text-sm text-muted-foreground">
21
- <div className="grid h-10 w-10 place-items-center rounded-md border border-border bg-card">
22
- <Sparkles className="h-5 w-5 text-primary" />
23
- </div>
24
- <div className="text-base font-semibold text-foreground">{title}</div>
25
- {detail ? <div className="max-w-md leading-6">{detail}</div> : null}
26
- </div>
27
- );
28
- }
29
-
30
- export function DataPanel<T>({
31
- title,
32
- description,
33
- result,
34
- children,
35
- className,
36
- }: {
37
- title: string;
38
- description?: string;
39
- result?: ApiResult<T>;
40
- children: (data: T) => React.ReactNode;
41
- className?: string;
42
- }) {
43
- const mode = useAppStore((state) => state.mode);
44
- return (
45
- <Card className={cn("overflow-hidden", className)}>
46
- <CardHeader className="flex-row items-start justify-between gap-3">
47
- <div>
48
- <CardTitle>{title}</CardTitle>
49
- {description ? <CardDescription>{description}</CardDescription> : null}
50
- </div>
51
- <SourceBadge result={result} />
52
- </CardHeader>
53
- <CardContent>
54
- {result?.ok ? children(result.data) : (
55
- <EmptyState detail={mode === "basic" ? "This area needs setup or is not available yet." : result?.error || "This capability is not reporting right now."} />
56
- )}
57
- </CardContent>
58
- </Card>
59
- );
60
- }
61
-
62
- export function LoadingPanel({ title }: { title: string }) {
63
- return (
64
- <Card>
65
- <CardHeader>
66
- <CardTitle>{title}</CardTitle>
67
- </CardHeader>
68
- <CardContent>
69
- <div className="flex items-center gap-2 text-sm text-muted-foreground">
70
- <Loader2 className="h-4 w-4 animate-spin" /> Loading
71
- </div>
72
- </CardContent>
73
- </Card>
74
- );
75
- }
76
-
77
- export function StatGrid({ stats }: { stats: Array<{ label: string; value: unknown; hint?: string }> }) {
78
- return (
79
- <div className="grid gap-3 sm:grid-cols-2 xl:grid-cols-4">
80
- {stats.map((stat) => (
81
- <div key={stat.label} className="rounded-lg border border-border bg-background/55 p-4">
82
- <div className="text-xs uppercase text-muted-foreground">{stat.label}</div>
83
- <div className="mt-2 text-2xl font-semibold leading-tight">{typeof stat.value === "number" ? fmtNumber(stat.value) : String(stat.value ?? "-")}</div>
84
- {stat.hint ? <div className="mt-2 text-xs leading-5 text-muted-foreground">{stat.hint}</div> : null}
85
- </div>
86
- ))}
87
- </div>
88
- );
89
- }
90
-
91
- function isRecord(value: unknown): value is Record<string, unknown> {
92
- return Boolean(value && typeof value === "object" && !Array.isArray(value));
93
- }
94
-
95
- function scalarText(value: unknown) {
96
- if (value === null || value === undefined || value === "") return "-";
97
- if (typeof value === "number") return Number.isFinite(value) ? fmtNumber(value) : "-";
98
- if (typeof value === "boolean") return value ? "Enabled" : "Disabled";
99
- return String(value);
100
- }
101
-
102
- const BASIC_HIDDEN_KEY = /(^id$|_id$|token|secret|passphrase|fingerprint|public_key|private_key|dsn|schema|endpoint|base_url|localhost|127\.0\.0\.1|stack|trace|raw|runtime|engine|module|port|host|api|internal)/i;
103
-
104
- function hideInBasic(key: string) {
105
- return BASIC_HIDDEN_KEY.test(key);
106
- }
107
-
108
- function humanText(value: unknown) {
109
- const text = scalarText(value);
110
- if (text === "-") return text;
111
- if (text.includes("/") || text.includes("@") || /\.[a-z0-9]{2,5}$/i.test(text)) return text;
112
- return titleize(text.replace(/^agent:/i, "").replace(/^tool:/i, ""));
113
- }
114
-
115
- function firstRecordList(value: Record<string, unknown>) {
116
- const preferred = [
117
- "documents", "sources", "items", "agents", "workflows", "runs", "events",
118
- "permissions", "models", "peers", "invitations", "roles", "policies",
119
- "hooks", "tools", "templates", "plugins", "recent_events",
120
- ];
121
- for (const key of preferred) {
122
- const rows = asArray<Record<string, unknown>>(value[key]);
123
- if (rows.length) return rows;
124
- }
125
- return [];
126
- }
127
-
128
- export function ValuePreview({ value }: { value: unknown }) {
129
- if (typeof value === "boolean") {
130
- return <Badge variant={value ? "success" : "muted"}>{value ? "enabled" : "disabled"}</Badge>;
131
- }
132
- if (Array.isArray(value)) {
133
- if (!value.length) return <span className="text-muted-foreground">None</span>;
134
- const primitive = value.every((item) => item === null || ["string", "number", "boolean"].includes(typeof item));
135
- if (primitive) {
136
- return (
137
- <span className="flex flex-wrap gap-1">
138
- {value.slice(0, 5).map((item, index) => <Badge key={`${String(item)}-${index}`} variant="muted">{scalarText(item)}</Badge>)}
139
- {value.length > 5 ? <Badge variant="muted">+{value.length - 5}</Badge> : null}
140
- </span>
141
- );
142
- }
143
- return <span className="text-muted-foreground">{fmtNumber(value.length)} records</span>;
144
- }
145
- if (isRecord(value)) {
146
- const keys = Object.keys(value);
147
- if (!keys.length) return <span className="text-muted-foreground">No fields</span>;
148
- return <span className="text-muted-foreground">{keys.slice(0, 4).map(titleize).join(", ")}{keys.length > 4 ? ` +${keys.length - 4}` : ""}</span>;
149
- }
150
- const text = scalarText(value);
151
- return <span className="break-words">{text.length > 96 ? shortId(text, 96) : text}</span>;
152
- }
153
-
154
- export function KeyValueList({ data, limit = 8 }: { data: Record<string, unknown>; limit?: number }) {
155
- const mode = useAppStore((state) => state.mode);
156
- const rows = Object.entries(data || {})
157
- .filter(([key]) => mode !== "basic" || !hideInBasic(key))
158
- .slice(0, limit);
159
- if (!rows.length) return <EmptyState title="No values" />;
160
- return (
161
- <div className="divide-y divide-border rounded-md border border-border">
162
- {rows.map(([key, value]) => (
163
- <div key={key} className="grid grid-cols-[minmax(9rem,0.5fr)_1fr] gap-3 p-3 text-sm">
164
- <span className="font-medium text-muted-foreground">{titleize(key)}</span>
165
- <span className="min-w-0 break-words"><ValuePreview value={value} /></span>
166
- </div>
167
- ))}
168
- </div>
169
- );
170
- }
171
-
172
- export function StructuredView({
173
- value,
174
- titleKey = "title",
175
- metaKey = "status",
176
- limit = 8,
177
- }: {
178
- value: unknown;
179
- titleKey?: string;
180
- metaKey?: string;
181
- limit?: number;
182
- }) {
183
- const mode = useAppStore((state) => state.mode);
184
- if (mode === "basic") return <FriendlySummary value={value} titleKey={titleKey} metaKey={metaKey} limit={limit} />;
185
- if (Array.isArray(value)) {
186
- if (!value.length) return <EmptyState title="Nothing here yet" detail="New items will appear here when Lattice has something to show." />;
187
- if (value.every((item) => isRecord(item))) {
188
- return <EntityList items={value} titleKey={titleKey} metaKey={metaKey} limit={limit} />;
189
- }
190
- return (
191
- <div className="flex flex-wrap gap-1 rounded-md border border-border bg-background p-3">
192
- {value.slice(0, limit).map((item, index) => <Badge key={`${String(item)}-${index}`} variant="muted">{scalarText(item)}</Badge>)}
193
- {value.length > limit ? <Badge variant="muted">+{value.length - limit}</Badge> : null}
194
- </div>
195
- );
196
- }
197
- if (isRecord(value)) return <KeyValueList data={value} limit={limit} />;
198
- return (
199
- <div className="rounded-md border border-border bg-background p-3 text-sm">
200
- <ValuePreview value={value} />
201
- </div>
202
- );
203
- }
204
-
205
- export function FriendlySummary({
206
- value,
207
- titleKey = "title",
208
- metaKey = "status",
209
- limit = 6,
210
- }: {
211
- value: unknown;
212
- titleKey?: string;
213
- metaKey?: string;
214
- limit?: number;
215
- }) {
216
- if (Array.isArray(value)) {
217
- if (!value.length) return <EmptyState title="Nothing here yet" detail="New items will appear here when Lattice has something to show." />;
218
- if (value.every((item) => isRecord(item))) {
219
- return <EntityList items={value} titleKey={titleKey} metaKey={metaKey} limit={limit} />;
220
- }
221
- return (
222
- <div className="flex flex-wrap gap-1 rounded-md border border-border bg-background/55 p-3">
223
- {value.slice(0, limit).map((item, index) => <Badge key={`${String(item)}-${index}`} variant="muted">{humanText(item)}</Badge>)}
224
- {value.length > limit ? <Badge variant="muted">+{value.length - limit}</Badge> : null}
225
- </div>
226
- );
227
- }
228
- if (isRecord(value)) {
229
- const list = firstRecordList(value);
230
- if (list.length) return <EntityList items={list} titleKey={titleKey} metaKey={metaKey} limit={limit} />;
231
- const friendly = Object.fromEntries(
232
- Object.entries(value)
233
- .filter(([key]) => !hideInBasic(key))
234
- .map(([key, item]) => [key, Array.isArray(item) ? `${fmtNumber(item.length)} items` : isRecord(item) ? "available" : item]),
235
- );
236
- return <KeyValueList data={friendly} limit={limit} />;
237
- }
238
- return (
239
- <div className="rounded-md border border-border bg-background/55 p-3 text-sm">
240
- {humanText(value)}
241
- </div>
242
- );
243
- }
244
-
245
- export function OperationResult({
246
- result,
247
- successLabel = "Request completed",
248
- }: {
249
- result?: ApiResult<unknown> | null;
250
- successLabel?: string;
251
- }) {
252
- const mode = useAppStore((state) => state.mode);
253
- if (!result) return null;
254
- if (!result.ok) {
255
- return <EmptyState title="Request unavailable" detail={result.error || <ValuePreview value={result.data} />} />;
256
- }
257
- return (
258
- <div className="space-y-2 rounded-md border border-border bg-background p-3">
259
- <Badge variant="success">{successLabel}</Badge>
260
- {mode === "basic" ? <FriendlySummary value={result.data} /> : <StructuredView value={result.data} />}
261
- </div>
262
- );
263
- }
264
-
265
- export function EntityList({
266
- items,
267
- titleKey = "title",
268
- metaKey = "type",
269
- limit = 8,
270
- }: {
271
- items: unknown;
272
- titleKey?: string;
273
- metaKey?: string;
274
- limit?: number;
275
- }) {
276
- const mode = useAppStore((state) => state.mode);
277
- const rows = asArray<Record<string, unknown>>(items).slice(0, limit);
278
- if (!rows.length) return <EmptyState title="Nothing here yet" detail="New items will appear here when Lattice has something to show." />;
279
- return (
280
- <div className="grid gap-2">
281
- {rows.map((item, index) => (
282
- <div key={String(item.id || item.name || index)} className="rounded-lg border border-border bg-background/55 p-3">
283
- <div className="flex flex-wrap items-center justify-between gap-2">
284
- <div className="font-medium">{mode === "basic" ? humanText(item[titleKey] || item.name || item.label || `Item ${index + 1}`) : String(item[titleKey] || item.name || item.id || `Record ${index + 1}`)}</div>
285
- <Badge variant="muted">{mode === "basic" ? humanText(item[metaKey] || item.status || item.state || "ready") : String(item[metaKey] || item.status || item.state || "record")}</Badge>
286
- </div>
287
- {item.summary || item.description || item.path || (item.id && item[titleKey] !== item.id) ? (
288
- <p className="mt-1 text-sm text-muted-foreground">{String(item.summary || item.description || item.path || item.id)}</p>
289
- ) : null}
290
- {mode !== "basic" && item.id && item[titleKey] !== item.id ? (
291
- <div className="mt-1 text-xs text-muted-foreground">{shortId(item.id, 48)}</div>
292
- ) : null}
293
- </div>
294
- ))}
295
- </div>
296
- );
297
- }
298
-
299
- export function ModeGate({
300
- title = "Advanced controls",
301
- detail = "Switch modes when you want diagnostics or administrative controls. Basic mode keeps the product focused on everyday use.",
302
- target = "advanced",
303
- }: {
304
- title?: string;
305
- detail?: string;
306
- target?: "advanced" | "admin";
307
- }) {
308
- const setMode = useAppStore((state) => state.setMode);
309
- return (
310
- <Card>
311
- <CardContent className="flex flex-col items-start gap-3 p-6">
312
- <div className="grid h-10 w-10 place-items-center rounded-md border border-border bg-background/70">
313
- <LockKeyhole className="h-5 w-5 text-primary" />
314
- </div>
315
- <div>
316
- <div className="text-lg font-semibold">{title}</div>
317
- <p className="mt-1 max-w-2xl text-sm leading-6 text-muted-foreground">{detail}</p>
318
- </div>
319
- <Button onClick={() => setMode(target)}>{target === "admin" ? "Switch to Admin Console" : "Switch to Advanced"}</Button>
320
- </CardContent>
321
- </Card>
322
- );
323
- }
324
-
325
- export function ActionButton({
326
- label,
327
- successLabel = "Done",
328
- action,
329
- invalidate,
330
- variant = "outline",
331
- disabled,
332
- }: {
333
- label: string;
334
- successLabel?: string;
335
- action: () => Promise<ApiResult<unknown>>;
336
- invalidate?: string[];
337
- variant?: React.ComponentProps<typeof Button>["variant"];
338
- disabled?: boolean;
339
- }) {
340
- const qc = useQueryClient();
341
- const [result, setResult] = React.useState<string | null>(null);
342
- const mut = useMutation({
343
- mutationFn: action,
344
- onSuccess: async (res) => {
345
- setResult(res.ok ? successLabel : res.error || "Unavailable");
346
- if (invalidate) {
347
- await Promise.all(invalidate.map((key) => qc.invalidateQueries({ queryKey: [key] })));
348
- }
349
- },
350
- });
351
- return (
352
- <div className="flex flex-wrap items-center gap-2">
353
- <Button variant={variant} disabled={disabled || mut.isPending} onClick={() => mut.mutate()}>
354
- {mut.isPending ? <Loader2 className="h-4 w-4 animate-spin" /> : null}
355
- {label}
356
- </Button>
357
- {result ? (
358
- <span className={cn("inline-flex items-center gap-1 text-xs", result === successLabel ? "text-emerald-300" : "text-amber-300")}>
359
- {result === successLabel ? <CheckCircle2 className="h-3.5 w-3.5" /> : <AlertCircle className="h-3.5 w-3.5" />}
360
- {result}
361
- </span>
362
- ) : null}
363
- </div>
364
- );
365
- }
366
-
367
- export function Tabs({
368
- tabs,
369
- value,
370
- onChange,
371
- }: {
372
- tabs: Array<{ id: string; label: string }>;
373
- value: string;
374
- onChange: (id: string) => void;
375
- }) {
376
- return (
377
- <div className="inline-flex max-w-full flex-wrap gap-1 rounded-lg border border-border bg-muted/28 p-1">
378
- {tabs.map((tab) => (
379
- <button
380
- key={tab.id}
381
- onClick={() => onChange(tab.id)}
382
- className={cn(
383
- "h-9 rounded-md px-3.5 text-sm font-semibold transition",
384
- value === tab.id ? "bg-card text-foreground shadow-sm" : "text-muted-foreground hover:bg-card/60 hover:text-foreground",
385
- )}
386
- >
387
- {tab.label}
388
- </button>
389
- ))}
390
- </div>
391
- );
392
- }
@@ -1,27 +0,0 @@
1
- import * as React from "react";
2
- import { cn } from "@/lib/utils";
3
-
4
- type BadgeProps = React.HTMLAttributes<HTMLSpanElement> & {
5
- variant?: "default" | "success" | "warning" | "muted" | "danger";
6
- };
7
-
8
- const variants = {
9
- default: "border-primary/25 bg-primary/12 text-primary",
10
- success: "border-emerald-500/25 bg-emerald-500/12 text-emerald-300",
11
- warning: "border-amber-500/25 bg-amber-500/12 text-amber-300",
12
- muted: "border-border bg-muted/70 text-muted-foreground",
13
- danger: "border-destructive/30 bg-destructive/12 text-destructive",
14
- };
15
-
16
- export function Badge({ className, variant = "default", ...props }: BadgeProps) {
17
- return (
18
- <span
19
- className={cn(
20
- "inline-flex min-h-6 max-w-full items-center rounded-md border px-2 py-0.5 text-xs font-semibold leading-none",
21
- variants[variant],
22
- className,
23
- )}
24
- {...props}
25
- />
26
- );
27
- }
@@ -1,37 +0,0 @@
1
- import * as React from "react";
2
- import { cva, type VariantProps } from "class-variance-authority";
3
- import { cn } from "@/lib/utils";
4
-
5
- const buttonVariants = cva(
6
- "inline-flex h-10 max-w-full items-center justify-center gap-2 rounded-md px-3.5 text-sm font-semibold leading-none transition focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-45",
7
- {
8
- variants: {
9
- variant: {
10
- default: "bg-primary text-primary-foreground shadow-[0_10px_30px_hsl(var(--primary)/0.16)] hover:bg-primary/92",
11
- secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/82",
12
- ghost: "text-foreground hover:bg-muted",
13
- outline: "border border-border bg-card/70 hover:bg-muted/88",
14
- destructive: "bg-destructive text-destructive-foreground hover:bg-destructive/90",
15
- },
16
- size: {
17
- sm: "h-8 px-2.5 text-xs",
18
- md: "h-10 px-3.5",
19
- icon: "h-10 w-10 px-0",
20
- },
21
- },
22
- defaultVariants: {
23
- variant: "default",
24
- size: "md",
25
- },
26
- },
27
- );
28
-
29
- export type ButtonProps = React.ButtonHTMLAttributes<HTMLButtonElement> &
30
- VariantProps<typeof buttonVariants>;
31
-
32
- export const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
33
- ({ className, variant, size, ...props }, ref) => (
34
- <button ref={ref} className={cn(buttonVariants({ variant, size, className }))} {...props} />
35
- ),
36
- );
37
- Button.displayName = "Button";
@@ -1,22 +0,0 @@
1
- import * as React from "react";
2
- import { cn } from "@/lib/utils";
3
-
4
- export function Card({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) {
5
- return <section className={cn("premium-surface rounded-lg text-card-foreground", className)} {...props} />;
6
- }
7
-
8
- export function CardHeader({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) {
9
- return <div className={cn("flex flex-col gap-1.5 p-5", className)} {...props} />;
10
- }
11
-
12
- export function CardTitle({ className, ...props }: React.HTMLAttributes<HTMLHeadingElement>) {
13
- return <h2 className={cn("text-base font-semibold tracking-normal", className)} {...props} />;
14
- }
15
-
16
- export function CardDescription({ className, ...props }: React.HTMLAttributes<HTMLParagraphElement>) {
17
- return <p className={cn("text-sm leading-6 text-muted-foreground", className)} {...props} />;
18
- }
19
-
20
- export function CardContent({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) {
21
- return <div className={cn("p-5 pt-0", className)} {...props} />;
22
- }
@@ -1,16 +0,0 @@
1
- import * as React from "react";
2
- import { cn } from "@/lib/utils";
3
-
4
- export const Input = React.forwardRef<HTMLInputElement, React.InputHTMLAttributes<HTMLInputElement>>(
5
- ({ className, ...props }, ref) => (
6
- <input
7
- ref={ref}
8
- className={cn(
9
- "h-10 w-full rounded-md border border-input bg-background/70 px-3 text-sm outline-none transition placeholder:text-muted-foreground focus:border-ring focus:ring-2 focus:ring-ring/35 disabled:cursor-not-allowed disabled:opacity-50",
10
- className,
11
- )}
12
- {...props}
13
- />
14
- ),
15
- );
16
- Input.displayName = "Input";
@@ -1,16 +0,0 @@
1
- import * as React from "react";
2
- import { cn } from "@/lib/utils";
3
-
4
- export const Textarea = React.forwardRef<HTMLTextAreaElement, React.TextareaHTMLAttributes<HTMLTextAreaElement>>(
5
- ({ className, ...props }, ref) => (
6
- <textarea
7
- ref={ref}
8
- className={cn(
9
- "min-h-28 w-full resize-y rounded-md border border-input bg-background/70 px-3 py-3 text-sm leading-6 outline-none transition placeholder:text-muted-foreground focus:border-ring focus:ring-2 focus:ring-ring/35 disabled:cursor-not-allowed disabled:opacity-50",
10
- className,
11
- )}
12
- {...props}
13
- />
14
- ),
15
- );
16
- Textarea.displayName = "Textarea";