agent-stage 0.2.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 (50) hide show
  1. package/dist/commands/add-page.d.ts +2 -0
  2. package/dist/commands/add-page.js +132 -0
  3. package/dist/commands/components.d.ts +2 -0
  4. package/dist/commands/components.js +136 -0
  5. package/dist/commands/doctor.d.ts +2 -0
  6. package/dist/commands/doctor.js +247 -0
  7. package/dist/commands/exec.d.ts +2 -0
  8. package/dist/commands/exec.js +59 -0
  9. package/dist/commands/init.d.ts +2 -0
  10. package/dist/commands/init.js +208 -0
  11. package/dist/commands/inspect.d.ts +2 -0
  12. package/dist/commands/inspect.js +62 -0
  13. package/dist/commands/ls.d.ts +2 -0
  14. package/dist/commands/ls.js +84 -0
  15. package/dist/commands/reset.d.ts +2 -0
  16. package/dist/commands/reset.js +92 -0
  17. package/dist/commands/restart.d.ts +2 -0
  18. package/dist/commands/restart.js +90 -0
  19. package/dist/commands/rm-page.d.ts +2 -0
  20. package/dist/commands/rm-page.js +32 -0
  21. package/dist/commands/start.d.ts +2 -0
  22. package/dist/commands/start.js +82 -0
  23. package/dist/commands/status.d.ts +2 -0
  24. package/dist/commands/status.js +52 -0
  25. package/dist/commands/stop.d.ts +2 -0
  26. package/dist/commands/stop.js +58 -0
  27. package/dist/commands/watch.d.ts +2 -0
  28. package/dist/commands/watch.js +54 -0
  29. package/dist/index.d.ts +2 -0
  30. package/dist/index.js +48 -0
  31. package/dist/utils/paths.d.ts +45 -0
  32. package/dist/utils/paths.js +138 -0
  33. package/package.json +32 -0
  34. package/template/components.json +17 -0
  35. package/template/index.html +13 -0
  36. package/template/package.json +40 -0
  37. package/template/postcss.config.js +6 -0
  38. package/template/src/components/ui/button.tsx +55 -0
  39. package/template/src/components/ui/card.tsx +78 -0
  40. package/template/src/components/ui/input.tsx +24 -0
  41. package/template/src/index.css +59 -0
  42. package/template/src/lib/utils.ts +6 -0
  43. package/template/src/main.tsx +23 -0
  44. package/template/src/routes/__root.tsx +11 -0
  45. package/template/src/routes/index.tsx +46 -0
  46. package/template/src/vite-env.d.ts +1 -0
  47. package/template/tailwind.config.js +53 -0
  48. package/template/tsconfig.json +25 -0
  49. package/template/tsconfig.node.json +11 -0
  50. package/template/vite.config.ts +22 -0
