create-edhor-stack 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/LICENSE +21 -0
- package/README.md +75 -0
- package/STACK.md +1086 -0
- package/dist/index.js +3181 -0
- package/package.json +44 -0
- package/templates/apps/api-elysia/package.json +21 -0
- package/templates/apps/api-elysia/src/index.ts +59 -0
- package/templates/apps/api-elysia/src/lib/eden.ts +25 -0
- package/templates/apps/api-elysia/src/lib/env.ts +18 -0
- package/templates/apps/api-elysia/src/routes/health.ts +13 -0
- package/templates/apps/api-elysia/src/routes/users.ts +117 -0
- package/templates/apps/api-elysia/tsconfig.json +15 -0
- package/templates/apps/api-hono/package.json +20 -0
- package/templates/apps/api-hono/src/index.ts +66 -0
- package/templates/apps/api-hono/src/lib/env.ts +18 -0
- package/templates/apps/api-hono/src/routes/health.ts +20 -0
- package/templates/apps/api-hono/src/routes/users.ts +110 -0
- package/templates/apps/api-hono/tsconfig.json +15 -0
- package/templates/apps/mobile/.env.example +9 -0
- package/templates/apps/mobile/app/_layout.tsx +16 -0
- package/templates/apps/mobile/app/index.tsx +39 -0
- package/templates/apps/mobile/app.json +37 -0
- package/templates/apps/mobile/assets/adaptive-icon.png +0 -0
- package/templates/apps/mobile/assets/favicon.png +0 -0
- package/templates/apps/mobile/assets/icon.png +0 -0
- package/templates/apps/mobile/assets/splash-icon.png +0 -0
- package/templates/apps/mobile/package.json +39 -0
- package/templates/apps/mobile/src/api/client.ts +51 -0
- package/templates/apps/mobile/src/api/index.ts +3 -0
- package/templates/apps/mobile/src/api/queries.ts +24 -0
- package/templates/apps/mobile/src/api/schemas.ts +32 -0
- package/templates/apps/mobile/src/lib/env.ts +40 -0
- package/templates/apps/mobile/src/lib/query-client.ts +28 -0
- package/templates/apps/mobile/src/lib/result.ts +45 -0
- package/templates/apps/mobile/src/lib/store.ts +63 -0
- package/templates/apps/mobile/tsconfig.json +10 -0
- package/templates/apps/web/.env.example +11 -0
- package/templates/apps/web/package.json +29 -0
- package/templates/apps/web/src/lib/env.ts +52 -0
- package/templates/apps/web/src/lib/queries.ts +27 -0
- package/templates/apps/web/src/lib/query-client.ts +11 -0
- package/templates/apps/web/src/router.tsx +17 -0
- package/templates/apps/web/src/routes/__root.tsx +32 -0
- package/templates/apps/web/src/routes/index.tsx +16 -0
- package/templates/apps/web/src/styles.css +26 -0
- package/templates/apps/web/tsconfig.json +10 -0
- package/templates/apps/web/vite.config.ts +21 -0
- package/templates/base/.claude/settings.json +33 -0
- package/templates/base/.claude/skills/add-api-endpoint.md +137 -0
- package/templates/base/.claude/skills/add-component.md +79 -0
- package/templates/base/.claude/skills/add-route.md +134 -0
- package/templates/base/.claude/skills/add-store.md +158 -0
- package/templates/base/.husky/pre-commit +1 -0
- package/templates/base/.lintstagedrc +4 -0
- package/templates/base/.node-version +1 -0
- package/templates/base/AGENTS.md +135 -0
- package/templates/base/CLAUDE.md.hbs +139 -0
- package/templates/base/Dockerfile +32 -0
- package/templates/base/biome.json +52 -0
- package/templates/base/fly.toml.hbs +20 -0
- package/templates/base/gitignore +36 -0
- package/templates/base/package.json.hbs +22 -0
- package/templates/base/tsconfig.json +14 -0
- package/templates/base/turbo.json +22 -0
- package/templates/packages/shared/package.json +17 -0
- package/templates/packages/shared/src/index.ts +4 -0
- package/templates/packages/shared/src/schemas.ts +50 -0
- package/templates/packages/shared/src/types.ts +47 -0
- package/templates/packages/shared/src/utils.ts +87 -0
- package/templates/packages/shared/tsconfig.json +14 -0
- package/templates/packages/stripe/package.json +18 -0
- package/templates/packages/stripe/src/client.ts +110 -0
- package/templates/packages/stripe/src/index.ts +3 -0
- package/templates/packages/stripe/src/schemas.ts +65 -0
- package/templates/packages/stripe/src/webhooks.ts +91 -0
- package/templates/packages/stripe/tsconfig.json +14 -0
- package/templates/packages/ui/components.json +19 -0
- package/templates/packages/ui/package.json +29 -0
- package/templates/packages/ui/src/components/button.tsx +58 -0
- package/templates/packages/ui/src/index.ts +5 -0
- package/templates/packages/ui/src/lib/utils.ts +6 -0
- package/templates/packages/ui/src/styles.css +120 -0
- package/templates/packages/ui/tsconfig.json +10 -0
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import Stripe from 'stripe';
|
|
2
|
+
|
|
3
|
+
if (!process.env.STRIPE_SECRET_KEY) {
|
|
4
|
+
throw new Error('STRIPE_SECRET_KEY is not set');
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export const stripe = new Stripe(process.env.STRIPE_SECRET_KEY, {
|
|
8
|
+
apiVersion: '2024-12-18.acacia',
|
|
9
|
+
typescript: true,
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
// ============================================================================
|
|
13
|
+
// CUSTOMERS
|
|
14
|
+
// ============================================================================
|
|
15
|
+
|
|
16
|
+
export async function createCustomer(params: {
|
|
17
|
+
email: string;
|
|
18
|
+
name?: string;
|
|
19
|
+
metadata?: Record<string, string>;
|
|
20
|
+
}) {
|
|
21
|
+
return stripe.customers.create({
|
|
22
|
+
email: params.email,
|
|
23
|
+
name: params.name,
|
|
24
|
+
metadata: params.metadata,
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export async function getCustomer(customerId: string) {
|
|
29
|
+
return stripe.customers.retrieve(customerId);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export async function getCustomerByEmail(email: string) {
|
|
33
|
+
const customers = await stripe.customers.list({ email, limit: 1 });
|
|
34
|
+
return customers.data[0] ?? null;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// ============================================================================
|
|
38
|
+
// CHECKOUT SESSIONS
|
|
39
|
+
// ============================================================================
|
|
40
|
+
|
|
41
|
+
export async function createCheckoutSession(params: {
|
|
42
|
+
customerId?: string;
|
|
43
|
+
customerEmail?: string;
|
|
44
|
+
priceId: string;
|
|
45
|
+
successUrl: string;
|
|
46
|
+
cancelUrl: string;
|
|
47
|
+
metadata?: Record<string, string>;
|
|
48
|
+
}) {
|
|
49
|
+
return stripe.checkout.sessions.create({
|
|
50
|
+
customer: params.customerId,
|
|
51
|
+
customer_email: params.customerId ? undefined : params.customerEmail,
|
|
52
|
+
mode: 'subscription',
|
|
53
|
+
line_items: [{ price: params.priceId, quantity: 1 }],
|
|
54
|
+
success_url: params.successUrl,
|
|
55
|
+
cancel_url: params.cancelUrl,
|
|
56
|
+
metadata: params.metadata,
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export async function createOneTimeCheckoutSession(params: {
|
|
61
|
+
customerId?: string;
|
|
62
|
+
customerEmail?: string;
|
|
63
|
+
priceId: string;
|
|
64
|
+
successUrl: string;
|
|
65
|
+
cancelUrl: string;
|
|
66
|
+
metadata?: Record<string, string>;
|
|
67
|
+
}) {
|
|
68
|
+
return stripe.checkout.sessions.create({
|
|
69
|
+
customer: params.customerId,
|
|
70
|
+
customer_email: params.customerId ? undefined : params.customerEmail,
|
|
71
|
+
mode: 'payment',
|
|
72
|
+
line_items: [{ price: params.priceId, quantity: 1 }],
|
|
73
|
+
success_url: params.successUrl,
|
|
74
|
+
cancel_url: params.cancelUrl,
|
|
75
|
+
metadata: params.metadata,
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// ============================================================================
|
|
80
|
+
// SUBSCRIPTIONS
|
|
81
|
+
// ============================================================================
|
|
82
|
+
|
|
83
|
+
export async function getSubscription(subscriptionId: string) {
|
|
84
|
+
return stripe.subscriptions.retrieve(subscriptionId);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export async function cancelSubscription(subscriptionId: string) {
|
|
88
|
+
return stripe.subscriptions.cancel(subscriptionId);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export async function getCustomerSubscriptions(customerId: string) {
|
|
92
|
+
return stripe.subscriptions.list({
|
|
93
|
+
customer: customerId,
|
|
94
|
+
status: 'all',
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// ============================================================================
|
|
99
|
+
// BILLING PORTAL
|
|
100
|
+
// ============================================================================
|
|
101
|
+
|
|
102
|
+
export async function createBillingPortalSession(params: {
|
|
103
|
+
customerId: string;
|
|
104
|
+
returnUrl: string;
|
|
105
|
+
}) {
|
|
106
|
+
return stripe.billingPortal.sessions.create({
|
|
107
|
+
customer: params.customerId,
|
|
108
|
+
return_url: params.returnUrl,
|
|
109
|
+
});
|
|
110
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
|
|
3
|
+
// ============================================================================
|
|
4
|
+
// CHECKOUT SCHEMAS
|
|
5
|
+
// ============================================================================
|
|
6
|
+
|
|
7
|
+
export const CreateCheckoutSchema = z.object({
|
|
8
|
+
priceId: z.string().startsWith('price_'),
|
|
9
|
+
successUrl: z.string().url(),
|
|
10
|
+
cancelUrl: z.string().url(),
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
export const CheckoutSessionSchema = z.object({
|
|
14
|
+
id: z.string().startsWith('cs_'),
|
|
15
|
+
url: z.string().url().nullable(),
|
|
16
|
+
status: z.enum(['open', 'complete', 'expired']),
|
|
17
|
+
customer: z.string().nullable(),
|
|
18
|
+
subscription: z.string().nullable(),
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
// ============================================================================
|
|
22
|
+
// SUBSCRIPTION SCHEMAS
|
|
23
|
+
// ============================================================================
|
|
24
|
+
|
|
25
|
+
export const SubscriptionStatusSchema = z.enum([
|
|
26
|
+
'active',
|
|
27
|
+
'canceled',
|
|
28
|
+
'incomplete',
|
|
29
|
+
'incomplete_expired',
|
|
30
|
+
'past_due',
|
|
31
|
+
'paused',
|
|
32
|
+
'trialing',
|
|
33
|
+
'unpaid',
|
|
34
|
+
]);
|
|
35
|
+
|
|
36
|
+
export const SubscriptionSchema = z.object({
|
|
37
|
+
id: z.string().startsWith('sub_'),
|
|
38
|
+
status: SubscriptionStatusSchema,
|
|
39
|
+
currentPeriodStart: z.number(),
|
|
40
|
+
currentPeriodEnd: z.number(),
|
|
41
|
+
cancelAtPeriodEnd: z.boolean(),
|
|
42
|
+
priceId: z.string(),
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
// ============================================================================
|
|
46
|
+
// WEBHOOK SCHEMAS
|
|
47
|
+
// ============================================================================
|
|
48
|
+
|
|
49
|
+
export const WebhookEventSchema = z.object({
|
|
50
|
+
id: z.string().startsWith('evt_'),
|
|
51
|
+
type: z.string(),
|
|
52
|
+
data: z.object({
|
|
53
|
+
object: z.record(z.unknown()),
|
|
54
|
+
}),
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
// ============================================================================
|
|
58
|
+
// TYPES
|
|
59
|
+
// ============================================================================
|
|
60
|
+
|
|
61
|
+
export type CreateCheckout = z.infer<typeof CreateCheckoutSchema>;
|
|
62
|
+
export type CheckoutSession = z.infer<typeof CheckoutSessionSchema>;
|
|
63
|
+
export type SubscriptionStatus = z.infer<typeof SubscriptionStatusSchema>;
|
|
64
|
+
export type Subscription = z.infer<typeof SubscriptionSchema>;
|
|
65
|
+
export type WebhookEvent = z.infer<typeof WebhookEventSchema>;
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import type Stripe from 'stripe';
|
|
2
|
+
import { stripe } from './client';
|
|
3
|
+
|
|
4
|
+
if (!process.env.STRIPE_WEBHOOK_SECRET) {
|
|
5
|
+
console.warn('STRIPE_WEBHOOK_SECRET is not set - webhooks will not be verified');
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
// ============================================================================
|
|
9
|
+
// WEBHOOK VERIFICATION
|
|
10
|
+
// ============================================================================
|
|
11
|
+
|
|
12
|
+
export function constructWebhookEvent(
|
|
13
|
+
payload: string | Buffer,
|
|
14
|
+
signature: string
|
|
15
|
+
): Stripe.Event {
|
|
16
|
+
if (!process.env.STRIPE_WEBHOOK_SECRET) {
|
|
17
|
+
throw new Error('STRIPE_WEBHOOK_SECRET is not set');
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
return stripe.webhooks.constructEvent(
|
|
21
|
+
payload,
|
|
22
|
+
signature,
|
|
23
|
+
process.env.STRIPE_WEBHOOK_SECRET
|
|
24
|
+
);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// ============================================================================
|
|
28
|
+
// WEBHOOK HANDLER TYPES
|
|
29
|
+
// ============================================================================
|
|
30
|
+
|
|
31
|
+
export type WebhookHandler<T = unknown> = (data: T) => Promise<void>;
|
|
32
|
+
|
|
33
|
+
export interface WebhookHandlers {
|
|
34
|
+
'checkout.session.completed'?: WebhookHandler<Stripe.Checkout.Session>;
|
|
35
|
+
'customer.subscription.created'?: WebhookHandler<Stripe.Subscription>;
|
|
36
|
+
'customer.subscription.updated'?: WebhookHandler<Stripe.Subscription>;
|
|
37
|
+
'customer.subscription.deleted'?: WebhookHandler<Stripe.Subscription>;
|
|
38
|
+
'invoice.payment_succeeded'?: WebhookHandler<Stripe.Invoice>;
|
|
39
|
+
'invoice.payment_failed'?: WebhookHandler<Stripe.Invoice>;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// ============================================================================
|
|
43
|
+
// WEBHOOK PROCESSOR
|
|
44
|
+
// ============================================================================
|
|
45
|
+
|
|
46
|
+
export async function processWebhook(
|
|
47
|
+
event: Stripe.Event,
|
|
48
|
+
handlers: WebhookHandlers
|
|
49
|
+
): Promise<void> {
|
|
50
|
+
const handler = handlers[event.type as keyof WebhookHandlers];
|
|
51
|
+
|
|
52
|
+
if (handler) {
|
|
53
|
+
await handler(event.data.object);
|
|
54
|
+
} else {
|
|
55
|
+
console.log(`Unhandled webhook event: ${event.type}`);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// ============================================================================
|
|
60
|
+
// EXAMPLE USAGE
|
|
61
|
+
// ============================================================================
|
|
62
|
+
/*
|
|
63
|
+
// In your API route (e.g., /api/webhooks/stripe):
|
|
64
|
+
|
|
65
|
+
import { constructWebhookEvent, processWebhook } from '@your-app/stripe/webhooks';
|
|
66
|
+
|
|
67
|
+
export async function POST(request: Request) {
|
|
68
|
+
const payload = await request.text();
|
|
69
|
+
const signature = request.headers.get('stripe-signature')!;
|
|
70
|
+
|
|
71
|
+
try {
|
|
72
|
+
const event = constructWebhookEvent(payload, signature);
|
|
73
|
+
|
|
74
|
+
await processWebhook(event, {
|
|
75
|
+
'checkout.session.completed': async (session) => {
|
|
76
|
+
// Handle successful checkout
|
|
77
|
+
console.log('Checkout completed:', session.id);
|
|
78
|
+
},
|
|
79
|
+
'customer.subscription.updated': async (subscription) => {
|
|
80
|
+
// Handle subscription update
|
|
81
|
+
console.log('Subscription updated:', subscription.id);
|
|
82
|
+
},
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
return new Response('OK', { status: 200 });
|
|
86
|
+
} catch (error) {
|
|
87
|
+
console.error('Webhook error:', error);
|
|
88
|
+
return new Response('Webhook Error', { status: 400 });
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
*/
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2022",
|
|
4
|
+
"module": "ESNext",
|
|
5
|
+
"moduleResolution": "bundler",
|
|
6
|
+
"strict": true,
|
|
7
|
+
"esModuleInterop": true,
|
|
8
|
+
"skipLibCheck": true,
|
|
9
|
+
"declaration": true,
|
|
10
|
+
"declarationMap": true,
|
|
11
|
+
"composite": true
|
|
12
|
+
},
|
|
13
|
+
"include": ["src/**/*"]
|
|
14
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://ui.shadcn.com/schema.json",
|
|
3
|
+
"style": "new-york",
|
|
4
|
+
"rsc": false,
|
|
5
|
+
"tsx": true,
|
|
6
|
+
"tailwind": {
|
|
7
|
+
"config": "",
|
|
8
|
+
"css": "src/styles.css",
|
|
9
|
+
"baseColor": "zinc",
|
|
10
|
+
"cssVariables": true
|
|
11
|
+
},
|
|
12
|
+
"aliases": {
|
|
13
|
+
"components": "src/components",
|
|
14
|
+
"utils": "src/lib/utils",
|
|
15
|
+
"ui": "src/components",
|
|
16
|
+
"lib": "src/lib"
|
|
17
|
+
},
|
|
18
|
+
"iconLibrary": "lucide"
|
|
19
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@{{name}}/ui",
|
|
3
|
+
"version": "0.0.0",
|
|
4
|
+
"private": true,
|
|
5
|
+
"type": "module",
|
|
6
|
+
"exports": {
|
|
7
|
+
".": "./src/index.ts",
|
|
8
|
+
"./components/*": "./src/components/*.tsx",
|
|
9
|
+
"./lib/*": "./src/lib/*.ts",
|
|
10
|
+
"./styles.css": "./src/styles.css"
|
|
11
|
+
},
|
|
12
|
+
"scripts": {
|
|
13
|
+
"ui:add": "bunx shadcn@latest add"
|
|
14
|
+
},
|
|
15
|
+
"dependencies": {
|
|
16
|
+
"@radix-ui/react-slot": "^1.1.0",
|
|
17
|
+
"class-variance-authority": "^0.7.1",
|
|
18
|
+
"clsx": "^2.1.1",
|
|
19
|
+
"tailwind-merge": "^3.0.0",
|
|
20
|
+
"lucide-react": "^0.460.0"
|
|
21
|
+
},
|
|
22
|
+
"devDependencies": {
|
|
23
|
+
"tailwindcss": "^4.0.0",
|
|
24
|
+
"@types/react": "^19.0.0"
|
|
25
|
+
},
|
|
26
|
+
"peerDependencies": {
|
|
27
|
+
"react": "^19.0.0"
|
|
28
|
+
}
|
|
29
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
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 buttonVariants = cva(
|
|
8
|
+
"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-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",
|
|
9
|
+
{
|
|
10
|
+
variants: {
|
|
11
|
+
variant: {
|
|
12
|
+
default:
|
|
13
|
+
'bg-primary text-primary-foreground shadow-xs hover:bg-primary/90',
|
|
14
|
+
destructive:
|
|
15
|
+
'bg-destructive text-white shadow-xs hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60',
|
|
16
|
+
outline:
|
|
17
|
+
'border border-input bg-background shadow-xs hover:bg-accent hover:text-accent-foreground',
|
|
18
|
+
secondary:
|
|
19
|
+
'bg-secondary text-secondary-foreground shadow-xs hover:bg-secondary/80',
|
|
20
|
+
ghost: 'hover:bg-accent hover:text-accent-foreground',
|
|
21
|
+
link: 'text-primary underline-offset-4 hover:underline',
|
|
22
|
+
},
|
|
23
|
+
size: {
|
|
24
|
+
default: 'h-9 px-4 py-2 has-[>svg]:px-3',
|
|
25
|
+
sm: 'h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5',
|
|
26
|
+
lg: 'h-10 rounded-md px-6 has-[>svg]:px-4',
|
|
27
|
+
icon: 'size-9',
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
defaultVariants: {
|
|
31
|
+
variant: 'default',
|
|
32
|
+
size: 'default',
|
|
33
|
+
},
|
|
34
|
+
}
|
|
35
|
+
);
|
|
36
|
+
|
|
37
|
+
function Button({
|
|
38
|
+
className,
|
|
39
|
+
variant,
|
|
40
|
+
size,
|
|
41
|
+
asChild = false,
|
|
42
|
+
...props
|
|
43
|
+
}: React.ComponentProps<'button'> &
|
|
44
|
+
VariantProps<typeof buttonVariants> & {
|
|
45
|
+
asChild?: boolean;
|
|
46
|
+
}) {
|
|
47
|
+
const Comp = asChild ? Slot : 'button';
|
|
48
|
+
|
|
49
|
+
return (
|
|
50
|
+
<Comp
|
|
51
|
+
data-slot="button"
|
|
52
|
+
className={cn(buttonVariants({ variant, size, className }))}
|
|
53
|
+
{...props}
|
|
54
|
+
/>
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export { Button, buttonVariants };
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
@import "tailwindcss";
|
|
2
|
+
|
|
3
|
+
@theme inline {
|
|
4
|
+
--color-background: var(--background);
|
|
5
|
+
--color-foreground: var(--foreground);
|
|
6
|
+
--color-card: var(--card);
|
|
7
|
+
--color-card-foreground: var(--card-foreground);
|
|
8
|
+
--color-popover: var(--popover);
|
|
9
|
+
--color-popover-foreground: var(--popover-foreground);
|
|
10
|
+
--color-primary: var(--primary);
|
|
11
|
+
--color-primary-foreground: var(--primary-foreground);
|
|
12
|
+
--color-secondary: var(--secondary);
|
|
13
|
+
--color-secondary-foreground: var(--secondary-foreground);
|
|
14
|
+
--color-muted: var(--muted);
|
|
15
|
+
--color-muted-foreground: var(--muted-foreground);
|
|
16
|
+
--color-accent: var(--accent);
|
|
17
|
+
--color-accent-foreground: var(--accent-foreground);
|
|
18
|
+
--color-destructive: var(--destructive);
|
|
19
|
+
--color-destructive-foreground: var(--destructive-foreground);
|
|
20
|
+
--color-border: var(--border);
|
|
21
|
+
--color-input: var(--input);
|
|
22
|
+
--color-ring: var(--ring);
|
|
23
|
+
--color-chart-1: var(--chart-1);
|
|
24
|
+
--color-chart-2: var(--chart-2);
|
|
25
|
+
--color-chart-3: var(--chart-3);
|
|
26
|
+
--color-chart-4: var(--chart-4);
|
|
27
|
+
--color-chart-5: var(--chart-5);
|
|
28
|
+
--color-sidebar: var(--sidebar);
|
|
29
|
+
--color-sidebar-foreground: var(--sidebar-foreground);
|
|
30
|
+
--color-sidebar-primary: var(--sidebar-primary);
|
|
31
|
+
--color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
|
|
32
|
+
--color-sidebar-accent: var(--sidebar-accent);
|
|
33
|
+
--color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
|
|
34
|
+
--color-sidebar-border: var(--sidebar-border);
|
|
35
|
+
--color-sidebar-ring: var(--sidebar-ring);
|
|
36
|
+
--radius-sm: calc(var(--radius) - 4px);
|
|
37
|
+
--radius-md: calc(var(--radius) - 2px);
|
|
38
|
+
--radius-lg: var(--radius);
|
|
39
|
+
--radius-xl: calc(var(--radius) + 4px);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
:root {
|
|
43
|
+
--radius: 0.625rem;
|
|
44
|
+
--background: oklch(1 0 0);
|
|
45
|
+
--foreground: oklch(0.145 0 0);
|
|
46
|
+
--card: oklch(1 0 0);
|
|
47
|
+
--card-foreground: oklch(0.145 0 0);
|
|
48
|
+
--popover: oklch(1 0 0);
|
|
49
|
+
--popover-foreground: oklch(0.145 0 0);
|
|
50
|
+
--primary: oklch(0.205 0 0);
|
|
51
|
+
--primary-foreground: oklch(0.985 0 0);
|
|
52
|
+
--secondary: oklch(0.97 0 0);
|
|
53
|
+
--secondary-foreground: oklch(0.205 0 0);
|
|
54
|
+
--muted: oklch(0.97 0 0);
|
|
55
|
+
--muted-foreground: oklch(0.556 0 0);
|
|
56
|
+
--accent: oklch(0.97 0 0);
|
|
57
|
+
--accent-foreground: oklch(0.205 0 0);
|
|
58
|
+
--destructive: oklch(0.577 0.245 27.325);
|
|
59
|
+
--destructive-foreground: oklch(0.577 0.245 27.325);
|
|
60
|
+
--border: oklch(0.922 0 0);
|
|
61
|
+
--input: oklch(0.922 0 0);
|
|
62
|
+
--ring: oklch(0.708 0 0);
|
|
63
|
+
--chart-1: oklch(0.646 0.222 41.116);
|
|
64
|
+
--chart-2: oklch(0.6 0.118 184.704);
|
|
65
|
+
--chart-3: oklch(0.398 0.07 227.392);
|
|
66
|
+
--chart-4: oklch(0.828 0.189 84.429);
|
|
67
|
+
--chart-5: oklch(0.769 0.188 70.08);
|
|
68
|
+
--sidebar: oklch(0.985 0 0);
|
|
69
|
+
--sidebar-foreground: oklch(0.145 0 0);
|
|
70
|
+
--sidebar-primary: oklch(0.205 0 0);
|
|
71
|
+
--sidebar-primary-foreground: oklch(0.985 0 0);
|
|
72
|
+
--sidebar-accent: oklch(0.97 0 0);
|
|
73
|
+
--sidebar-accent-foreground: oklch(0.205 0 0);
|
|
74
|
+
--sidebar-border: oklch(0.922 0 0);
|
|
75
|
+
--sidebar-ring: oklch(0.708 0 0);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
.dark {
|
|
79
|
+
--background: oklch(0.145 0 0);
|
|
80
|
+
--foreground: oklch(0.985 0 0);
|
|
81
|
+
--card: oklch(0.145 0 0);
|
|
82
|
+
--card-foreground: oklch(0.985 0 0);
|
|
83
|
+
--popover: oklch(0.145 0 0);
|
|
84
|
+
--popover-foreground: oklch(0.985 0 0);
|
|
85
|
+
--primary: oklch(0.985 0 0);
|
|
86
|
+
--primary-foreground: oklch(0.205 0 0);
|
|
87
|
+
--secondary: oklch(0.269 0 0);
|
|
88
|
+
--secondary-foreground: oklch(0.985 0 0);
|
|
89
|
+
--muted: oklch(0.269 0 0);
|
|
90
|
+
--muted-foreground: oklch(0.708 0 0);
|
|
91
|
+
--accent: oklch(0.269 0 0);
|
|
92
|
+
--accent-foreground: oklch(0.985 0 0);
|
|
93
|
+
--destructive: oklch(0.396 0.141 25.723);
|
|
94
|
+
--destructive-foreground: oklch(0.637 0.237 25.331);
|
|
95
|
+
--border: oklch(0.269 0 0);
|
|
96
|
+
--input: oklch(0.269 0 0);
|
|
97
|
+
--ring: oklch(0.439 0 0);
|
|
98
|
+
--chart-1: oklch(0.488 0.243 264.376);
|
|
99
|
+
--chart-2: oklch(0.696 0.17 162.48);
|
|
100
|
+
--chart-3: oklch(0.769 0.188 70.08);
|
|
101
|
+
--chart-4: oklch(0.627 0.265 303.9);
|
|
102
|
+
--chart-5: oklch(0.645 0.246 16.439);
|
|
103
|
+
--sidebar: oklch(0.205 0 0);
|
|
104
|
+
--sidebar-foreground: oklch(0.985 0 0);
|
|
105
|
+
--sidebar-primary: oklch(0.488 0.243 264.376);
|
|
106
|
+
--sidebar-primary-foreground: oklch(0.985 0 0);
|
|
107
|
+
--sidebar-accent: oklch(0.269 0 0);
|
|
108
|
+
--sidebar-accent-foreground: oklch(0.985 0 0);
|
|
109
|
+
--sidebar-border: oklch(0.269 0 0);
|
|
110
|
+
--sidebar-ring: oklch(0.439 0 0);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
@layer base {
|
|
114
|
+
* {
|
|
115
|
+
@apply border-border;
|
|
116
|
+
}
|
|
117
|
+
body {
|
|
118
|
+
@apply bg-background text-foreground;
|
|
119
|
+
}
|
|
120
|
+
}
|