@tanstack/cta-framework-react-cra 0.45.0 → 0.46.1

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 (25) hide show
  1. package/add-ons/ai/assets/src/routes/demo/ai-chat.tsx +2 -2
  2. package/examples/resume/README.md +82 -0
  3. package/examples/resume/assets/content/education/code-school.md +17 -0
  4. package/examples/resume/assets/content/jobs/freelance.md +13 -0
  5. package/examples/resume/assets/content/jobs/initech-junior.md +20 -0
  6. package/examples/resume/assets/content/jobs/initech-lead.md +29 -0
  7. package/examples/resume/assets/content/jobs/initrode-senior.md +28 -0
  8. package/examples/resume/assets/content-collections.ts +36 -0
  9. package/examples/resume/assets/public/headshot-on-white.jpg +0 -0
  10. package/examples/resume/assets/src/components/ResumeAssistant.tsx +193 -0
  11. package/examples/resume/assets/src/components/ResumeAssistantButton.tsx +20 -0
  12. package/examples/resume/assets/src/components/ui/badge.tsx +46 -0
  13. package/examples/resume/assets/src/components/ui/card.tsx +92 -0
  14. package/examples/resume/assets/src/components/ui/checkbox.tsx +30 -0
  15. package/examples/resume/assets/src/components/ui/hover-card.tsx +44 -0
  16. package/examples/resume/assets/src/components/ui/separator.tsx +26 -0
  17. package/examples/resume/assets/src/lib/resume-ai-hook.ts +21 -0
  18. package/examples/resume/assets/src/lib/resume-tools.ts +165 -0
  19. package/examples/resume/assets/src/lib/utils.ts +6 -0
  20. package/examples/resume/assets/src/routes/api.resume-chat.ts +110 -0
  21. package/examples/resume/assets/src/routes/index.tsx +220 -0
  22. package/examples/resume/assets/src/styles.css +138 -0
  23. package/examples/resume/info.json +30 -0
  24. package/examples/resume/package.json +27 -0
  25. package/package.json +2 -2
@@ -20,7 +20,7 @@ import GuitarRecommendation from '@/components/demo-GuitarRecommendation'
20
20
 
21
21
  import './ai-chat.css'
22
22
 