@@ -0,0 +1,52 @@
1
+ import { Command } from 'commander';
2
+ import consola from 'consola';
3
+ import c from 'picocolors';
4
+ import { getWorkspaceDir, readRuntimeConfig, isInitialized } from '../utils/paths.js';
5
+ function isRunning(pid) {
6
+ try {
7
+ process.kill(pid, 0);
8
+ return true;
9
+ }
10
+ catch {
11
+ return false;
12
+ }
13
+ }
14
+ export const statusCommand = new Command('status')
15
+ .description('Show the Agentstage Runtime status')
16
+ .action(async () => {
17
+ // 1. 检查是否已初始化
18
+ if (!isInitialized()) {
19
+ consola.error('Project not initialized. Please run `agentstage init` first.');
20
+ process.exit(1);
21
+ }
22
+ const workspaceDir = await getWorkspaceDir();
23
+ console.log();
24
+ console.log(c.bold('Agentstage Runtime'));
25
+ console.log(c.gray('─'.repeat(40)));
26
+ console.log(`Workspace: ${c.cyan(workspaceDir)}`);
27
+ // 2. 读取运行时配置
28
+ const config = await readRuntimeConfig();
29
+ if (!config) {
30
+ console.log(`Status: ${c.red('●')} Stopped`);
31
+ console.log();
32
+ console.log(c.gray('Run `agentstage start` to start the runtime.'));
33
+ console.log();
34
+ return;
35
+ }
36
+ // 3. 检查进程是否存活
37
+ const running = isRunning(config.pid);
38
+ if (running) {
39
+ console.log(`Status: ${c.green('●')} Running`);
40
+ console.log(`PID: ${config.pid}`);
41
+ console.log(`Port: ${config.port}`);
42
+ console.log(`Started: ${new Date(config.startedAt).toLocaleString()}`);
43
+ console.log(`Web: ${c.cyan(`http://localhost:${config.port}`)}`);
44
+ console.log(`Bridge: ${c.cyan(`ws://localhost:${config.port}/_bridge`)}`);
45
+ }
46
+ else {
47
+ console.log(`Status: ${c.yellow('●')} Dead (PID file exists but process not found)`);
48
+ console.log();
49
+ console.log(c.gray('Run `agentstage start` to restart the runtime.'));
50
+ }
51
+ console.log();
52
+ });
@@ -0,0 +1,2 @@
1
+ import { Command } from 'commander';
2
+ export declare const stopCommand: Command;
@@ -0,0 +1,58 @@
1
+ import { Command } from 'commander';
2
+ import consola from 'consola';
3
+ import { unlink } from 'fs/promises';
4
+ import { existsSync } from 'fs';
5
+ import { getPidFile, readRuntimeConfig, removeRuntimeConfig, isInitialized } from '../utils/paths.js';
6
+ async function killProcess(pid) {
7
+ try {
8
+ process.kill(pid, 'SIGTERM');
9
+ let attempts = 0;
10
+ while (attempts < 10) {
11
+ await new Promise(r => setTimeout(r, 500));
12
+ try {
13
+ process.kill(pid, 0);
14
+ attempts++;
15
+ }
16
+ catch {
17
+ break;
18
+ }
19
+ }
20
+ if (attempts >= 10) {
21
+ process.kill(pid, 'SIGKILL');
22
+ }
23
+ }
24
+ catch (error) {
25
+ if (error.code !== 'ESRCH')
26
+ throw error;
27
+ }
28
+ }
29
+ export const stopCommand = new Command('stop')
30
+ .description('Stop the Agentstage Runtime')
31
+ .action(async () => {
32
+ // 1. 检查是否已初始化
33
+ if (!isInitialized()) {
34
+ consola.error('Project not initialized. Please run `agentstage init` first.');
35
+ process.exit(1);
36
+ }
37
+ // 2. 读取运行时配置
38
+ const config = await readRuntimeConfig();
39
+ if (!config) {
40
+ consola.info('Runtime is not running');
41
+ return;
42
+ }
43
+ // 3. 停止进程
44
+ try {
45
+ await killProcess(config.pid);
46
+ await removeRuntimeConfig();
47
+ // 兼容旧版本:删除 pid 文件
48
+ const pidFile = await getPidFile();
49
+ if (existsSync(pidFile)) {
50
+ await unlink(pidFile).catch(() => { });
51
+ }
52
+ consola.success('Runtime stopped');
53
+ }
54
+ catch (error) {
55
+ consola.error('Failed to stop runtime:', error.message);
56
+ process.exit(1);
57
+ }
58
+ });
@@ -0,0 +1,2 @@
1
+ import { Command } from 'commander';
2
+ export declare const watchCommand: Command;
@@ -0,0 +1,54 @@
1
+ import { Command } from 'commander';
2
+ import consola from 'consola';
3
+ import c from 'picocolors';
4
+ import { BridgeClient } from 'agent-stage-bridge/sdk';
5
+ import { readRuntimeConfig, isInitialized } from '../utils/paths.js';
6
+ export const watchCommand = new Command('watch')
7
+ .description('Watch a page for real-time changes')
8
+ .argument('<page>', 'Page ID')
9
+ .action(async (pageId) => {
10
+ try {
11
+ // 1. 检查是否已初始化
12
+ if (!isInitialized()) {
13
+ consola.error('Project not initialized. Please run `agentstage init` first.');
14
+ process.exit(1);
15
+ }
16
+ // 2. 检查是否已启动
17
+ const config = await readRuntimeConfig();
18
+ if (!config) {
19
+ consola.error('Runtime is not running. Please run `agentstage start` first.');
20
+ process.exit(1);
21
+ }
22
+ const client = new BridgeClient(`ws://localhost:${config.port}/_bridge`);
23
+ client.onEvent((event) => {
24
+ const timestamp = new Date().toISOString();
25
+ console.log(c.gray(`[${timestamp}]`), event);
26
+ });
27
+ await client.connect();
28
+ // 查找并订阅所有该 page 的 stores
29
+ const stores = await client.listStores();
30
+ const pageStores = stores.filter(s => s.pageId === pageId);
31
+ if (pageStores.length === 0) {
32
+ consola.error(`Page "${pageId}" not found`);
33
+ client.disconnect();
34
+ process.exit(1);
35
+ }
36
+ for (const store of pageStores) {
37
+ client.subscribe(store.id);
38
+ }
39
+ consola.success(`Watching ${c.cyan(pageId)}. Press Ctrl+C to exit.`);
40
+ console.log();
41
+ // 保持运行
42
+ process.on('SIGINT', () => {
43
+ console.log();
44
+ consola.info('Stopped watching');
45
+ client.disconnect();
46
+ process.exit(0);
47
+ });
48
+ await new Promise(() => { });
49
+ }
50
+ catch (error) {
51
+ consola.error('Failed to watch page:', error.message);
52
+ process.exit(1);
53
+ }
54
+ });
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
package/dist/index.js ADDED
@@ -0,0 +1,48 @@
1
+ #!/usr/bin/env node
2
+ import { Command } from 'commander';
3
+ import consola from 'consola';
4
+ import { initCommand } from './commands/init.js';
5
+ import { startCommand } from './commands/start.js';
6
+ import { stopCommand } from './commands/stop.js';
7
+ import { restartCommand } from './commands/restart.js';
8
+ import { statusCommand } from './commands/status.js';
9
+ import { lsCommand } from './commands/ls.js';
10
+ import { inspectCommand } from './commands/inspect.js';
11
+ import { watchCommand } from './commands/watch.js';
12
+ import { execCommand } from './commands/exec.js';
13
+ import { addPageCommand } from './commands/add-page.js';
14
+ import { rmPageCommand } from './commands/rm-page.js';
15
+ import { componentsCommand } from './commands/components.js';
16
+ import { doctorCommand } from './commands/doctor.js';
17
+ import { resetCommand } from './commands/reset.js';
18
+ const program = new Command();
19
+ program
20
+ .name('agentstage')
21
+ .description('Agent UI Stage CLI - Create interactive UI for AI agents')
22
+ .version('0.2.0');
23
+ // 注册命令
24
+ program.addCommand(initCommand);
25
+ program.addCommand(startCommand);
26
+ program.addCommand(stopCommand);
27
+ program.addCommand(restartCommand);
28
+ program.addCommand(statusCommand);
29
+ program.addCommand(lsCommand);
30
+ program.addCommand(inspectCommand);
31
+ program.addCommand(watchCommand);
32
+ program.addCommand(execCommand);
33
+ program.addCommand(addPageCommand);
34
+ program.addCommand(rmPageCommand);
35
+ program.addCommand(componentsCommand);
36
+ program.addCommand(doctorCommand);
37
+ program.addCommand(resetCommand);
38
+ // 错误处理
39
+ program.exitOverride();
40
+ try {
41
+ await program.parseAsync(process.argv);
42
+ }
43
+ catch (error) {
44
+ if (error.code !== 'commander.help' && error.code !== 'commander.version') {
45
+ consola.error(error.message || 'Unknown error');
46
+ process.exit(1);
47
+ }
48
+ }
@@ -0,0 +1,45 @@
1
+ export declare const CLI_CONFIG_DIR: string;
2
+ export declare const WORKSPACE_FILE: string;
3
+ export declare const DEFAULT_WORKSPACE: string;
4
+ /**
5
+ * 获取当前工作目录
6
+ */
7
+ export declare function getWorkspaceDir(): Promise<string>;
8
+ /**
9
+ * 设置工作目录
10
+ */
11
+ export declare function setWorkspaceDir(dir: string): Promise<void>;
12
+ /**
13
+ * 获取 PID 文件路径
14
+ */
15
+ export declare function getPidFile(): Promise<string>;
16
+ /**
17
+ * 获取 pages 目录
18
+ */
19
+ export declare function getPagesDir(): Promise<string>;
20
+ export declare function getRuntimeConfigFile(): Promise<string>;
21
+ export interface RuntimeConfig {
22
+ pid: number;
23
+ port: number;
24
+ startedAt: string;
25
+ }
26
+ /**
27
+ * 读取运行时配置
28
+ */
29
+ export declare function readRuntimeConfig(): Promise<RuntimeConfig | null>;
30
+ /**
31
+ * 保存运行时配置
32
+ */
33
+ export declare function saveRuntimeConfig(config: RuntimeConfig): Promise<void>;
34
+ /**
35
+ * 删除运行时配置
36
+ */
37
+ export declare function removeRuntimeConfig(): Promise<void>;
38
+ /**
39
+ * 检查是否已初始化
40
+ */
41
+ export declare function isInitialized(): boolean;
42
+ /**
43
+ * 检查服务是否正在运行
44
+ */
45
+ export declare function isRunning(): Promise<boolean>;
@@ -0,0 +1,138 @@
1
+ import { join, resolve } from 'pathe';
2
+ import { existsSync } from 'fs';
3
+ import { mkdir, readFile, writeFile } from 'fs/promises';
4
+ import { homedir } from 'os';
5
+ // CLI 配置目录
6
+ export const CLI_CONFIG_DIR = join(homedir(), '.config', 'agentstage');
7
+ export const WORKSPACE_FILE = join(CLI_CONFIG_DIR, 'workspace');
8
+ // 默认工作目录
9
+ export const DEFAULT_WORKSPACE = join(homedir(), '.agentstage');
10
+ /**
11
+ * 获取当前工作目录
12
+ */
13
+ export async function getWorkspaceDir() {
14
+ // 1. 从配置文件读取
15
+ try {
16
+ const workspace = await readFile(WORKSPACE_FILE, 'utf8');
17
+ if (workspace && existsSync(workspace.trim())) {
18
+ return workspace.trim();
19
+ }
20
+ }
21
+ catch {
22
+ // 配置文件不存在
23
+ }
24
+ // 2. 检查当前目录是否有 .agentstage
25
+ const currentDir = join(process.cwd(), '.agentstage');
26
+ if (existsSync(currentDir)) {
27
+ return currentDir;
28
+ }
29
+ // 3. 检查默认目录是否存在
30
+ if (existsSync(DEFAULT_WORKSPACE)) {
31
+ return DEFAULT_WORKSPACE;
32
+ }
33
+ // 4. 没有初始化
34
+ throw new Error('No workspace found. Run `agentstage init` first, or cd to a directory with .agentstage/');
35
+ }
36
+ /**
37
+ * 设置工作目录
38
+ */
39
+ export async function setWorkspaceDir(dir) {
40
+ await mkdir(CLI_CONFIG_DIR, { recursive: true });
41
+ await writeFile(WORKSPACE_FILE, resolve(dir));
42
+ }
43
+ /**
44
+ * 获取 PID 文件路径
45
+ */
46
+ export async function getPidFile() {
47
+ const workspace = await getWorkspaceDir();
48
+ return join(workspace, '.agentstage', 'pid');
49
+ }
50
+ /**
51
+ * 获取 pages 目录
52
+ */
53
+ export async function getPagesDir() {
54
+ const workspace = await getWorkspaceDir();
55
+ // Vite template structure
56
+ if (existsSync(join(workspace, 'src', 'pages'))) {
57
+ return join(workspace, 'src', 'pages');
58
+ }
59
+ // Legacy TanStack Start structure (for backwards compatibility)
60
+ if (existsSync(join(workspace, 'src', 'routes', 'pages'))) {
61
+ return join(workspace, 'src', 'routes', 'pages');
62
+ }
63
+ if (existsSync(join(workspace, 'app', 'routes', 'pages'))) {
64
+ return join(workspace, 'app', 'routes', 'pages');
65
+ }
66
+ throw new Error('Pages directory not found at src/pages');
67
+ }
68
+ // 运行时配置文件路径
69
+ export async function getRuntimeConfigFile() {
70
+ const workspace = await getWorkspaceDir();
71
+ return join(workspace, '.agentstage', 'runtime.json');
72
+ }
73
+ /**
74
+ * 读取运行时配置
75
+ */
76
+ export async function readRuntimeConfig() {
77
+ const configFile = await getRuntimeConfigFile();
78
+ if (!existsSync(configFile)) {
79
+ return null;
80
+ }
81
+ try {
82
+ const content = await readFile(configFile, 'utf8');
83
+ return JSON.parse(content);
84
+ }
85
+ catch {
86
+ return null;
87
+ }
88
+ }
89
+ /**
90
+ * 保存运行时配置
91
+ */
92
+ export async function saveRuntimeConfig(config) {
93
+ const configFile = await getRuntimeConfigFile();
94
+ await mkdir(join(configFile, '..'), { recursive: true });
95
+ await writeFile(configFile, JSON.stringify(config, null, 2));
96
+ }
97
+ /**
98
+ * 删除运行时配置
99
+ */
100
+ export async function removeRuntimeConfig() {
101
+ const configFile = await getRuntimeConfigFile();
102
+ if (existsSync(configFile)) {
103
+ await writeFile(configFile, ''); // 清空而不是删除,便于检查
104
+ }
105
+ }
106
+ /**
107
+ * 检查是否已初始化
108
+ */
109
+ export function isInitialized() {
110
+ // 检查配置文件是否存在
111
+ if (!existsSync(WORKSPACE_FILE)) {
112
+ return false;
113
+ }
114
+ try {
115
+ const workspace = readFileSync(WORKSPACE_FILE, 'utf8');
116
+ // 检查工作目录是否有 package.json(项目标志)
117
+ return existsSync(join(workspace.trim(), 'package.json'));
118
+ }
119
+ catch {
120
+ return false;
121
+ }
122
+ }
123
+ /**
124
+ * 检查服务是否正在运行
125
+ */
126
+ export async function isRunning() {
127
+ const config = await readRuntimeConfig();
128
+ if (!config)
129
+ return false;
130
+ try {
131
+ process.kill(config.pid, 0);
132
+ return true;
133
+ }
134
+ catch {
135
+ return false;
136
+ }
137
+ }
138
+ import { readFileSync } from 'fs';
package/package.json ADDED
@@ -0,0 +1,32 @@
1
+ {
2
+ "name": "agent-stage",
3
+ "version": "0.2.0",
4
+ "files": ["dist", "template"],
5
+ "type": "module",
6
+ "bin": {
7
+ "agent-stage": "./dist/index.js"
8
+ },
9
+ "scripts": {
10
+ "dev": "tsx src/index.ts",
11
+ "build": "rimraf dist && tsc -p tsconfig.build.json",
12
+ "typecheck": "pnpm -C ../bridge build && tsc -p tsconfig.json --noEmit"
13
+ },
14
+ "dependencies": {
15
+ "agent-stage-bridge": "^0.1.0",
16
+ "@clack/prompts": "^0.8.2",
17
+ "commander": "^12.1.0",
18
+ "consola": "^3.2.3",
19
+ "execa": "^9.3.1",
20
+ "pathe": "^1.1.2",
21
+ "picocolors": "^1.1.1"
22
+ },
23
+ "devDependencies": {
24
+ "@types/node": "^22.13.5",
25
+ "rimraf": "^6.0.1",
26
+ "tsx": "^4.21.0",
27
+ "typescript": "^5.8.2"
28
+ },
29
+ "publishConfig": {
30
+ "access": "public"
31
+ }
32
+ }
@@ -0,0 +1,17 @@
1
+ {
2
+ "$schema": "https://ui.shadcn.com/schema.json",
3
+ "style": "default",
4
+ "rsc": false,
5
+ "tsx": true,
6
+ "tailwind": {
7
+ "config": "tailwind.config.js",
8
+ "css": "src/index.css",
9
+ "baseColor": "neutral",
10
+ "cssVariables": true,
11
+ "prefix": ""
12
+ },
13
+ "aliases": {
14
+ "components": "@/components",
15
+ "utils": "@/lib/utils"
16
+ }
17
+ }
@@ -0,0 +1,13 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <link rel="icon" type="image/svg+xml" href="/vite.svg" />
6
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7
+ <title>Agentstage App</title>
8
+ </head>
9
+ <body>
10
+ <div id="root"></div>
11
+ <script type="module" src="/src/main.tsx"></script>
12
+ </body>
13
+ </html>
@@ -0,0 +1,40 @@
1
+ {
2
+ "name": "agentstage-app",
3
+ "private": true,
4
+ "version": "0.0.0",
5
+ "type": "module",
6
+ "scripts": {
7
+ "dev": "vite",
8
+ "build": "tsc && vite build",
9
+ "preview": "vite preview",
10
+ "type-check": "tsc --noEmit"
11
+ },
12
+ "dependencies": {
13
+ "react": "^18.2.0",
14
+ "react-dom": "^18.2.0",
15
+ "@tanstack/react-router": "^1.114.3",
16
+ "@tanstack/router-core": "^1.114.3",
17
+ "@tanstack/router-devtools": "^1.114.3",
18
+ "agent-stage-bridge": "workspace:*",
19
+ "class-variance-authority": "^0.7.0",
20
+ "clsx": "^2.1.0",
21
+ "tailwind-merge": "^2.2.0",
22
+ "tailwindcss-animate": "^1.0.7",
23
+ "lucide-react": "^0.300.0",
24
+ "zod": "^3.23.0",
25
+ "zustand": "^4.5.0"
26
+ },
27
+ "devDependencies": {
28
+ "@types/react": "^18.2.43",
29
+ "@types/react-dom": "^18.2.17",
30
+ "@radix-ui/react-slot": "^1.0.2",
31
+ "@tanstack/router-generator": "^1.114.3",
32
+ "@tanstack/router-plugin": "^1.114.3",
33
+ "@vitejs/plugin-react": "^4.2.1",
34
+ "autoprefixer": "^10.4.17",
35
+ "postcss": "^8.4.33",
36
+ "tailwindcss": "^3.4.1",
37
+ "typescript": "^5.2.2",
38
+ "vite": "^5.0.8"
39
+ }
40
+ }
@@ -0,0 +1,6 @@
1
+ export default {
2
+ plugins: {
3
+ tailwindcss: {},
4
+ autoprefixer: {},
5
+ },
6
+ }
@@ -0,0 +1,55 @@
1
+ import * as React from "react"
2
+ import { Slot } from "@radix-ui/react-slot"
3
+ import { cva, type VariantProps } from "class-variance-authority"
4
+ import { cn } from "@/lib/utils"
5
+
6
+ const buttonVariants = cva(
7
+ "inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50",
8
+ {
9
+ variants: {
10
+ variant: {
11
+ default: "bg-primary text-primary-foreground hover:bg-primary/90",
12
+ destructive:
13
+ "bg-destructive text-destructive-foreground hover:bg-destructive/90",
14
+ outline:
15
+ "border border-input bg-background hover:bg-accent hover:text-accent-foreground",
16
+ secondary:
17
+ "bg-secondary text-secondary-foreground hover:bg-secondary/80",
18
+ ghost: "hover:bg-accent hover:text-accent-foreground",
19
+ link: "text-primary underline-offset-4 hover:underline",
20
+ },
21
+ size: {
22
+ default: "h-10 px-4 py-2",
23
+ sm: "h-9 rounded-md px-3",
24
+ lg: "h-11 rounded-md px-8",
25
+ icon: "h-10 w-10",
26
+ },
27
+ },
28
+ defaultVariants: {
29
+ variant: "default",
30
+ size: "default",
31
+ },
32
+ }
33
+ )
34
+
35
+ export interface ButtonProps
36
+ extends React.ButtonHTMLAttributes<HTMLButtonElement>,
37
+ VariantProps<typeof buttonVariants> {
38
+ asChild?: boolean
39
+ }
40
+
41
+ const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
42
+ ({ className, variant, size, asChild = false, ...props }, ref) => {
43
+ const Comp = asChild ? Slot : "button"
44
+ return (
45
+ <Comp
46
+ className={cn(buttonVariants({ variant, size, className }))}
47
+ ref={ref}
48
+ {...props}
49
+ />
50
+ )
51
+ }
52
+ )
53
+ Button.displayName = "Button"
54
+
55
+ export { Button, buttonVariants }
@@ -0,0 +1,78 @@
1
+ import * as React from "react"
2
+ import { cn } from "@/lib/utils"
3
+
4
+ const Card = React.forwardRef<
5
+ HTMLDivElement,
6
+ React.HTMLAttributes<HTMLDivElement>
7
+ >(({ className, ...props }, ref) => (
8
+ <div
9
+ ref={ref}
10
+ className={cn(
11
+ "rounded-lg border bg-card text-card-foreground shadow-sm",
12
+ className
13
+ )}
14
+ {...props}
15
+ />
16
+ ))
17
+ Card.displayName = "Card"
18
+
19
+ const CardHeader = React.forwardRef<
20
+ HTMLDivElement,
21
+ React.HTMLAttributes<HTMLDivElement>
22
+ >(({ className, ...props }, ref) => (
23
+ <div
24
+ ref={ref}
25
+ className={cn("flex flex-col space-y-1.5 p-6", className)}
26
+ {...props}
27
+ />
28
+ ))
29
+ CardHeader.displayName = "CardHeader"
30
+
31
+ const CardTitle = React.forwardRef<
32
+ HTMLParagraphElement,
33
+ React.HTMLAttributes<HTMLHeadingElement>
34
+ >(({ className, ...props }, ref) => (
35
+ <h3
36
+ ref={ref}
37
+ className={cn(
38
+ "text-2xl font-semibold leading-none tracking-tight",
39
+ className
40
+ )}
41
+ {...props}
42
+ />
43
+ ))
44
+ CardTitle.displayName = "CardTitle"
45
+
46
+ const CardDescription = React.forwardRef<
47
+ HTMLParagraphElement,
48
+ React.HTMLAttributes<HTMLParagraphElement>
49
+ >(({ className, ...props }, ref) => (
50
+ <p
51
+ ref={ref}
52
+ className={cn("text-sm text-muted-foreground", className)}
53
+ {...props}
54
+ />
55
+ ))
56
+ CardDescription.displayName = "CardDescription"
57
+
58
+ const CardContent = React.forwardRef<
59
+ HTMLDivElement,
60
+ React.HTMLAttributes<HTMLDivElement>
61
+ >(({ className, ...props }, ref) => (
62
+ <div ref={ref} className={cn("p-6 pt-0", className)} {...props} />
63
+ ))
64
+ CardContent.displayName = "CardContent"
65
+
66
+ const CardFooter = React.forwardRef<
67
+ HTMLDivElement,
68
+ React.HTMLAttributes<HTMLDivElement>
69
+ >(({ className, ...props }, ref) => (
70
+ <div
71
+ ref={ref}
72
+ className={cn("flex items-center p-6 pt-0", className)}
73
+ {...props}
74
+ />
75
+ ))
76
+ CardFooter.displayName = "CardFooter"
77
+
78
+ export { Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent }
@@ -0,0 +1,24 @@
1
+ import * as React from "react"
2
+ import { cn } from "@/lib/utils"
3
+
4
+ export interface InputProps
5
+ extends React.InputHTMLAttributes<HTMLInputElement> {}
6
+
7
+ const Input = React.forwardRef<HTMLInputElement, InputProps>(
8
+ ({ className, type, ...props }, ref) => {
9
+ return (
10
+ <input
11
+ type={type}
12
+ className={cn(
13
+ "flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",
14
+ className
15
+ )}
16
+ ref={ref}
17
+ {...props}
18
+ />
19
+ )
20
+ }
21
+ )
22
+ Input.displayName = "Input"
23
+
24
+ export { Input }