@rudderhq/agent-runtime-opencode-local 0.7.2-canary.0 → 0.7.3

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 (50) hide show
  1. package/dist/index.d.ts +1 -1
  2. package/dist/index.d.ts.map +1 -1
  3. package/dist/index.js +2 -2
  4. package/dist/server/index.d.ts +1 -1
  5. package/dist/server/index.d.ts.map +1 -1
  6. package/dist/server/index.js +1 -1
  7. package/dist/server/index.js.map +1 -1
  8. package/dist/server/models.d.ts.map +1 -1
  9. package/dist/server/models.js +86 -4
  10. package/dist/server/models.js.map +1 -1
  11. package/dist/server/models.test.js +31 -0
  12. package/dist/server/models.test.js.map +1 -1
  13. package/package.json +2 -2
  14. package/skills/app-builder/SKILL.md +39 -9
  15. package/skills/app-builder/assets/scaffold/app/globals.css +109 -16
  16. package/skills/app-builder/assets/scaffold/components/contacts-workspace.tsx +176 -152
  17. package/skills/app-builder/assets/scaffold/components/ui/alert.tsx +12 -0
  18. package/skills/app-builder/assets/scaffold/components/ui/badge.tsx +27 -0
  19. package/skills/app-builder/assets/scaffold/components/ui/button.tsx +9 -7
  20. package/skills/app-builder/assets/scaffold/components/ui/card.tsx +15 -3
  21. package/skills/app-builder/assets/scaffold/components/ui/empty.tsx +18 -0
  22. package/skills/app-builder/assets/scaffold/components/ui/field.tsx +23 -0
  23. package/skills/app-builder/assets/scaffold/components/ui/input.tsx +1 -1
  24. package/skills/app-builder/assets/scaffold/components/ui/label.tsx +1 -1
  25. package/skills/app-builder/assets/scaffold/components/ui/separator.tsx +6 -0
  26. package/skills/app-builder/assets/scaffold/components/ui/skeleton.tsx +6 -0
  27. package/skills/app-builder/assets/scaffold/components/ui/table.tsx +30 -0
  28. package/skills/app-builder/assets/scaffold/components.json +20 -0
  29. package/skills/app-builder/assets/scaffold/next-env.d.ts +1 -1
  30. package/skills/app-builder/assets/scaffold/next.config.ts +19 -0
  31. package/skills/app-builder/assets/scaffold/package.json +2 -1
  32. package/skills/app-builder/assets/scaffold/rudder.app.json +1 -1
  33. package/skills/app-builder/assets/scaffold/rudder.ui.json +13 -0
  34. package/skills/app-builder/assets/scaffold/scripts/validate-rudder-ui.mjs +50 -0
  35. package/skills/app-builder/assets/scaffold/tests/e2e/app.spec.ts +35 -1
  36. package/skills/app-builder/evals/evals.json +2 -1
  37. package/skills/app-builder/references/design-guidelines.md +19 -2
  38. package/skills/app-builder/references/rudder-ui-preset.md +73 -0
  39. package/skills/app-builder/references/scaffold-contract.md +12 -0
  40. package/skills/app-builder/references/verification.md +6 -0
  41. package/skills/app-builder/scripts/report-build-status.mjs +83 -0
  42. package/skills/browser/SKILL.md +11 -4
  43. package/skills/browser/agents/openai.yaml +2 -2
  44. package/skills/browser/evals/trigger-evals.json +22 -0
  45. package/skills/rudder-docs/SKILL.md +2 -2
  46. package/skills/rudder-docs/evals/retrieval-authority-evals.json +40 -0
  47. package/skills/rudder-docs/evals/trigger-evals.json +2 -2
  48. package/skills/rudder-docs/references/api-reference.md +0 -2
  49. package/skills/rudder-docs/references/plugin-authoring.md +38 -114
  50. package/skills/rudder-docs/references/source-map.md +2 -0