23
- function InitalLayout({ children }: { children: React.ReactNode }) {
23
+ function InitialLayout({ children }: { children: React.ReactNode }) {
24
24
  return (
25
25
  <div className="flex-1 flex items-center justify-center px-4">
26
26
  <div className="text-center max-w-3xl mx-auto w-full">
@@ -187,7 +187,7 @@ function ChatPage() {
187
187
  }
188
188
  }
189
189
 
190
- const Layout = messages.length ? ChattingLayout : InitalLayout
190
+ const Layout = messages.length ? ChattingLayout : InitialLayout
191
191
 
192
192
  return (
193
193
  <div className="relative flex h-[calc(100vh-80px)] bg-gray-900">
@@ -0,0 +1,82 @@
1
+ # Resume Example
2
+
3
+ A professional resume template built with TanStack Start and content-collections for Netlify deployment.
4
+
5
+ ## Features
6
+
7
+ - **Content Collections**: Work experience and education managed as markdown files
8
+ - **Skills Filter**: Interactive sidebar to filter jobs by skills/technologies
9
+ - **Beautiful UI**: Modern design with shadcn/ui components
10
+ - **SSR Ready**: Full server-side rendering with TanStack Start
11
+
12
+ ## Project Structure
13
+
14
+ ```
15
+ ├── content/
16
+ │ ├── jobs/ # Work experience entries
17
+ │ └── education/ # Education entries
18
+ ├── src/
19
+ │ ├── components/
20
+ │ │ └── ui/ # Shadcn UI components
21
+ │ │ ├── badge.tsx
22
+ │ │ ├── card.tsx
23
+ │ │ ├── checkbox.tsx
24
+ │ │ ├── hover-card.tsx
25
+ │ │ └── separator.tsx
26
+ │ ├── lib/
27
+ │ │ └── utils.ts # Utility functions
28
+ │ └── routes/
29
+ │ ├── __root.tsx # Root layout
30
+ │ └── index.tsx # Resume page
31
+ └── public/
32
+ └── headshot-on-white.jpg
33
+ ```
34
+
35
+ ## Adding Work Experience
36
+
37
+ Create a new markdown file in `content/jobs/` with the following frontmatter:
38
+
39
+ ```markdown
40
+ ---
41
+ jobTitle: Your Job Title
42
+ company: Company Name
43
+ location: City, State
44
+ startDate: 2024-01-01
45
+ endDate: 2024-12-31 # Optional - omit for current position
46
+ summary: Brief summary of your role
47
+ tags:
48
+ - React
49
+ - TypeScript
50
+ - Web Development
51
+ ---
52
+
53
+ Detailed description of your responsibilities and achievements...
54
+ ```
55
+
56
+ ## Adding Education
57
+
58
+ Create a new markdown file in `content/education/`:
59
+
60
+ ```markdown
61
+ ---
62
+ school: School Name
63
+ summary: Degree or Program Name
64
+ startDate: 2020-01-01
65
+ endDate: 2024-01-01
66
+ tags:
67
+ - Relevant
68
+ - Skills
69
+ ---
70
+
71
+ Details about your education...
72
+ ```
73
+
74
+ ## Development
75
+
76
+ ```bash
77
+ # Start development server
78
+ npm run dev
79
+
80
+ # Build for production
81
+ npm run build
82
+ ```
@@ -0,0 +1,17 @@
1
+ ---
2
+ school: Code School
3
+ summary: Full Stack Development
4
+ startDate: 2020-01-01
5
+ endDate: 2020-12-31
6
+ tags:
7
+ [
8
+ "Full Stack Development",
9
+ "JavaScript",
10
+ "React",
11
+ "Node.js",
12
+ "Express",
13
+ "MongoDB",
14
+ ]
15
+ ---
16
+
17
+ Completed a comprehensive full stack development course covering JavaScript, React, Node.js, Express, and MongoDB. Gained hands-on experience with modern web development frameworks and databases while building a full stack application.
@@ -0,0 +1,13 @@
1
+ ---
2
+ jobTitle: Frontend Development Consultant
3
+ company: Freelance
4
+ location: Remote
5
+ startDate: 2020-01-01
6
+ endDate: 2020-12-31
7
+ summary: Independent frontend development consultant working with multiple clients on web applications and sites
8
+ description:
9
+ tags:
10
+ ["React", "TypeScript", "Frontend Development", "JavaScript", "CSS", "HTML"]
11
+ ---
12
+
13
+ Provided expert frontend development services to various clients, specializing in React, TypeScript, and modern web technologies. Delivered responsive and performant web applications while maintaining high code quality and best practices.
@@ -0,0 +1,20 @@
1
+ ---
2
+ jobTitle: Junior Frontend Developer
3
+ company: IniTech
4
+ location: Remote
5
+ startDate: 2021-01-01
6
+ endDate: 2021-12-31
7
+ summary: Junior frontend developer working on React-based web applications using modern development practices
8
+ description:
9
+ tags:
10
+ [
11
+ "React",
12
+ "TypeScript",
13
+ "Frontend Development",
14
+ "JavaScript",
15
+ "TanStack",
16
+ "Web Development",
17
+ ]
18
+ ---
19
+
20
+ Worked as a junior frontend developer at IniTech, contributing to React-based web applications. Collaborated with the development team to implement responsive user interfaces, integrate REST APIs, and maintain code quality. Gained hands-on experience with modern frontend technologies and development workflows while working on production applications.
@@ -0,0 +1,29 @@
1
+ ---
2
+ jobTitle: Frontend Team Lead
3
+ company: IniTech
4
+ location: Remote
5
+ startDate: 2022-01-01
6
+ endDate: 2022-12-31
7
+ summary: Led a cross-functional team of three engineers in developing and maintaining IniTech's marketing platform, driving technical decisions and implementing modern development practices
8
+ description:
9
+ tags:
10
+ [
11
+ "React",
12
+ "TypeScript",
13
+ "Team Leadership",
14
+ "Frontend Architecture",
15
+ "Technical Leadership",
16
+ "Project Management",
17
+ "TanStack",
18
+ "Web Development",
19
+ ]
20
+ ---
21
+
22
+ Led the development and maintenance of IniTech's marketing platform as Frontend Team Lead, managing a cross-functional team of three engineers including a backend developer and a DevOps engineer. Key achievements and responsibilities included:
23
+
24
+ - Architected and implemented a modern React-based marketing platform using TypeScript and TanStack, resulting in a 40% improvement in page load times
25
+ - Managed and mentored a team of three engineers, conducting regular code reviews, sprint planning, and technical guidance
26
+ - Collaborated with marketing stakeholders to define and implement new features while maintaining high code quality and performance standards
27
+ - Established CI/CD pipelines and development workflows in partnership with DevOps, reducing deployment time by 60%
28
+ - Coordinated with the backend developer to design and implement RESTful APIs, ensuring seamless integration between frontend and backend systems
29
+ - Introduced modern development practices including automated testing, component documentation, and performance monitoring
@@ -0,0 +1,28 @@
1
+ ---
2
+ jobTitle: Senior Frontend Developer
3
+ company: Initrode
4
+ location: Remote
5
+ startDate: 2024-01-01
6
+ summary: Developed and architected real-time data visualization features for Initrode's enterprise dashboard platform, implementing a robust WebSocket layer and optimizing performance for large-scale data streams
7
+ description:
8
+ tags:
9
+ [
10
+ "React",
11
+ "TypeScript",
12
+ "WebSockets",
13
+ "Real-time Data",
14
+ "System Architecture",
15
+ "Performance Optimization",
16
+ "TanStack",
17
+ "Web Development",
18
+ ]
19
+ ---
20
+
21
+ Served as a Senior Frontend Developer on Initrode's Dashboard team, focusing on building high-performance, real-time data visualization features for enterprise clients. Key achievements and responsibilities included:
22
+
23
+ - Designed and implemented a WebSocket-based real-time data transport layer, enabling live updates for dashboard components with sub-100ms latency
24
+ - Architected and developed a modular dashboard framework using React and TypeScript, supporting dynamic widget composition and real-time data streams
25
+ - Optimized frontend performance for handling large-scale data sets, implementing efficient data structures and virtualization techniques that improved rendering performance by 65%
26
+ - Built reusable charting and visualization components using D3.js and TanStack libraries, supporting real-time updates and complex data transformations
27
+ - Implemented client-side data caching and state management strategies, reducing server load by 40% while maintaining data consistency
28
+ - Developed automated testing suites for WebSocket connections and real-time data flow, achieving 90% test coverage for critical paths
@@ -0,0 +1,36 @@
1
+ import { defineCollection, defineConfig } from '@content-collections/core'
2
+ import { z } from 'zod'
3
+
4
+ const jobs = defineCollection({
5
+ name: 'jobs',
6
+ directory: 'content/jobs',
7
+ include: '**/*.md',
8
+ schema: z.object({
9
+ jobTitle: z.string(),
10
+ summary: z.string(),
11
+ startDate: z.string(),
12
+ endDate: z.string().optional(),
13
+ company: z.string(),
14
+ location: z.string(),
15
+ tags: z.array(z.string()),
16
+ content: z.string(),
17
+ }),
18
+ })
19
+
20
+ const education = defineCollection({
21
+ name: 'education',
22
+ directory: 'content/education',
23
+ include: '**/*.md',
24
+ schema: z.object({
25
+ school: z.string(),
26
+ summary: z.string(),
27
+ startDate: z.string(),
28
+ endDate: z.string().optional(),
29
+ tags: z.array(z.string()),
30
+ content: z.string(),
31
+ }),
32
+ })
33
+
34
+ export default defineConfig({
35
+ collections: [jobs, education],
36
+ })
@@ -0,0 +1,193 @@
1
+ import { useEffect, useRef, useState } from "react";
2
+ import { Send, X, Briefcase, UserCheck } from "lucide-react";
3
+ import { Streamdown } from "streamdown";
4
+ import { Store } from "@tanstack/store";
5
+
6
+ import { useResumeChat } from "@/lib/resume-ai-hook";
7
+ import type { ResumeChatMessages } from "@/lib/resume-ai-hook";
8
+
9
+ function Messages({ messages }: { messages: ResumeChatMessages }) {
10
+ const messagesContainerRef = useRef<HTMLDivElement>(null);
11
+
12
+ useEffect(() => {
13
+ if (messagesContainerRef.current) {
14
+ messagesContainerRef.current.scrollTop =
15
+ messagesContainerRef.current.scrollHeight;
16
+ }
17
+ }, [messages]);
18
+
19
+ if (!messages.length) {
20
+ return (
21
+ <div className="flex-1 flex flex-col items-center justify-center text-slate-300/60 text-sm px-6 py-8">
22
+ <div className="relative mb-4">
23
+ <Briefcase className="w-12 h-12 text-blue-400/40 animate-pulse" />
24
+ <UserCheck className="w-6 h-6 text-purple-400/60 absolute -bottom-1 -right-1" />
25
+ </div>
26
+ <p className="text-center text-slate-200/80 font-medium">
27
+ Welcome, Recruiter!
28
+ </p>
29
+ <p className="text-xs text-slate-300/40 mt-2 text-center max-w-[200px]">
30
+ Ask about skills, experience, or qualifications...
31
+ </p>
32
+ </div>
33
+ );
34
+ }
35
+
36
+ return (
37
+ <div ref={messagesContainerRef} className="flex-1 overflow-y-auto">
38
+ {messages.map(({ id, role, parts }) => (
39
+ <div
40
+ key={id}
41
+ className={`py-3 ${
42
+ role === "assistant"
43
+ ? "bg-linear-to-r from-blue-500/5 via-purple-500/5 to-slate-500/5"
44
+ : "bg-transparent"
45
+ }`}
46
+ >
47
+ {parts.map((part, index) => {
48
+ if (part.type === "text" && part.content) {
49
+ return (
50
+ <div key={index} className="flex items-start gap-3 px-4">
51
+ {role === "assistant" ? (
52
+ <div className="w-7 h-7 rounded-full bg-linear-to-br from-blue-500 via-purple-500 to-slate-600 flex items-center justify-center text-xs font-bold text-white flex-shrink-0 shadow-lg shadow-blue-500/20">
53
+ <Briefcase className="w-4 h-4" />
54
+ </div>
55
+ ) : (
56
+ <div className="w-7 h-7 rounded-full bg-slate-600 flex items-center justify-center text-xs font-medium text-white flex-shrink-0">
57
+ You
58
+ </div>
59
+ )}
60
+ <div className="flex-1 min-w-0 text-slate-100 prose dark:prose-invert max-w-none prose-sm prose-p:text-slate-100 prose-headings:text-slate-200 prose-strong:text-slate-300">
61
+ <Streamdown>{part.content}</Streamdown>
62
+ </div>
63
+ </div>
64
+ );
65
+ }
66
+ return null;
67
+ })}
68
+ </div>
69
+ ))}
70
+ </div>
71
+ );
72
+ }
73
+
74
+ // Export store for header control
75
+ export const showResumeAssistant = new Store(false);
76
+
77
+ export default function ResumeAssistant() {
78
+ const [isOpen, setIsOpen] = useState(false);
79
+ const { messages, sendMessage, isLoading } = useResumeChat();
80
+ const [input, setInput] = useState("");
81
+
82
+ // Sync with store for header control
83
+ useEffect(() => {
84
+ return showResumeAssistant.subscribe(() => {
85
+ setIsOpen(showResumeAssistant.state);
86
+ });
87
+ }, []);
88
+
89
+ const handleToggle = () => {
90
+ const newState = !isOpen;
91
+ setIsOpen(newState);
92
+ showResumeAssistant.setState(() => newState);
93
+ };
94
+
95
+ const handleSend = () => {
96
+ if (input.trim()) {
97
+ sendMessage(input);
98
+ setInput("");
99
+ }
100
+ };
101
+
102
+ if (!isOpen) return null;
103
+
104
+ return (
105
+ <div className="fixed top-20 right-4 z-[100] w-[400px] h-[520px] rounded-3xl shadow-2xl flex flex-col overflow-hidden border border-blue-500/20 backdrop-blur-xl bg-linear-to-b from-slate-900/98 via-slate-900/95 to-slate-800/98">
106
+ {/* Decorative top gradient */}
107
+ <div className="absolute top-0 left-0 right-0 h-32 bg-linear-to-b from-blue-500/10 via-purple-500/5 to-transparent pointer-events-none" />
108
+
109
+ {/* Header */}
110
+ <div className="relative flex items-center justify-between p-4 border-b border-blue-500/10">
111
+ <div className="flex items-center gap-3">
112
+ <div className="w-10 h-10 rounded-2xl bg-linear-to-br from-blue-500 via-purple-500 to-slate-600 flex items-center justify-center shadow-lg shadow-blue-500/30 rotate-3 hover:rotate-0 transition-transform">
113
+ <Briefcase className="w-5 h-5 text-white" />
114
+ </div>
115
+ <div>
116
+ <h3 className="font-bold text-slate-200 text-base tracking-tight">
117
+ Resume Assistant
118
+ </h3>
119
+ <p className="text-xs text-blue-300/50">Candidate Evaluation AI</p>
120
+ </div>
121
+ </div>
122
+ <button
123
+ onClick={handleToggle}
124
+ className="text-slate-300/50 hover:text-slate-100 transition-colors p-2 hover:bg-white/5 rounded-xl"
125
+ >
126
+ <X className="w-5 h-5" />
127
+ </button>
128
+ </div>
129
+
130
+ {/* Messages */}
131
+ <Messages messages={messages} />
132
+
133
+ {/* Loading indicator */}
134
+ {isLoading && (
135
+ <div className="px-4 py-3 border-t border-blue-500/10">
136
+ <div className="flex items-center gap-2 text-blue-400/80 text-xs">
137
+ <div className="flex gap-1">
138
+ <span className="w-2 h-2 bg-blue-400 rounded-full animate-bounce [animation-delay:-0.3s]"></span>
139
+ <span className="w-2 h-2 bg-purple-400 rounded-full animate-bounce [animation-delay:-0.15s]"></span>
140
+ <span className="w-2 h-2 bg-slate-400 rounded-full animate-bounce"></span>
141
+ </div>
142
+ <span className="font-medium">Analyzing experience...</span>
143
+ </div>
144
+ </div>
145
+ )}
146
+
147
+ {/* Input */}
148
+ <div className="relative p-4 border-t border-blue-500/10 bg-slate-900/50">
149
+ <form
150
+ onSubmit={(e) => {
151
+ e.preventDefault();
152
+ handleSend();
153
+ }}
154
+ >
155
+ <div className="relative">
156
+ <textarea
157
+ value={input}
158
+ onChange={(e) => setInput(e.target.value)}
159
+ placeholder="Ask about skills, experience, or qualifications..."
160
+ disabled={isLoading}
161
+ className="w-full rounded-2xl border border-blue-500/20 bg-slate-800/50 pl-4 pr-12 py-3 text-sm text-slate-100 placeholder-slate-300/30 focus:outline-none focus:ring-2 focus:ring-blue-500/40 focus:border-transparent resize-none overflow-hidden disabled:opacity-50 transition-all"
162
+ rows={1}
163
+ style={{ minHeight: "48px", maxHeight: "100px" }}
164
+ onInput={(e) => {
165
+ const target = e.target as HTMLTextAreaElement;
166
+ target.style.height = "auto";
167
+ target.style.height = Math.min(target.scrollHeight, 100) + "px";
168
+ }}
169
+ onKeyDown={(e) => {
170
+ if (
171
+ e.key === "Enter" &&
172
+ !e.shiftKey &&
173
+ input.trim() &&
174
+ !isLoading
175
+ ) {
176
+ e.preventDefault();
177
+ handleSend();
178
+ }
179
+ }}
180
+ />
181
+ <button
182
+ type="submit"
183
+ disabled={!input.trim() || isLoading}
184
+ className="absolute right-3 top-1/2 -translate-y-1/2 p-2 rounded-xl bg-linear-to-r from-blue-500 to-purple-500 text-white disabled:opacity-30 disabled:bg-gray-600 disabled:from-gray-600 disabled:to-gray-600 transition-all hover:shadow-lg hover:shadow-blue-500/20"
185
+ >
186
+ <Send className="w-4 h-4" />
187
+ </button>
188
+ </div>
189
+ </form>
190
+ </div>
191
+ </div>
192
+ );
193
+ }
@@ -0,0 +1,20 @@
1
+ import { Briefcase, ChevronRight } from "lucide-react";
2
+ import { showResumeAssistant } from "./ResumeAssistant";
3
+
4
+ export default function RemyButton() {
5
+ return (
6
+ <div className="px-2 mb-2 w-full">
7
+ <button
8
+ onClick={() => showResumeAssistant.setState(true)}
9
+ className="w-full flex items-center justify-between px-4 py-2.5 rounded-lg bg-linear-to-r from-blue-500 to-purple-800 text-white hover:opacity-90 transition-opacity"
10
+ aria-label="Open Resume Assistant"
11
+ >
12
+ <div className="flex items-center gap-2">
13
+ <Briefcase size={24} />
14
+ <span className="text-sm">Resume Assistant</span>
15
+ </div>
16
+ <ChevronRight className="w-4 h-4" />
17
+ </button>
18
+ </div>
19
+ );
20
+ }
@@ -0,0 +1,46 @@
1
+ import * as React from "react"
2
+ import { Slot } from "@radix-ui/react-slot"
3
+ import { cva, type VariantProps } from "class-variance-authority"
4
+
5
+ import { cn } from "@/lib/utils"
6
+
7
+ const badgeVariants = cva(
8
+ "inline-flex items-center justify-center rounded-md border px-2 py-0.5 text-xs font-medium w-fit whitespace-nowrap shrink-0 [&>svg]:size-3 gap-1 [&>svg]:pointer-events-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive transition-[color,box-shadow] overflow-hidden",
9
+ {
10
+ variants: {
11
+ variant: {
12
+ default:
13
+ "border-transparent bg-primary text-primary-foreground [a&]:hover:bg-primary/90",
14
+ secondary:
15
+ "border-transparent bg-secondary text-secondary-foreground [a&]:hover:bg-secondary/90",
16
+ destructive:
17
+ "border-transparent bg-destructive text-white [a&]:hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60",
18
+ outline:
19
+ "text-foreground [a&]:hover:bg-accent [a&]:hover:text-accent-foreground",
20
+ },
21
+ },
22
+ defaultVariants: {
23
+ variant: "default",
24
+ },
25
+ }
26
+ )
27
+
28
+ function Badge({
29
+ className,
30
+ variant,
31
+ asChild = false,
32
+ ...props
33
+ }: React.ComponentProps<"span"> &
34
+ VariantProps<typeof badgeVariants> & { asChild?: boolean }) {
35
+ const Comp = asChild ? Slot : "span"
36
+
37
+ return (
38
+ <Comp
39
+ data-slot="badge"
40
+ className={cn(badgeVariants({ variant }), className)}
41
+ {...props}
42
+ />
43
+ )
44
+ }
45
+
46
+ export { Badge, badgeVariants }
@@ -0,0 +1,92 @@
1
+ import * as React from "react"
2
+
3
+ import { cn } from "@/lib/utils"
4
+
5
+ function Card({ className, ...props }: React.ComponentProps<"div">) {
6
+ return (
7
+ <div
8
+ data-slot="card"
9
+ className={cn(
10
+ "bg-card text-card-foreground flex flex-col gap-6 rounded-xl border py-6 shadow-sm",
11
+ className
12
+ )}
13
+ {...props}
14
+ />
15
+ )
16
+ }
17
+
18
+ function CardHeader({ className, ...props }: React.ComponentProps<"div">) {
19
+ return (
20
+ <div
21
+ data-slot="card-header"
22
+ className={cn(
23
+ "@container/card-header grid auto-rows-min grid-rows-[auto_auto] items-start gap-1.5 px-6 has-data-[slot=card-action]:grid-cols-[1fr_auto] [.border-b]:pb-6",
24
+ className
25
+ )}
26
+ {...props}
27
+ />
28
+ )
29
+ }
30
+
31
+ function CardTitle({ className, ...props }: React.ComponentProps<"div">) {
32
+ return (
33
+ <div
34
+ data-slot="card-title"
35
+ className={cn("leading-none font-semibold", className)}
36
+ {...props}
37
+ />
38
+ )
39
+ }
40
+
41
+ function CardDescription({ className, ...props }: React.ComponentProps<"div">) {
42
+ return (
43
+ <div
44
+ data-slot="card-description"
45
+ className={cn("text-muted-foreground text-sm", className)}
46
+ {...props}
47
+ />
48
+ )
49
+ }
50
+
51
+ function CardAction({ className, ...props }: React.ComponentProps<"div">) {
52
+ return (
53
+ <div
54
+ data-slot="card-action"
55
+ className={cn(
56
+ "col-start-2 row-span-2 row-start-1 self-start justify-self-end",
57
+ className
58
+ )}
59
+ {...props}
60
+ />
61
+ )
62
+ }
63
+
64
+ function CardContent({ className, ...props }: React.ComponentProps<"div">) {
65
+ return (
66
+ <div
67
+ data-slot="card-content"
68
+ className={cn("px-6", className)}
69
+ {...props}
70
+ />
71
+ )
72
+ }
73
+
74
+ function CardFooter({ className, ...props }: React.ComponentProps<"div">) {
75
+ return (
76
+ <div
77
+ data-slot="card-footer"
78
+ className={cn("flex items-center px-6 [.border-t]:pt-6", className)}
79
+ {...props}
80
+ />
81
+ )
82
+ }
83
+
84
+ export {
85
+ Card,
86
+ CardHeader,
87
+ CardFooter,
88
+ CardTitle,
89
+ CardAction,
90
+ CardDescription,
91
+ CardContent,
92
+ }
@@ -0,0 +1,30 @@
1
+ import * as React from "react"
2
+ import * as CheckboxPrimitive from "@radix-ui/react-checkbox"
3
+ import { CheckIcon } from "lucide-react"
4
+
5
+ import { cn } from "@/lib/utils"
6
+
7
+ function Checkbox({
8
+ className,
9
+ ...props
10
+ }: React.ComponentProps<typeof CheckboxPrimitive.Root>) {
11
+ return (
12
+ <CheckboxPrimitive.Root
13
+ data-slot="checkbox"
14
+ className={cn(
15
+ "peer border-input dark:bg-input/30 data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground dark:data-[state=checked]:bg-primary data-[state=checked]:border-primary focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive size-4 shrink-0 rounded-[4px] border shadow-xs transition-shadow outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50",
16
+ className
17
+ )}
18
+ {...props}
19
+ >
20
+ <CheckboxPrimitive.Indicator
21
+ data-slot="checkbox-indicator"
22
+ className="flex items-center justify-center text-current transition-none"
23
+ >
24
+ <CheckIcon className="size-3.5" />
25
+ </CheckboxPrimitive.Indicator>
26
+ </CheckboxPrimitive.Root>
27
+ )
28
+ }
29
+
30
+ export { Checkbox }
@@ -0,0 +1,44 @@
1
+ "use client"
2
+
3
+ import * as React from "react"
4
+ import * as HoverCardPrimitive from "@radix-ui/react-hover-card"
5
+
6
+ import { cn } from "@/lib/utils"
7
+
8
+ function HoverCard({
9
+ ...props
10
+ }: React.ComponentProps<typeof HoverCardPrimitive.Root>) {
11
+ return <HoverCardPrimitive.Root data-slot="hover-card" {...props} />
12
+ }
13
+
14
+ function HoverCardTrigger({
15
+ ...props
16
+ }: React.ComponentProps<typeof HoverCardPrimitive.Trigger>) {
17
+ return (
18
+ <HoverCardPrimitive.Trigger data-slot="hover-card-trigger" {...props} />
19
+ )
20
+ }
21
+
22
+ function HoverCardContent({
23
+ className,
24
+ align = "center",
25
+ sideOffset = 4,
26
+ ...props
27
+ }: React.ComponentProps<typeof HoverCardPrimitive.Content>) {
28
+ return (
29
+ <HoverCardPrimitive.Portal data-slot="hover-card-portal">
30
+ <HoverCardPrimitive.Content
31
+ data-slot="hover-card-content"
32
+ align={align}
33
+ sideOffset={sideOffset}
34
+ className={cn(
35
+ "bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 w-64 origin-(--radix-hover-card-content-transform-origin) rounded-md border p-4 shadow-md outline-hidden",
36
+ className
37
+ )}
38
+ {...props}
39
+ />
40
+ </HoverCardPrimitive.Portal>
41
+ )
42
+ }
43
+
44
+ export { HoverCard, HoverCardTrigger, HoverCardContent }