launchframe 0.2.0 → 0.2.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 (72) hide show
  1. package/README.md +143 -183
  2. package/bin/launchframe.mjs +234 -30
  3. package/package.json +52 -67
  4. package/template/.aider.conf.yml +3 -0
  5. package/template/.amazonq/cli-agents/clone-website.json +9 -0
  6. package/template/.amazonq/rules/project.md +156 -0
  7. package/template/.augment/commands/clone-website.md +516 -0
  8. package/template/.claude/skills/clone-website/SKILL.md +515 -0
  9. package/template/.clinerules +156 -0
  10. package/template/.codex/skills/clone-website/SKILL.md +515 -0
  11. package/template/.continue/commands/clone-website.md +517 -0
  12. package/template/.continue/rules/project.md +160 -0
  13. package/template/.cursor/commands/clone-website.md +512 -0
  14. package/template/.cursor/rules/project.mdc +7 -0
  15. package/template/.dockerignore +60 -0
  16. package/template/.gemini/commands/clone-website.toml +518 -0
  17. package/template/.gitattributes +9 -0
  18. package/template/.github/ISSUE_TEMPLATE/bug_report.yml +86 -0
  19. package/template/.github/ISSUE_TEMPLATE/config.yml +5 -0
  20. package/template/.github/ISSUE_TEMPLATE/feature_request.yml +50 -0
  21. package/template/.github/PULL_REQUEST_TEMPLATE.md +19 -0
  22. package/template/.github/copilot-instructions.md +156 -0
  23. package/template/.github/copilot-setup-steps.yml +3 -0
  24. package/template/.github/skills/clone-website/SKILL.md +515 -0
  25. package/template/.github/workflows/ci.yml +36 -0
  26. package/template/.nvmrc +1 -0
  27. package/template/.opencode/commands/clone-website.md +515 -0
  28. package/template/.windsurf/workflows/clone-website.md +512 -0
  29. package/template/.windsurfrules +2 -0
  30. package/template/AGENTS.md +74 -0
  31. package/template/CHANGELOG.md +80 -0
  32. package/template/CLAUDE.md +1 -0
  33. package/template/Dockerfile +114 -0
  34. package/template/Dockerfile.dev +15 -0
  35. package/template/GEMINI.md +1 -0
  36. package/template/README.md +129 -0
  37. package/template/components.json +25 -0
  38. package/template/docker-compose.yml +53 -0
  39. package/template/docs/design-references/.gitkeep +0 -0
  40. package/template/docs/design-references/comparison.png +0 -0
  41. package/template/docs/research/INSPECTION_GUIDE.md +80 -0
  42. package/template/eslint.config.mjs +18 -0
  43. package/template/next.config.ts +8 -0
  44. package/template/package.json +59 -0
  45. package/template/postcss.config.mjs +7 -0
  46. package/template/public/images/.gitkeep +0 -0
  47. package/template/public/seo/.gitkeep +0 -0
  48. package/template/public/videos/.gitkeep +0 -0
  49. package/template/scripts/.gitkeep +0 -0
  50. package/template/scripts/sync-agent-rules.sh +88 -0
  51. package/template/scripts/sync-skills.mjs +111 -0
  52. package/template/src/app/favicon.ico +0 -0
  53. package/template/src/app/globals.css +130 -0
  54. package/template/src/app/layout.tsx +33 -0
  55. package/template/src/app/page.tsx +9 -0
  56. package/template/src/components/ui/button.tsx +60 -0
  57. package/template/src/hooks/.gitkeep +0 -0
  58. package/template/src/lib/utils.ts +6 -0
  59. package/template/src/types/.gitkeep +0 -0
  60. package/template/tsconfig.json +34 -0
  61. package/packages/extract/automated-clone-pass.ts +0 -353
  62. package/packages/extract/browser-extract.ts +0 -237
  63. package/packages/extract/cloner-research-emit.ts +0 -270
  64. package/packages/extract/dom-crawler.ts +0 -521
  65. package/packages/extract/emit.ts +0 -553
  66. package/packages/extract/extract.ts +0 -548
  67. package/packages/extract/host-slug.ts +0 -5
  68. package/packages/extract/mirror-emit.ts +0 -620
  69. package/packages/extract/package.json +0 -13
  70. package/packages/extract/reference-dump.ts +0 -431
  71. package/packages/extract/synthesize.ts +0 -551
  72. package/packages/extract/types.ts +0 -316
