launchframe 0.2.0 → 0.2.2
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/README.md +144 -183
- package/bin/launchframe.mjs +261 -28
- package/package.json +52 -67
- package/template/.aider.conf.yml +3 -0
- package/template/.amazonq/cli-agents/clone-website.json +9 -0
- package/template/.amazonq/rules/project.md +161 -0
- package/template/.augment/commands/clone-website.md +518 -0
- package/template/.claude/skills/clone-website/SKILL.md +517 -0
- package/template/.clinerules +161 -0
- package/template/.codex/skills/clone-website/SKILL.md +517 -0
- package/template/.continue/commands/clone-website.md +519 -0
- package/template/.continue/rules/project.md +165 -0
- package/template/.cursor/commands/clone-website.md +514 -0
- package/template/.cursor/rules/project.mdc +20 -0
- package/template/.dockerignore +60 -0
- package/template/.gemini/commands/clone-website.toml +520 -0
- package/template/.gitattributes +9 -0
- package/template/.github/ISSUE_TEMPLATE/bug_report.yml +86 -0
- package/template/.github/ISSUE_TEMPLATE/config.yml +5 -0
- package/template/.github/ISSUE_TEMPLATE/feature_request.yml +50 -0
- package/template/.github/PULL_REQUEST_TEMPLATE.md +19 -0
- package/template/.github/copilot-instructions.md +161 -0
- package/template/.github/copilot-setup-steps.yml +3 -0
- package/template/.github/skills/clone-website/SKILL.md +517 -0
- package/template/.github/workflows/ci.yml +36 -0
- package/template/.nvmrc +1 -0
- package/template/.opencode/commands/clone-website.md +517 -0
- package/template/.windsurf/workflows/clone-website.md +514 -0
- package/template/.windsurfrules +2 -0
- package/template/AGENTS.md +79 -0
- package/template/CHANGELOG.md +80 -0
- package/template/CLAUDE.md +1 -0
- package/template/Dockerfile +114 -0
- package/template/Dockerfile.dev +15 -0
- package/template/GEMINI.md +1 -0
- package/template/README.md +118 -0
- package/template/START_HERE.md +15 -0
- package/template/components.json +25 -0
- package/template/docker-compose.yml +53 -0
- package/template/docs/design-references/.gitkeep +0 -0
- package/template/docs/design-references/comparison.png +0 -0
- package/template/docs/research/INSPECTION_GUIDE.md +80 -0
- package/template/eslint.config.mjs +18 -0
- package/template/next.config.ts +8 -0
- package/template/package.json +59 -0
- package/template/postcss.config.mjs +7 -0
- package/template/public/images/.gitkeep +0 -0
- package/template/public/seo/.gitkeep +0 -0
- package/template/public/videos/.gitkeep +0 -0
- package/template/scripts/.gitkeep +0 -0
- package/template/scripts/sync-agent-rules.sh +88 -0
- package/template/scripts/sync-skills.mjs +111 -0
- package/template/src/app/favicon.ico +0 -0
- package/template/src/app/globals.css +130 -0
- package/template/src/app/layout.tsx +33 -0
- package/template/src/app/page.tsx +9 -0
- package/template/src/components/ui/button.tsx +60 -0
- package/template/src/hooks/.gitkeep +0 -0
- package/template/src/lib/utils.ts +6 -0
- package/template/src/types/.gitkeep +0 -0
- package/template/tsconfig.json +34 -0
- package/packages/extract/automated-clone-pass.ts +0 -353
- package/packages/extract/browser-extract.ts +0 -237
- package/packages/extract/cloner-research-emit.ts +0 -270
- package/packages/extract/dom-crawler.ts +0 -521
- package/packages/extract/emit.ts +0 -553
- package/packages/extract/extract.ts +0 -548
- package/packages/extract/host-slug.ts +0 -5
- package/packages/extract/mirror-emit.ts +0 -620
- package/packages/extract/package.json +0 -13
- package/packages/extract/reference-dump.ts +0 -431
- package/packages/extract/synthesize.ts +0 -551
- 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
|
|
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
|
+
}
|