@@ -1,10 +1,15 @@
1
1
  "use client";
2
2
 
3
+ import { Alert } from "@/components/ui/alert";
4
+ import { Badge } from "@/components/ui/badge";
3
5
  import { Button } from "@/components/ui/button";
4
- import { Card, CardContent, CardHeader } from "@/components/ui/card";
6
+ import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
7
+ import { Empty, EmptyDescription, EmptyHeader, EmptyTitle } from "@/components/ui/empty";
8
+ import { Field, FieldGroup, FieldLabel } from "@/components/ui/field";
5
9
  import { Input } from "@/components/ui/input";
6
- import { Label } from "@/components/ui/label";
7
- import { Download, Plus, Search, Trash2, Upload } from "lucide-react";
10
+ import { Skeleton } from "@/components/ui/skeleton";
11
+ import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table";
12
+ import { Download, Plus, Search, Trash2, Upload, UsersRound } from "lucide-react";
8
13
  import { FormEvent, useCallback, useEffect, useMemo, useRef, useState } from "react";
9
14
 
10
15
  type Contact = {
@@ -25,6 +30,13 @@ type Draft = {
25
30
 
26
31
  const emptyDraft: Draft = { name: "", email: "", company: "" };
27
32
 
33
+ const statusVariant: Record<Contact["status"], "secondary" | "outline" | "success"> = {
34
+ new: "secondary",
35
+ contacted: "outline",
36
+ replied: "success",
37
+ paused: "outline",
38
+ };
39
+
28
40
  export function ContactsWorkspace() {
29
41
  const [contacts, setContacts] = useState<Contact[]>([]);
30
42
  const [draft, setDraft] = useState<Draft>(emptyDraft);
@@ -118,175 +130,187 @@ export function ContactsWorkspace() {
118
130
  }
119
131
 
120
132
  return (
121
- <div className="mx-auto grid max-w-7xl gap-6 px-4 py-6 lg:grid-cols-[minmax(0,1fr)_22rem] lg:px-8">
122
- <section className="min-w-0">
123
- <header className="mb-5 flex flex-col gap-4 sm:flex-row sm:items-end sm:justify-between">
124
- <div>
125
- <p className="mb-1 text-sm font-medium text-[var(--primary)]">Contacts</p>
126
- <h1 className="m-0 text-2xl font-semibold tracking-tight">Customer workspace</h1>
127
- <p className="mb-0 mt-2 text-sm text-[var(--muted-foreground)]">
128
- Keep the next conversation visible and every record on this device.
129
- </p>
130
- </div>
131
- <div className="flex flex-wrap gap-2">
132
- <input
133
- ref={importRef}
134
- aria-label="Import app data"
135
- className="sr-only"
136
- type="file"
137
- accept="application/json,.json"
138
- onChange={(event) => {
139
- const file = event.currentTarget.files?.[0];
140
- if (file) void importData(file);
141
- }}
142
- />
143
- <Button type="button" variant="outline" onClick={() => importRef.current?.click()}>
144
- <Upload aria-hidden size={16} />
145
- Import
146
- </Button>
147
- <Button asChild variant="outline">
148
- <a href="/api/data/export" download>
149
- <Download aria-hidden size={16} />
150
- Export
151
- </a>
152
- </Button>
133
+ <div className="min-h-screen bg-background text-foreground">
134
+ <header className="border-b bg-card/80">
135
+ <div className="mx-auto flex h-14 max-w-7xl items-center justify-between px-4 lg:px-6">
136
+ <div className="flex min-w-0 items-center gap-3">
137
+ <span className="flex size-8 shrink-0 items-center justify-center rounded-md bg-primary text-primary-foreground">
138
+ <UsersRound aria-hidden className="size-4" />
139
+ </span>
140
+ <div className="min-w-0">
141
+ <p className="m-0 truncate text-sm font-semibold">Pipeline Desk</p>
142
+ <p className="m-0 text-xs text-muted-foreground">Local customer workspace</p>
143
+ </div>
153
144
  </div>
154
- </header>
145
+ <Badge variant="outline">Development data</Badge>
146
+ </div>
147
+ </header>
155
148
 
156
- <Card>
157
- <CardHeader className="flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between">
149
+ <main className="mx-auto grid max-w-7xl gap-4 px-4 py-5 lg:grid-cols-[minmax(0,1fr)_20rem] lg:px-6">
150
+ <section className="min-w-0">
151
+ <div className="mb-4 flex flex-col gap-3 sm:flex-row sm:items-end sm:justify-between">
158
152
  <div>
159
- <h2 className="m-0 text-base font-semibold">All contacts</h2>
160
- <p className="mb-0 mt-1 text-sm text-[var(--muted-foreground)]">
161
- {contacts.length} {contacts.length === 1 ? "record" : "records"}
153
+ <h1 className="m-0 text-2xl font-semibold">Contacts</h1>
154
+ <p className="mb-0 mt-1 text-sm text-muted-foreground">
155
+ Keep the next conversation visible and every record on this device.
162
156
  </p>
163
157
  </div>
164
- <div className="relative w-full sm:w-72">
165
- <Search
166
- aria-hidden
167
- className="pointer-events-none absolute left-3 top-1/2 -translate-y-1/2 text-[var(--muted-foreground)]"
168
- size={16}
169
- />
170
- <Input
171
- aria-label="Search contacts"
172
- className="pl-9"
173
- placeholder="Search name, email, company…"
174
- value={query}
175
- onChange={(event) => setQuery(event.target.value)}
158
+ <div className="flex flex-wrap gap-2">
159
+ <input
160
+ ref={importRef}
161
+ aria-label="Import app data"
162
+ className="sr-only"
163
+ type="file"
164
+ accept="application/json,.json"
165
+ onChange={(event) => {
166
+ const file = event.currentTarget.files?.[0];
167
+ if (file) void importData(file);
168
+ }}
176
169
  />
170
+ <Button type="button" variant="outline" onClick={() => importRef.current?.click()}>
171
+ <Upload aria-hidden data-icon="inline-start" />
172
+ Import
173
+ </Button>
174
+ <Button asChild variant="outline">
175
+ <a href="/api/data/export" download>
176
+ <Download aria-hidden data-icon="inline-start" />
177
+ Export
178
+ </a>
179
+ </Button>
177
180
  </div>
178
- </CardHeader>
179
- <div aria-live="polite">
180
- {loading ? (
181
- <p className="p-8 text-center text-sm text-[var(--muted-foreground)]">
182
- Loading contacts…
183
- </p>
184
- ) : visibleContacts.length === 0 ? (
185
- <div className="p-10 text-center">
186
- <h3 className="m-0 text-base font-semibold">
187
- {contacts.length === 0 ? "No contacts yet" : "No matching contacts"}
188
- </h3>
189
- <p className="mb-0 mt-2 text-sm text-[var(--muted-foreground)]">
190
- {contacts.length === 0
191
- ? "Add the first contact with the form beside this list."
192
- : "Try a different name, email, company, or status."}
193
- </p>
181
+ </div>
182
+
183
+ <Card>
184
+ <CardHeader className="sm:flex-row sm:items-center sm:justify-between">
185
+ <div>
186
+ <CardTitle>All contacts</CardTitle>
187
+ <CardDescription>{contacts.length} {contacts.length === 1 ? "record" : "records"}</CardDescription>
188
+ </div>
189
+ <div className="relative w-full sm:w-64">
190
+ <Search aria-hidden className="pointer-events-none absolute left-3 top-1/2 size-4 -translate-y-1/2 text-muted-foreground" />
191
+ <Input
192
+ aria-label="Search contacts"
193
+ className="pl-9"
194
+ placeholder="Search contacts"
195
+ value={query}
196
+ onChange={(event) => setQuery(event.target.value)}
197
+ />
194
198
  </div>
195
- ) : (
196
- <div className="overflow-x-auto">
197
- <table className="w-full min-w-[42rem] border-collapse text-left text-sm">
198
- <thead className="bg-[var(--muted)] text-xs uppercase tracking-wide text-[var(--muted-foreground)]">
199
- <tr>
200
- <th className="px-5 py-3 font-medium" scope="col">Contact</th>
201
- <th className="px-5 py-3 font-medium" scope="col">Company</th>
202
- <th className="px-5 py-3 font-medium" scope="col">Status</th>
203
- <th className="px-5 py-3 text-right font-medium" scope="col">Actions</th>
204
- </tr>
205
- </thead>
206
- <tbody>
199
+ </CardHeader>
200
+ <div aria-live="polite">
201
+ {loading ? (
202
+ <div className="flex flex-col gap-3 p-4" aria-label="Loading contacts">
203
+ {[0, 1, 2].map((item) => <Skeleton className="h-11 w-full" key={item} />)}
204
+ </div>
205
+ ) : visibleContacts.length === 0 ? (
206
+ <Empty>
207
+ <EmptyHeader>
208
+ <EmptyTitle>{contacts.length === 0 ? "No contacts yet" : "No matching contacts"}</EmptyTitle>
209
+ <EmptyDescription>
210
+ {contacts.length === 0
211
+ ? "Add the first contact with the form beside this list."
212
+ : "Try a different name, email, company, or status."}
213
+ </EmptyDescription>
214
+ </EmptyHeader>
215
+ </Empty>
216
+ ) : (
217
+ <Table className="table-fixed sm:min-w-[40rem] sm:table-auto">
218
+ <TableHeader>
219
+ <TableRow>
220
+ <TableHead scope="col">Contact</TableHead>
221
+ <TableHead className="hidden sm:table-cell" scope="col">Company</TableHead>
222
+ <TableHead className="w-20 sm:w-auto" scope="col">Status</TableHead>
223
+ <TableHead aria-label="Actions" className="w-12 text-right sm:w-auto" scope="col">
224
+ <span className="hidden sm:inline">Actions</span>
225
+ </TableHead>
226
+ </TableRow>
227
+ </TableHeader>
228
+ <TableBody>
207
229
  {visibleContacts.map((contact) => (
208
- <tr className="border-t" key={contact.id}>
209
- <td className="px-5 py-4">
230
+ <TableRow key={contact.id}>
231
+ <TableCell>
210
232
  <strong className="block font-medium">{contact.name}</strong>
211
- <span className="text-[var(--muted-foreground)]">{contact.email}</span>
212
- </td>
213
- <td className="px-5 py-4">{contact.company || ""}</td>
214
- <td className="px-5 py-4">
215
- <span className="rounded-full bg-[var(--muted)] px-2.5 py-1 text-xs font-medium capitalize">
216
- {contact.status}
233
+ <span className="block break-all text-muted-foreground sm:inline">{contact.email}</span>
234
+ <span className="mt-1 block text-xs text-muted-foreground sm:hidden">
235
+ {contact.company || "Company not set"}
217
236
  </span>
218
- </td>
219
- <td className="px-5 py-4 text-right">
237
+ </TableCell>
238
+ <TableCell className="hidden sm:table-cell">{contact.company || "Not set"}</TableCell>
239
+ <TableCell>
240
+ <Badge variant={statusVariant[contact.status]}>{contact.status}</Badge>
241
+ </TableCell>
242
+ <TableCell className="text-right">
220
243
  <Button
221
244
  aria-label={`Delete ${contact.name}`}
222
- size="sm"
245
+ size="icon-sm"
223
246
  type="button"
224
247
  variant="ghost"
225
248
  onClick={() => void deleteContact(contact)}
226
249
  >
227
- <Trash2 aria-hidden size={16} />
250
+ <Trash2 aria-hidden />
228
251
  </Button>
229
- </td>
230
- </tr>
252
+ </TableCell>
253
+ </TableRow>
231
254
  ))}
232
- </tbody>
233
- </table>
234
- </div>
235
- )}
236
- </div>
237
- </Card>
238
- </section>
255
+ </TableBody>
256
+ </Table>
257
+ )}
258
+ </div>
259
+ </Card>
260
+ </section>
239
261
 
240
- <aside>
241
- <Card>
242
- <CardHeader>
243
- <h2 className="m-0 flex items-center gap-2 text-base font-semibold">
244
- <Plus aria-hidden size={18} />
245
- Add contact
246
- </h2>
247
- </CardHeader>
248
- <CardContent>
249
- <form className="grid gap-4" onSubmit={(event) => void createContact(event)}>
250
- <div className="grid gap-1.5">
251
- <Label htmlFor="contact-name">Name</Label>
252
- <Input
253
- id="contact-name"
254
- required
255
- autoComplete="name"
256
- value={draft.name}
257
- onChange={(event) => setDraft({ ...draft, name: event.target.value })}
258
- />
259
- </div>
260
- <div className="grid gap-1.5">
261
- <Label htmlFor="contact-email">Email</Label>
262
- <Input
263
- id="contact-email"
264
- required
265
- autoComplete="email"
266
- type="email"
267
- value={draft.email}
268
- onChange={(event) => setDraft({ ...draft, email: event.target.value })}
269
- />
270
- </div>
271
- <div className="grid gap-1.5">
272
- <Label htmlFor="contact-company">Company</Label>
273
- <Input
274
- id="contact-company"
275
- autoComplete="organization"
276
- value={draft.company}
277
- onChange={(event) => setDraft({ ...draft, company: event.target.value })}
278
- />
279
- </div>
280
- {error ? (
281
- <p className="m-0 text-sm text-[var(--destructive)]" role="alert">{error}</p>
282
- ) : null}
283
- <Button disabled={saving} type="submit">
284
- {saving ? "Adding" : "Add contact"}
285
- </Button>
286
- </form>
287
- </CardContent>
288
- </Card>
289
- </aside>
262
+ <aside>
263
+ <Card>
264
+ <CardHeader>
265
+ <CardTitle className="flex items-center gap-2">
266
+ <Plus aria-hidden className="size-4" />
267
+ Add contact
268
+ </CardTitle>
269
+ <CardDescription>Create a development record.</CardDescription>
270
+ </CardHeader>
271
+ <CardContent>
272
+ <form onSubmit={(event) => void createContact(event)}>
273
+ <FieldGroup>
274
+ <Field>
275
+ <FieldLabel htmlFor="contact-name">Name</FieldLabel>
276
+ <Input
277
+ id="contact-name"
278
+ required
279
+ autoComplete="name"
280
+ value={draft.name}
281
+ onChange={(event) => setDraft({ ...draft, name: event.target.value })}
282
+ />
283
+ </Field>
284
+ <Field>
285
+ <FieldLabel htmlFor="contact-email">Email</FieldLabel>
286
+ <Input
287
+ id="contact-email"
288
+ required
289
+ autoComplete="email"
290
+ type="email"
291
+ value={draft.email}
292
+ onChange={(event) => setDraft({ ...draft, email: event.target.value })}
293
+ />
294
+ </Field>
295
+ <Field>
296
+ <FieldLabel htmlFor="contact-company">Company</FieldLabel>
297
+ <Input
298
+ id="contact-company"
299
+ autoComplete="organization"
300
+ value={draft.company}
301
+ onChange={(event) => setDraft({ ...draft, company: event.target.value })}
302
+ />
303
+ </Field>
304
+ {error ? <Alert>{error}</Alert> : null}
305
+ <Button disabled={saving} type="submit">
306
+ {saving ? "Adding..." : "Add contact"}
307
+ </Button>
308
+ </FieldGroup>
309
+ </form>
310
+ </CardContent>
311
+ </Card>
312
+ </aside>
313
+ </main>
290
314
  </div>
291
315
  );
292
316
  }
@@ -0,0 +1,12 @@
1
+ import { cn } from "@/lib/utils";
2
+ import * as React from "react";
3
+
4
+ export function Alert({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) {
5
+ return (
6
+ <div
7
+ role="alert"
8
+ className={cn("rounded-md border border-destructive/25 bg-destructive/10 px-3 py-2 text-sm text-destructive", className)}
9
+ {...props}
10
+ />
11
+ );
12
+ }
@@ -0,0 +1,27 @@
1
+ import { cn } from "@/lib/utils";
2
+ import { cva, type VariantProps } from "class-variance-authority";
3
+ import * as React from "react";
4
+
5
+ const badgeVariants = cva(
6
+ "inline-flex items-center rounded-sm border px-1.5 py-0.5 text-xs font-medium",
7
+ {
8
+ variants: {
9
+ variant: {
10
+ default: "border-transparent bg-primary text-primary-foreground",
11
+ secondary: "border-transparent bg-secondary text-secondary-foreground",
12
+ outline: "text-foreground",
13
+ success: "border-primary/20 bg-accent text-accent-foreground",
14
+ destructive: "border-destructive/20 bg-destructive/10 text-destructive",
15
+ },
16
+ },
17
+ defaultVariants: { variant: "secondary" },
18
+ },
19
+ );
20
+
21
+ export function Badge({
22
+ className,
23
+ variant,
24
+ ...props
25
+ }: React.HTMLAttributes<HTMLSpanElement> & VariantProps<typeof badgeVariants>) {
26
+ return <span className={cn(badgeVariants({ variant }), className)} {...props} />;
27
+ }
@@ -4,18 +4,20 @@ import { cva, type VariantProps } from "class-variance-authority";
4
4
  import * as React from "react";
5
5
 
6
6
  const buttonVariants = cva(
7
- "inline-flex min-h-10 items-center justify-center gap-2 rounded-lg px-4 text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--ring)] disabled:pointer-events-none disabled:opacity-50",
7
+ "inline-flex shrink-0 items-center justify-center gap-2 rounded-md text-foreground text-sm font-medium transition-colors outline-none focus-visible:ring-2 focus-visible:ring-ring/60 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4",
8
8
  {
9
9
  variants: {
10
10
  variant: {
11
- default: "bg-[var(--primary)] text-[var(--primary-foreground)] hover:brightness-95",
12
- outline: "border bg-[var(--card)] hover:bg-[var(--muted)]",
13
- ghost: "hover:bg-[var(--muted)]",
14
- destructive: "bg-[var(--destructive)] text-white hover:brightness-95",
11
+ default: "bg-primary text-primary-foreground hover:bg-primary/90",
12
+ outline: "border border-input bg-background hover:bg-accent hover:text-accent-foreground",
13
+ secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80",
14
+ ghost: "hover:bg-accent hover:text-accent-foreground",
15
+ destructive: "bg-destructive text-destructive-foreground hover:bg-destructive/90",
15
16
  },
16
17
  size: {
17
- default: "h-10",
18
- sm: "h-9 px-3",
18
+ default: "h-9 px-3.5",
19
+ sm: "h-8 px-3 text-xs",
20
+ "icon-sm": "size-8",
19
21
  },
20
22
  },
21
23
  defaultVariants: {
@@ -4,16 +4,28 @@ import * as React from "react";
4
4
  export function Card({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) {
5
5
  return (
6
6
  <section
7
- className={cn("rounded-xl border bg-[var(--card)] text-[var(--card-foreground)] shadow-sm", className)}
7
+ className={cn("rounded-lg border bg-card text-card-foreground", className)}
8
8
  {...props}
9
9
  />
10
10
  );
11
11
  }
12
12
 
13
13
  export function CardHeader({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) {
14
- return <div className={cn("border-b px-5 py-4", className)} {...props} />;
14
+ return <div className={cn("flex flex-col gap-1 border-b px-4 py-3", className)} {...props} />;
15
15
  }
16
16
 
17
17
  export function CardContent({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) {
18
- return <div className={cn("p-5", className)} {...props} />;
18
+ return <div className={cn("p-4", className)} {...props} />;
19
+ }
20
+
21
+ export function CardTitle({ className, ...props }: React.HTMLAttributes<HTMLHeadingElement>) {
22
+ return <h2 className={cn("m-0 text-base font-semibold", className)} {...props} />;
23
+ }
24
+
25
+ export function CardDescription({ className, ...props }: React.HTMLAttributes<HTMLParagraphElement>) {
26
+ return <p className={cn("m-0 text-sm text-muted-foreground", className)} {...props} />;
27
+ }
28
+
29
+ export function CardFooter({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) {
30
+ return <div className={cn("flex items-center border-t px-4 py-3", className)} {...props} />;
19
31
  }
@@ -0,0 +1,18 @@
1
+ import { cn } from "@/lib/utils";
2
+ import * as React from "react";
3
+
4
+ export function Empty({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) {
5
+ return <div className={cn("flex min-h-44 flex-col items-center justify-center gap-3 p-6 text-center", className)} {...props} />;
6
+ }
7
+
8
+ export function EmptyHeader({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) {
9
+ return <div className={cn("flex max-w-sm flex-col gap-1", className)} {...props} />;
10
+ }
11
+
12
+ export function EmptyTitle({ className, ...props }: React.HTMLAttributes<HTMLHeadingElement>) {
13
+ return <h3 className={cn("m-0 text-sm font-semibold", className)} {...props} />;
14
+ }
15
+
16
+ export function EmptyDescription({ className, ...props }: React.HTMLAttributes<HTMLParagraphElement>) {
17
+ return <p className={cn("m-0 text-sm text-muted-foreground", className)} {...props} />;
18
+ }
@@ -0,0 +1,23 @@
1
+ import { Label } from "@/components/ui/label";
2
+ import { cn } from "@/lib/utils";
3
+ import * as React from "react";
4
+
5
+ export function FieldGroup({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) {
6
+ return <div className={cn("flex flex-col gap-4", className)} {...props} />;
7
+ }
8
+
9
+ export function Field({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) {
10
+ return <div className={cn("flex flex-col gap-1.5", className)} {...props} />;
11
+ }
12
+
13
+ export function FieldLabel({ className, ...props }: React.ComponentProps<typeof Label>) {
14
+ return <Label className={className} {...props} />;
15
+ }
16
+
17
+ export function FieldDescription({ className, ...props }: React.HTMLAttributes<HTMLParagraphElement>) {
18
+ return <p className={cn("m-0 text-xs text-muted-foreground", className)} {...props} />;
19
+ }
20
+
21
+ export function FieldError({ className, ...props }: React.HTMLAttributes<HTMLParagraphElement>) {
22
+ return <p className={cn("m-0 text-xs text-destructive", className)} {...props} />;
23
+ }
@@ -7,7 +7,7 @@ export const Input = React.forwardRef<HTMLInputElement, React.ComponentProps<"in
7
7
  ref={ref}
8
8
  type={type}
9
9
  className={cn(
10
- "flex h-10 w-full rounded-lg border bg-white px-3 py-2 text-sm outline-none placeholder:text-[var(--muted-foreground)] focus:ring-2 focus:ring-[var(--ring)] disabled:opacity-50",
10
+ "flex h-9 w-full rounded-md border border-input bg-background px-3 py-1 text-sm outline-none transition-shadow placeholder:text-muted-foreground focus-visible:ring-2 focus-visible:ring-ring/60 disabled:cursor-not-allowed disabled:opacity-50",
11
11
  className,
12
12
  )}
13
13
  {...props}
@@ -2,5 +2,5 @@ import { cn } from "@/lib/utils";
2
2
  import * as React from "react";
3
3
 
4
4
  export function Label({ className, ...props }: React.LabelHTMLAttributes<HTMLLabelElement>) {
5
- return <label className={cn("text-sm font-medium", className)} {...props} />;
5
+ return <label className={cn("text-sm font-medium leading-none", className)} {...props} />;
6
6
  }
@@ -0,0 +1,6 @@
1
+ import { cn } from "@/lib/utils";
2
+ import * as React from "react";
3
+
4
+ export function Separator({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) {
5
+ return <div role="separator" className={cn("h-px w-full bg-border", className)} {...props} />;
6
+ }
@@ -0,0 +1,6 @@
1
+ import { cn } from "@/lib/utils";
2
+ import * as React from "react";
3
+
4
+ export function Skeleton({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) {
5
+ return <div className={cn("animate-pulse rounded-sm bg-muted", className)} {...props} />;
6
+ }
@@ -0,0 +1,30 @@
1
+ import { cn } from "@/lib/utils";
2
+ import * as React from "react";
3
+
4
+ export function Table({ className, ...props }: React.TableHTMLAttributes<HTMLTableElement>) {
5
+ return (
6
+ <div className="w-full overflow-x-auto">
7
+ <table className={cn("w-full caption-bottom text-sm", className)} {...props} />
8
+ </div>
9
+ );
10
+ }
11
+
12
+ export function TableHeader({ className, ...props }: React.HTMLAttributes<HTMLTableSectionElement>) {
13
+ return <thead className={cn("bg-muted/60 [&_tr]:border-b", className)} {...props} />;
14
+ }
15
+
16
+ export function TableBody({ className, ...props }: React.HTMLAttributes<HTMLTableSectionElement>) {
17
+ return <tbody className={cn("[&_tr:last-child]:border-0", className)} {...props} />;
18
+ }
19
+
20
+ export function TableRow({ className, ...props }: React.HTMLAttributes<HTMLTableRowElement>) {
21
+ return <tr className={cn("border-b transition-colors hover:bg-muted/40", className)} {...props} />;
22
+ }
23
+
24
+ export function TableHead({ className, ...props }: React.ThHTMLAttributes<HTMLTableCellElement>) {
25
+ return <th className={cn("h-9 px-4 text-left align-middle text-xs font-medium text-muted-foreground", className)} {...props} />;
26
+ }
27
+
28
+ export function TableCell({ className, ...props }: React.TdHTMLAttributes<HTMLTableCellElement>) {
29
+ return <td className={cn("px-4 py-3 align-middle", className)} {...props} />;
30
+ }
@@ -0,0 +1,20 @@
1
+ {
2
+ "$schema": "https://ui.shadcn.com/schema.json",
3
+ "style": "new-york",
4
+ "rsc": true,
5
+ "tsx": true,
6
+ "tailwind": {
7
+ "config": "",
8
+ "css": "app/globals.css",
9
+ "baseColor": "neutral",
10
+ "cssVariables": true
11
+ },
12
+ "iconLibrary": "lucide",
13
+ "aliases": {
14
+ "components": "@/components",
15
+ "utils": "@/lib/utils",
16
+ "ui": "@/components/ui",
17
+ "lib": "@/lib",
18
+ "hooks": "@/hooks"
19
+ }
20
+ }
@@ -1,6 +1,6 @@
1
1
  /// <reference types="next" />
2
2
  /// <reference types="next/image-types/global" />
3
- import "./.next/dev/types/routes.d.ts";
3
+ import "./.next/types/routes.d.ts";
4
4
 
5
5
  // NOTE: This file should not be edited
6
6
  // see https://nextjs.org/docs/app/api-reference/config/typescript for more information.