@@ -0,0 +1,88 @@
1
+ #!/usr/bin/env bash
2
+ #
3
+ # sync-agent-rules.sh — Generate AI agent config files from AGENTS.md
4
+ #
5
+ # AGENTS.md is the single source of truth. This script creates copies
6
+ # for agents that don't read AGENTS.md natively (Cline, Continue,
7
+ # Amazon Q, GitHub Copilot Chat).
8
+ #
9
+ # Usage:
10
+ # bash scripts/sync-agent-rules.sh
11
+ #
12
+ # Agents that DON'T need generated files (they read AGENTS.md natively):
13
+ # Codex CLI, OpenCode, Cursor, Windsurf, Copilot Coding Agent,
14
+ # Roo Code, Aider, Augment Code
15
+ #
16
+ # Agents with their own thin pointer files (created manually):
17
+ # Claude Code → CLAUDE.md (@AGENTS.md import)
18
+ # Gemini CLI → GEMINI.md (@AGENTS.md import)
19
+ # Cursor → .cursor/rules/project.mdc (pointer)
20
+ # Windsurf → .windsurfrules (pointer)
21
+ # Aider → .aider.conf.yml (read: [AGENTS.md])
22
+
23
+ set -euo pipefail
24
+
25
+ REPO_ROOT="$(cd "$(dirname "$0")/.." && pwd)"
26
+ SOURCE="$REPO_ROOT/AGENTS.md"
27
+
28
+ if [[ ! -f "$SOURCE" ]]; then
29
+ echo "Error: AGENTS.md not found at $SOURCE" >&2
30
+ exit 1
31
+ fi
32
+
33
+ # Resolve @file imports (Claude Code syntax) into inline content.
34
+ # Lines like "@docs/research/INSPECTION_GUIDE.md" become the file's contents.
35
+ resolve_imports() {
36
+ while IFS= read -r line || [[ -n "$line" ]]; do
37
+ line="${line%$'\r'}"
38
+ if [[ "$line" =~ ^@(.+)$ ]]; then
39
+ local import_path="${BASH_REMATCH[1]}"
40
+ local resolved="$REPO_ROOT/$import_path"
41
+ if [[ -f "$resolved" ]]; then
42
+ cat "$resolved"
43
+ echo ""
44
+ else
45
+ echo "<!-- Import not found: $import_path -->"
46
+ fi
47
+ else
48
+ echo "$line"
49
+ fi
50
+ done < "$SOURCE"
51
+ }
52
+
53
+ RESOLVED_CONTENT="$(resolve_imports)"
54
+
55
+ HEADER="<!-- AUTO-GENERATED from AGENTS.md — do not edit directly.
56
+ Run \`bash scripts/sync-agent-rules.sh\` to regenerate. -->"
57
+
58
+ # Helper: write a generated file with header
59
+ write_file() {
60
+ local target="$1"
61
+ local content="$2"
62
+ mkdir -p "$(dirname "$target")"
63
+ printf '%s\n\n%s\n' "$HEADER" "$content" > "$target"
64
+ echo " ✓ $target"
65
+ }
66
+
67
+ echo "Syncing agent rules from AGENTS.md..."
68
+
69
+ # GitHub Copilot Chat — .github/copilot-instructions.md
70
+ write_file "$REPO_ROOT/.github/copilot-instructions.md" "$RESOLVED_CONTENT"
71
+
72
+ # Cline / Roo Code — .clinerules
73
+ write_file "$REPO_ROOT/.clinerules" "$RESOLVED_CONTENT"
74
+
75
+ # Continue — .continue/rules/project.md
76
+ CONTINUE_FRONTMATTER="---
77
+ description: Project conventions for AI Website Clone Template
78
+ alwaysApply: true
79
+ ---"
80
+ write_file "$REPO_ROOT/.continue/rules/project.md" "$CONTINUE_FRONTMATTER
81
+ $RESOLVED_CONTENT"
82
+
83
+ # Amazon Q Developer — .amazonq/rules/project.md
84
+ write_file "$REPO_ROOT/.amazonq/rules/project.md" "$RESOLVED_CONTENT"
85
+
86
+ echo ""
87
+ echo "Done. Generated files are committed to the repo but sourced from AGENTS.md."
88
+ echo "Edit AGENTS.md, then re-run this script to update all agent configs."
@@ -0,0 +1,111 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Generates clone-website command/skill files for all supported AI coding platforms.
5
+ * Source of truth: .claude/skills/clone-website/SKILL.md
6
+ *
7
+ * Usage: node scripts/sync-skills.mjs
8
+ */
9
+
10
+ import { readFileSync, writeFileSync, mkdirSync } from 'node:fs';
11
+ import { dirname, join } from 'node:path';
12
+ import { fileURLToPath } from 'node:url';
13
+
14
+ const ROOT = join(dirname(fileURLToPath(import.meta.url)), '..');
15
+ const SOURCE = join(ROOT, '.claude', 'skills', 'clone-website', 'SKILL.md');
16
+
17
+ // --- Parse source skill ---
18
+
19
+ let raw;
20
+ try {
21
+ raw = readFileSync(SOURCE, 'utf8').replace(/\r\n/g, '\n');
22
+ } catch {
23
+ console.error(`Error: Source skill not found at .claude/skills/clone-website/SKILL.md`);
24
+ process.exit(1);
25
+ }
26
+
27
+ const match = raw.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/);
28
+ if (!match) {
29
+ console.error('Error: Could not parse SKILL.md frontmatter');
30
+ process.exit(1);
31
+ }
32
+
33
+ const body = match[2];
34
+ const shortDesc = 'Reverse-engineer and clone any website as a pixel-perfect replica';
35
+
36
+ // --- Helpers ---
37
+
38
+ function write(relPath, content) {
39
+ const full = join(ROOT, relPath);
40
+ mkdirSync(dirname(full), { recursive: true });
41
+ writeFileSync(full, content, 'utf8');
42
+ console.log(` \u2713 ${relPath}`);
43
+ }
44
+
45
+ const HEADER =
46
+ '<!-- AUTO-GENERATED from .claude/skills/clone-website/SKILL.md \u2014 do not edit directly.\n' +
47
+ ' Run `node scripts/sync-skills.mjs` to regenerate. -->\n\n';
48
+
49
+ const noArgs = (text) => text.replace(/\$ARGUMENTS/g, 'the target URL provided by the user');
50
+
51
+ // --- Generate ---
52
+
53
+ console.log('Syncing clone-website skill to all platforms...');
54
+ console.log(` Source: .claude/skills/clone-website/SKILL.md\n`);
55
+
56
+ // 1. Codex CLI — same SKILL.md format, same $ARGUMENTS syntax
57
+ write('.codex/skills/clone-website/SKILL.md', raw);
58
+
59
+ // 2. GitHub Copilot — same SKILL.md format
60
+ write('.github/skills/clone-website/SKILL.md', raw);
61
+
62
+ // 3. Cursor — plain markdown, no argument substitution support
63
+ write('.cursor/commands/clone-website.md', HEADER + noArgs(body));
64
+
65
+ // 4. Windsurf — markdown workflow
66
+ write('.windsurf/workflows/clone-website.md', HEADER + noArgs(body));
67
+
68
+ // 5. Gemini CLI — TOML format, {{args}} for arguments
69
+ const geminiBody = body.replace(/\$ARGUMENTS/g, '{{args}}');
70
+ write(
71
+ '.gemini/commands/clone-website.toml',
72
+ `# AUTO-GENERATED from .claude/skills/clone-website/SKILL.md\n` +
73
+ `# Run \`node scripts/sync-skills.mjs\` to regenerate.\n\n` +
74
+ `description = "${shortDesc}"\n\n` +
75
+ `[prompt]\ntext = '''\n${geminiBody}\n'''\n`
76
+ );
77
+
78
+ // 6. OpenCode — markdown + YAML frontmatter, $ARGUMENTS works natively
79
+ write(
80
+ '.opencode/commands/clone-website.md',
81
+ `---\ndescription: "${shortDesc}"\n---\n${HEADER}${body}`
82
+ );
83
+
84
+ // 7. Augment Code — markdown + YAML frontmatter
85
+ write(
86
+ '.augment/commands/clone-website.md',
87
+ `---\ndescription: "${shortDesc}"\nargument-hint: "<url>"\n---\n${HEADER}${body}`
88
+ );
89
+
90
+ // 8. Continue — prompt file with invokable: true
91
+ write(
92
+ '.continue/commands/clone-website.md',
93
+ `---\nname: clone-website\ndescription: "${shortDesc}"\ninvokable: true\n---\n${HEADER}${body}`
94
+ );
95
+
96
+ // 9. Amazon Q — JSON agent definition
97
+ write(
98
+ '.amazonq/cli-agents/clone-website.json',
99
+ JSON.stringify(
100
+ {
101
+ name: 'clone-website',
102
+ description: shortDesc,
103
+ prompt: noArgs(body),
104
+ fileContext: ['AGENTS.md', 'docs/research/**'],
105
+ },
106
+ null,
107
+ 2
108
+ ) + '\n'
109
+ );
110
+
111
+ console.log('\nDone! 9 platform command files generated from source skill.');
Binary file
@@ -0,0 +1,130 @@
1
+ @import "tailwindcss";
2
+ @import "tw-animate-css";
3
+ @import "shadcn/tailwind.css";
4
+
5
+ @custom-variant dark (&:is(.dark *));
6
+
7
+ @theme inline {
8
+ --color-background: var(--background);
9
+ --color-foreground: var(--foreground);
10
+ --font-sans: "Geist", "Geist Fallback", ui-sans-serif, system-ui, sans-serif;
11
+ --font-mono: "Geist Mono", "Geist Mono Fallback", ui-monospace, monospace;
12
+ --font-heading: var(--font-sans);
13
+ --color-sidebar-ring: var(--sidebar-ring);
14
+ --color-sidebar-border: var(--sidebar-border);
15
+ --color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
16
+ --color-sidebar-accent: var(--sidebar-accent);
17
+ --color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
18
+ --color-sidebar-primary: var(--sidebar-primary);
19
+ --color-sidebar-foreground: var(--sidebar-foreground);
20
+ --color-sidebar: var(--sidebar);
21
+ --color-chart-5: var(--chart-5);
22
+ --color-chart-4: var(--chart-4);
23
+ --color-chart-3: var(--chart-3);
24
+ --color-chart-2: var(--chart-2);
25
+ --color-chart-1: var(--chart-1);
26
+ --color-ring: var(--ring);
27
+ --color-input: var(--input);
28
+ --color-border: var(--border);
29
+ --color-destructive: var(--destructive);
30
+ --color-accent-foreground: var(--accent-foreground);
31
+ --color-accent: var(--accent);
32
+ --color-muted-foreground: var(--muted-foreground);
33
+ --color-muted: var(--muted);
34
+ --color-secondary-foreground: var(--secondary-foreground);
35
+ --color-secondary: var(--secondary);
36
+ --color-primary-foreground: var(--primary-foreground);
37
+ --color-primary: var(--primary);
38
+ --color-popover-foreground: var(--popover-foreground);
39
+ --color-popover: var(--popover);
40
+ --color-card-foreground: var(--card-foreground);
41
+ --color-card: var(--card);
42
+ --radius-sm: calc(var(--radius) * 0.6);
43
+ --radius-md: calc(var(--radius) * 0.8);
44
+ --radius-lg: var(--radius);
45
+ --radius-xl: calc(var(--radius) * 1.4);
46
+ --radius-2xl: calc(var(--radius) * 1.8);
47
+ --radius-3xl: calc(var(--radius) * 2.2);
48
+ --radius-4xl: calc(var(--radius) * 2.6);
49
+ }
50
+
51
+ :root {
52
+ --background: oklch(1 0 0);
53
+ --foreground: oklch(0.145 0 0);
54
+ --card: oklch(1 0 0);
55
+ --card-foreground: oklch(0.145 0 0);
56
+ --popover: oklch(1 0 0);
57
+ --popover-foreground: oklch(0.145 0 0);
58
+ --primary: oklch(0.205 0 0);
59
+ --primary-foreground: oklch(0.985 0 0);
60
+ --secondary: oklch(0.97 0 0);
61
+ --secondary-foreground: oklch(0.205 0 0);
62
+ --muted: oklch(0.97 0 0);
63
+ --muted-foreground: oklch(0.556 0 0);
64
+ --accent: oklch(0.97 0 0);
65
+ --accent-foreground: oklch(0.205 0 0);
66
+ --destructive: oklch(0.577 0.245 27.325);
67
+ --border: oklch(0.922 0 0);
68
+ --input: oklch(0.922 0 0);
69
+ --ring: oklch(0.708 0 0);
70
+ --chart-1: oklch(0.87 0 0);
71
+ --chart-2: oklch(0.556 0 0);
72
+ --chart-3: oklch(0.439 0 0);
73
+ --chart-4: oklch(0.371 0 0);
74
+ --chart-5: oklch(0.269 0 0);
75
+ --radius: 0.625rem;
76
+ --sidebar: oklch(0.985 0 0);
77
+ --sidebar-foreground: oklch(0.145 0 0);
78
+ --sidebar-primary: oklch(0.205 0 0);
79
+ --sidebar-primary-foreground: oklch(0.985 0 0);
80
+ --sidebar-accent: oklch(0.97 0 0);
81
+ --sidebar-accent-foreground: oklch(0.205 0 0);
82
+ --sidebar-border: oklch(0.922 0 0);
83
+ --sidebar-ring: oklch(0.708 0 0);
84
+ }
85
+
86
+ .dark {
87
+ --background: oklch(0.145 0 0);
88
+ --foreground: oklch(0.985 0 0);
89
+ --card: oklch(0.205 0 0);
90
+ --card-foreground: oklch(0.985 0 0);
91
+ --popover: oklch(0.205 0 0);
92
+ --popover-foreground: oklch(0.985 0 0);
93
+ --primary: oklch(0.922 0 0);
94
+ --primary-foreground: oklch(0.205 0 0);
95
+ --secondary: oklch(0.269 0 0);
96
+ --secondary-foreground: oklch(0.985 0 0);
97
+ --muted: oklch(0.269 0 0);
98
+ --muted-foreground: oklch(0.708 0 0);
99
+ --accent: oklch(0.269 0 0);
100
+ --accent-foreground: oklch(0.985 0 0);
101
+ --destructive: oklch(0.704 0.191 22.216);
102
+ --border: oklch(1 0 0 / 10%);
103
+ --input: oklch(1 0 0 / 15%);
104
+ --ring: oklch(0.556 0 0);
105
+ --chart-1: oklch(0.87 0 0);
106
+ --chart-2: oklch(0.556 0 0);
107
+ --chart-3: oklch(0.439 0 0);
108
+ --chart-4: oklch(0.371 0 0);
109
+ --chart-5: oklch(0.269 0 0);
110
+ --sidebar: oklch(0.205 0 0);
111
+ --sidebar-foreground: oklch(0.985 0 0);
112
+ --sidebar-primary: oklch(0.488 0.243 264.376);
113
+ --sidebar-primary-foreground: oklch(0.985 0 0);
114
+ --sidebar-accent: oklch(0.269 0 0);
115
+ --sidebar-accent-foreground: oklch(0.985 0 0);
116
+ --sidebar-border: oklch(1 0 0 / 10%);
117
+ --sidebar-ring: oklch(0.556 0 0);
118
+ }
119
+
120
+ @layer base {
121
+ * {
122
+ @apply border-border outline-ring/50;
123
+ }
124
+ body {
125
+ @apply bg-background text-foreground;
126
+ }
127
+ html {
128
+ @apply font-sans;
129
+ }
130
+ }
@@ -0,0 +1,33 @@
1
+ import type { Metadata } from "next";
2
+ import { Geist, Geist_Mono } from "next/font/google";
3
+ import "./globals.css";
4
+
5
+ const geistSans = Geist({
6
+ variable: "--font-geist-sans",
7
+ subsets: ["latin"],
8
+ });
9
+
10
+ const geistMono = Geist_Mono({
11
+ variable: "--font-geist-mono",
12
+ subsets: ["latin"],
13
+ });
14
+
15
+ export const metadata: Metadata = {
16
+ title: "Website Clone",
17
+ description: "Pixel-perfect website clone",
18
+ };
19
+
20
+ export default function RootLayout({
21
+ children,
22
+ }: Readonly<{
23
+ children: React.ReactNode;
24
+ }>) {
25
+ return (
26
+ <html
27
+ lang="en"
28
+ className={`${geistSans.variable} ${geistMono.variable} h-full antialiased`}
29
+ >
30
+ <body className="min-h-full flex flex-col">{children}</body>
31
+ </html>
32
+ );
33
+ }
@@ -0,0 +1,9 @@
1
+ export default function Home() {
2
+ return (
3
+ <main className="flex min-h-screen items-center justify-center">
4
+ <p className="text-muted-foreground">
5
+ Clone target not yet built. Run <code className="font-mono text-foreground">/clone-website</code> to start.
6
+ </p>
7
+ </main>
8
+ );
9
+ }
@@ -0,0 +1,60 @@
1
+ "use client"
2
+
3
+ import { Button as ButtonPrimitive } from "@base-ui/react/button"
4
+ import { cva, type VariantProps } from "class-variance-authority"
5
+
6
+ import { cn } from "@/lib/utils"
7
+
8
+ const buttonVariants = cva(
9
+ "group/button inline-flex shrink-0 items-center justify-center rounded-lg border border-transparent bg-clip-padding text-sm font-medium whitespace-nowrap transition-all outline-none select-none focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 active:not-aria-[haspopup]:translate-y-px disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
10
+ {
11
+ variants: {
12
+ variant: {
13
+ default: "bg-primary text-primary-foreground [a]:hover:bg-primary/80",
14
+ outline:
15
+ "border-border bg-background hover:bg-muted hover:text-foreground aria-expanded:bg-muted aria-expanded:text-foreground dark:border-input dark:bg-input/30 dark:hover:bg-input/50",
16
+ secondary:
17
+ "bg-secondary text-secondary-foreground hover:bg-secondary/80 aria-expanded:bg-secondary aria-expanded:text-secondary-foreground",
18
+ ghost:
19
+ "hover:bg-muted hover:text-foreground aria-expanded:bg-muted aria-expanded:text-foreground dark:hover:bg-muted/50",
20
+ destructive:
21
+ "bg-destructive/10 text-destructive hover:bg-destructive/20 focus-visible:border-destructive/40 focus-visible:ring-destructive/20 dark:bg-destructive/20 dark:hover:bg-destructive/30 dark:focus-visible:ring-destructive/40",
22
+ link: "text-primary underline-offset-4 hover:underline",
23
+ },
24
+ size: {
25
+ default:
26
+ "h-8 gap-1.5 px-2.5 has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2",
27
+ xs: "h-6 gap-1 rounded-[min(var(--radius-md),10px)] px-2 text-xs in-data-[slot=button-group]:rounded-lg has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 [&_svg:not([class*='size-'])]:size-3",
28
+ sm: "h-7 gap-1 rounded-[min(var(--radius-md),12px)] px-2.5 text-[0.8rem] in-data-[slot=button-group]:rounded-lg has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 [&_svg:not([class*='size-'])]:size-3.5",
29
+ lg: "h-9 gap-1.5 px-2.5 has-data-[icon=inline-end]:pr-3 has-data-[icon=inline-start]:pl-3",
30
+ icon: "size-8",
31
+ "icon-xs":
32
+ "size-6 rounded-[min(var(--radius-md),10px)] in-data-[slot=button-group]:rounded-lg [&_svg:not([class*='size-'])]:size-3",
33
+ "icon-sm":
34
+ "size-7 rounded-[min(var(--radius-md),12px)] in-data-[slot=button-group]:rounded-lg",
35
+ "icon-lg": "size-9",
36
+ },
37
+ },
38
+ defaultVariants: {
39
+ variant: "default",
40
+ size: "default",
41
+ },
42
+ }
43
+ )
44
+
45
+ function Button({
46
+ className,
47
+ variant = "default",
48
+ size = "default",
49
+ ...props
50
+ }: ButtonPrimitive.Props & VariantProps<typeof buttonVariants>) {
51
+ return (
52
+ <ButtonPrimitive
53
+ data-slot="button"
54
+ className={cn(buttonVariants({ variant, size, className }))}
55
+ {...props}
56
+ />
57
+ )
58
+ }
59
+
60
+ export { Button, buttonVariants }
File without changes
@@ -0,0 +1,6 @@
1
+ import { clsx, type ClassValue } from "clsx"
2
+ import { twMerge } from "tailwind-merge"
3
+
4
+ export function cn(...inputs: ClassValue[]) {
5
+ return twMerge(clsx(inputs))
6
+ }
File without changes
@@ -0,0 +1,34 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2017",
4
+ "lib": ["dom", "dom.iterable", "esnext"],
5
+ "allowJs": true,
6
+ "skipLibCheck": true,
7
+ "strict": true,
8
+ "noEmit": true,
9
+ "esModuleInterop": true,
10
+ "module": "esnext",
11
+ "moduleResolution": "bundler",
12
+ "resolveJsonModule": true,
13
+ "isolatedModules": true,
14
+ "jsx": "react-jsx",
15
+ "incremental": true,
16
+ "plugins": [
17
+ {
18
+ "name": "next"
19
+ }
20
+ ],
21
+ "paths": {
22
+ "@/*": ["./src/*"]
23
+ }
24
+ },
25
+ "include": [
26
+ "next-env.d.ts",
27
+ "**/*.ts",
28
+ "**/*.tsx",
29
+ ".next/types/**/*.ts",
30
+ ".next/dev/types/**/*.ts",
31
+ "**/*.mts"
32
+ ],
33
+ "exclude": ["node_modules"]
34
+ }