launchframe 0.4.0 → 0.4.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/.amazonq/cli-agents/clone-website.json +9 -0
- package/.amazonq/cli-agents/launchframe.json +9 -0
- package/.amazonq/rules/project.md +158 -0
- package/.augment/commands/clone-website.md +488 -0
- package/.augment/commands/launchframe.md +43 -0
- package/.claude/skills/clone-website/SKILL.md +14 -0
- package/.claude/skills/launchframe/SKILL.md +42 -0
- package/.clinerules +78 -67
- package/.codex/skills/clone-website/SKILL.md +487 -473
- package/.codex/skills/launchframe/SKILL.md +42 -0
- package/.continue/commands/clone-website.md +489 -475
- package/.continue/commands/launchframe.md +44 -0
- package/.continue/rules/project.md +82 -71
- package/.cursor/commands/clone-website.md +484 -470
- package/.cursor/commands/launchframe.md +39 -0
- package/.gemini/commands/clone-website.toml +490 -476
- package/.gemini/commands/launchframe.toml +45 -0
- package/.github/copilot-instructions.md +78 -67
- package/.github/skills/clone-website/SKILL.md +487 -473
- package/.github/skills/launchframe/SKILL.md +42 -0
- package/.opencode/commands/clone-website.md +487 -473
- package/.opencode/commands/launchframe.md +42 -0
- package/.windsurf/workflows/clone-website.md +484 -470
- package/.windsurf/workflows/launchframe.md +39 -0
- package/AGENTS.md +3 -2
- package/README.md +15 -14
- package/bin/launchframe.mjs +114 -77
- package/docs/research/INSPECTION_GUIDE.md +10 -0
- package/package.json +4 -2
- package/scripts/sync-skills.mjs +108 -95
- package/src/lib/launchframe-config.ts +2 -3
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
<!-- AUTO-GENERATED from .claude/skills/launchframe/SKILL.md — do not edit directly.
|
|
2
|
+
Run `node scripts/sync-skills.mjs` to regenerate. -->
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
# Launchframe
|
|
6
|
+
|
|
7
|
+
Help the user create a **new** Launchframe project. The CLI is **parameterless**:
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npx launchframe@latest
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
It unpacks the full template into the **current working directory** (the folder root where the user runs the command). They should `mkdir`, `cd` into an **empty** folder first.
|
|
14
|
+
|
|
15
|
+
## What you do
|
|
16
|
+
|
|
17
|
+
1. Ensure the user has an **empty** directory (no existing `package.json`, `src/`, or `next.config.ts` there). If the workspace is already a Launchframe/Next project, they may only need `/clone-website` instead — clarify.
|
|
18
|
+
|
|
19
|
+
2. Run exactly:
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
npx launchframe@latest
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Optional: `LAUNCHFRAME_SOURCE_URL` and `LAUNCHFRAME_SAAS_IDEA` environment variables in the same shell if they want values pre-filled without editing files later.
|
|
26
|
+
|
|
27
|
+
3. **Never** run this with the output target **inside** the `launchframe` package directory (the npm-installed template). If the open folder is this monorepo/template, have them `mkdir ../my-app && cd ../my-app` (sibling path), then run `npx launchframe@latest` there.
|
|
28
|
+
|
|
29
|
+
4. Optional flags (same as CLI): `--dir <path>` to scaffold into another folder instead of cwd, `--skip-install` to skip `npm install`.
|
|
30
|
+
|
|
31
|
+
5. After it finishes: they should **`npm run dev`** from **that same folder** (no extra `cd` if they scaffolded into cwd). Then they edit **`src/lib/launchframe-config.ts`** for reference URL and SaaS idea, and run **`/clone-website`** with that URL.
|
|
32
|
+
|
|
33
|
+
## If optional notes from the user after the slash command (the CLI itself needs no URL or SaaS strings) are present
|
|
34
|
+
|
|
35
|
+
The user may still paste a URL or idea in chat — **do not** require them for the CLI. Put any extra context into **`src/lib/launchframe-config.ts`** or `launchframe.context.json` for them after scaffold.
|
|
36
|
+
|
|
37
|
+
## Fallback (local dev only)
|
|
38
|
+
|
|
39
|
+
From a checkout of this repo: `node bin/launchframe.mjs` with the same empty-folder rules (optionally `--dir` outside the package root).
|
package/AGENTS.md
CHANGED
|
@@ -7,7 +7,7 @@ This version has breaking changes — APIs, conventions, and file structure may
|
|
|
7
7
|
# Website Reverse-Engineer Template
|
|
8
8
|
|
|
9
9
|
## What This Is
|
|
10
|
-
A reusable template for reverse-engineering any website into a clean, modern Next.js codebase using AI coding agents. The Next.js + shadcn/ui + Tailwind v4 base is pre-scaffolded —
|
|
10
|
+
A reusable template for reverse-engineering any website into a clean, modern Next.js codebase using AI coding agents. The Next.js + shadcn/ui + Tailwind v4 base is pre-scaffolded — for a **new** folder, `mkdir`, `cd` into an **empty** directory, run **`npx launchframe@latest`** (no parameters), then edit **`src/lib/launchframe-config.ts`** and run **`/clone-website`** with your URL (or invoke **`/launchframe`** so the agent runs the same command). Or work in this repo and run **`/clone-website <url1> [<url2> ...]`** to clone into the current tree.
|
|
11
11
|
|
|
12
12
|
## Tech Stack
|
|
13
13
|
- **Framework:** Next.js 16 (App Router, React 19, TypeScript strict)
|
|
@@ -35,6 +35,7 @@ A reusable template for reverse-engineering any website into a clean, modern Nex
|
|
|
35
35
|
- **No personal aesthetic changes during emulation phase** — match 1:1 first, customize later
|
|
36
36
|
- **Real content** — use actual text and assets from the target site, not placeholders
|
|
37
37
|
- **Beauty-first** — every pixel matters
|
|
38
|
+
- **DOM crawl priority** — when walking the target page, emphasize **images** (raster, responsive sources, CSS backgrounds), **SVGs** (inline icons, sprites, masks), then **motion** (keyframes, transitions, scroll/time-driven animation, libraries). Scrape and save real assets from the DOM/network first; **create** a stand-in image or vector only when fetch/blocking prevents extraction, and label substitutes clearly in research notes so the clone stays auditable
|
|
38
39
|
|
|
39
40
|
## Project Structure
|
|
40
41
|
```
|
|
@@ -60,6 +61,6 @@ scripts/ # Asset download scripts
|
|
|
60
61
|
## MOST IMPORTANT NOTES
|
|
61
62
|
- When launching Claude Code agent teams, ALWAYS have each teammate work in their own worktree branch and merge everyone's work at the end, resolving any merge conflicts smartly since you are basically serving the orchestrator role and have full context to our goals, work given, work achieved, and desired outcomes.
|
|
62
63
|
- After editing `AGENTS.md`, run `bash scripts/sync-agent-rules.sh` to regenerate platform-specific instruction files.
|
|
63
|
-
- After editing `.claude/skills
|
|
64
|
+
- After editing `.claude/skills/*/SKILL.md` (for example `clone-website` or `launchframe`), run `node scripts/sync-skills.mjs` to regenerate skill and command files for all platforms.
|
|
64
65
|
|
|
65
66
|
@docs/research/INSPECTION_GUIDE.md
|
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
<a href="https://github.com/JCodesMore/ai-website-cloner-template/blob/master/LICENSE"><img src="https://img.shields.io/badge/license-MIT-blue" alt="MIT License" /></a> <a href="https://github.com/JCodesMore/ai-website-cloner-template/stargazers"><img src="https://img.shields.io/github/stars/JCodesMore/ai-website-cloner-template?style=flat" alt="Stars" /></a> <a href="https://discord.gg/hrTSX5yTpB"><img src="https://img.shields.io/discord/1400896964597383279?label=discord" alt="Discord" /></a>
|
|
4
4
|
|
|
5
|
-
Scaffold a Next.js + shadcn/ui project
|
|
5
|
+
Scaffold a Next.js + shadcn/ui project in **the folder where you run the command** — use **`npx launchframe@latest`** (no arguments; create an empty directory, `cd` into it, then run it). Configure the reference URL and SaaS copy in **`src/lib/launchframe-config.ts`**. Then run **`/clone-website`** so your AI agent reverse-engineers the reference layout while preserving your messaging (`launchframe.context.json`, `docs/research/LAUNCHFRAME.md`, `src/lib/launchframe-config.ts`). In the agent, **`/launchframe`** means the same one-liner.
|
|
6
6
|
|
|
7
7
|
**Recommended: [Claude Code](https://docs.anthropic.com/en/docs/claude-code) with Opus 4.7 for best results** — but works with a variety of AI coding agents.
|
|
8
8
|
|
|
@@ -14,32 +14,33 @@ Scaffold a Next.js + shadcn/ui project from **a reference URL you want to copy**
|
|
|
14
14
|
|
|
15
15
|
## Quick Start
|
|
16
16
|
|
|
17
|
-
###
|
|
17
|
+
### 1. New project (empty folder)
|
|
18
18
|
|
|
19
|
-
|
|
19
|
+
Create an **empty** directory, go into it, run the CLI with **no parameters** — files are written to **that folder** (your project root):
|
|
20
20
|
|
|
21
21
|
```bash
|
|
22
|
-
|
|
22
|
+
mkdir my-app
|
|
23
|
+
cd my-app
|
|
24
|
+
npx launchframe@latest
|
|
23
25
|
```
|
|
24
26
|
|
|
25
|
-
Optional
|
|
27
|
+
Optional: set `LAUNCHFRAME_SOURCE_URL` and `LAUNCHFRAME_SAAS_IDEA` in the environment before running to pre-fill config without editing files.
|
|
26
28
|
|
|
27
|
-
|
|
28
|
-
- `--skip-install` — scaffold files only; run `npm install` yourself
|
|
29
|
+
Then edit `src/lib/launchframe-config.ts` if you still need to change the reference URL or SaaS pitch, run `npm run dev`, and use **`/clone-website`** with your reference URL.
|
|
29
30
|
|
|
30
|
-
|
|
31
|
+
### AI agents (slash command)
|
|
31
32
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
```
|
|
33
|
+
In Cursor, Claude Code, Continue, etc., **`/launchframe`** tells the agent to run **`npx launchframe@latest`** in an empty folder the user chooses (same as above). No URL or SaaS strings are required on the command line.
|
|
34
|
+
|
|
35
|
+
### Optional flags
|
|
36
36
|
|
|
37
|
-
|
|
37
|
+
- `--dir path` / `-o path` — scaffold into another folder instead of the current directory (must be empty)
|
|
38
|
+
- `--skip-install` — skip `npm install`
|
|
38
39
|
|
|
39
40
|
### Git template (advanced)
|
|
40
41
|
|
|
41
42
|
1. Clone this repository and `npm install`
|
|
42
|
-
2.
|
|
43
|
+
2. Point `src/lib/launchframe-config.ts` at your site, or create sibling folder and run **`npx launchframe@latest`** there
|
|
43
44
|
3. Run `/clone-website <target-url>` from your agent
|
|
44
45
|
|
|
45
46
|
> Using a different agent? Open `AGENTS.md` for project instructions — most agents pick it up automatically.
|
package/bin/launchframe.mjs
CHANGED
|
@@ -1,54 +1,82 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
/**
|
|
3
|
-
* Scaffold a Launchframe project
|
|
4
|
-
* Usage: npx launchframe@latest
|
|
3
|
+
* Scaffold a Launchframe project into the current directory (or --dir).
|
|
4
|
+
* Usage: npx launchframe@latest [--dir=path] [--skip-install]
|
|
5
|
+
* Optional: LAUNCHFRAME_SOURCE_URL, LAUNCHFRAME_SAAS_IDEA env vars, or legacy CLI args.
|
|
5
6
|
*/
|
|
6
7
|
|
|
7
|
-
import { cp, mkdir, readdir, readFile, writeFile } from "fs/promises";
|
|
8
|
+
import { cp, mkdir, readdir, readFile, stat, writeFile } from "fs/promises";
|
|
8
9
|
import { spawn } from "node:child_process";
|
|
9
|
-
import { dirname, isAbsolute, join, relative, resolve } from "node:path";
|
|
10
|
+
import { basename, dirname, isAbsolute, join, relative, resolve } from "node:path";
|
|
10
11
|
import { fileURLToPath } from "node:url";
|
|
11
12
|
|
|
12
13
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
13
14
|
const PKG_ROOT = resolve(__dirname, "..");
|
|
14
15
|
|
|
15
|
-
const
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
"
|
|
26
|
-
"
|
|
16
|
+
const DEFAULT_URL = "https://example.com";
|
|
17
|
+
const DEFAULT_SAAS_IDEA =
|
|
18
|
+
"Edit your SaaS pitch in src/lib/launchframe-config.ts, then run /clone-website with your reference URL.";
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Never copy these root entries into a scaffolded app (build artifacts, VCS, the CLI itself).
|
|
22
|
+
* All other root files and folders are copied as-is — including every dotfile and dot-directory
|
|
23
|
+
* so AI agents see the same agent rules and commands as this template.
|
|
24
|
+
*/
|
|
25
|
+
const SKIP_DIR_NAMES = new Set([
|
|
26
|
+
"bin",
|
|
27
|
+
"node_modules",
|
|
28
|
+
".git",
|
|
29
|
+
".next",
|
|
30
|
+
"dist",
|
|
31
|
+
"out",
|
|
32
|
+
"coverage",
|
|
33
|
+
".turbo",
|
|
27
34
|
]);
|
|
28
35
|
|
|
29
|
-
const SKIP_ROOT_FILES = new Set([
|
|
36
|
+
const SKIP_ROOT_FILES = new Set([
|
|
37
|
+
"package-lock.json",
|
|
38
|
+
".DS_Store",
|
|
39
|
+
"Thumbs.db",
|
|
40
|
+
]);
|
|
41
|
+
|
|
42
|
+
async function pathExists(p) {
|
|
43
|
+
try {
|
|
44
|
+
await stat(p);
|
|
45
|
+
return true;
|
|
46
|
+
} catch {
|
|
47
|
+
return false;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
30
50
|
|
|
31
51
|
function printHelp() {
|
|
32
52
|
console.log(`
|
|
33
|
-
launchframe — scaffold a Next.js app
|
|
53
|
+
launchframe — scaffold a Next.js app into the current directory (project root)
|
|
34
54
|
|
|
35
55
|
Usage:
|
|
36
|
-
npx launchframe@latest
|
|
56
|
+
npx launchframe@latest
|
|
57
|
+
|
|
58
|
+
This unpacks the template into the folder you are in (where you ran the command).
|
|
59
|
+
No URL or SaaS arguments are required — edit src/lib/launchframe-config.ts after scaffold.
|
|
37
60
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
61
|
+
Optional environment variables (same session):
|
|
62
|
+
LAUNCHFRAME_SOURCE_URL Reference site to clone later (https://...)
|
|
63
|
+
LAUNCHFRAME_SAAS_IDEA Short landing-page pitch text
|
|
64
|
+
|
|
65
|
+
Legacy (optional positional args, for scripts only):
|
|
66
|
+
npx launchframe@latest <url> "<saas-idea>"
|
|
41
67
|
|
|
42
68
|
Options:
|
|
43
|
-
--dir, -o
|
|
69
|
+
--dir, -o Scaffold into this folder instead of the current directory (must be empty)
|
|
44
70
|
--skip-install Do not run npm install after scaffolding
|
|
45
71
|
-h, --help Show this message
|
|
46
72
|
|
|
47
73
|
Note:
|
|
48
|
-
|
|
74
|
+
Run inside an empty project folder (no existing package.json / src / next.config).
|
|
75
|
+
The output folder must not be inside the Launchframe npm package directory.
|
|
49
76
|
|
|
50
77
|
Example:
|
|
51
|
-
|
|
78
|
+
mkdir my-app && cd my-app
|
|
79
|
+
npx launchframe@latest
|
|
52
80
|
`);
|
|
53
81
|
}
|
|
54
82
|
|
|
@@ -106,20 +134,8 @@ function validateUrl(raw) {
|
|
|
106
134
|
return u.href;
|
|
107
135
|
}
|
|
108
136
|
|
|
109
|
-
function
|
|
110
|
-
|
|
111
|
-
const host = new URL(urlStr).hostname.replace(/^www\./i, "");
|
|
112
|
-
const label = host.split(".")[0] || "site";
|
|
113
|
-
const safe = label.replace(/[^a-zA-Z0-9-_]/g, "-").toLowerCase();
|
|
114
|
-
return `${safe || "site"}-launchframe`;
|
|
115
|
-
} catch {
|
|
116
|
-
return "launchframe-app";
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
function slugFromDir(dir) {
|
|
122
|
-
const base = dir.replace(/\\/g, "/").split("/").filter(Boolean).pop() ?? "launchframe-app";
|
|
137
|
+
function slugFromDir(destRootAbs) {
|
|
138
|
+
const base = basename(resolve(destRootAbs));
|
|
123
139
|
return base
|
|
124
140
|
.toLowerCase()
|
|
125
141
|
.replace(/[^a-z0-9-_]/g, "-")
|
|
@@ -133,9 +149,6 @@ function tsStringLiteral(value) {
|
|
|
133
149
|
|
|
134
150
|
function shouldCopyRootEntry(baseName, isDirectory) {
|
|
135
151
|
if (SKIP_DIR_NAMES.has(baseName)) return false;
|
|
136
|
-
if (isDirectory && baseName.startsWith(".")) {
|
|
137
|
-
if (!ALLOW_DOT_DIRS.has(baseName)) return false;
|
|
138
|
-
}
|
|
139
152
|
if (!isDirectory && SKIP_ROOT_FILES.has(baseName)) return false;
|
|
140
153
|
return true;
|
|
141
154
|
}
|
|
@@ -188,7 +201,8 @@ async function writeGeneratedPackageJson(destRoot, npmPackageName) {
|
|
|
188
201
|
|
|
189
202
|
async function writeLaunchframeArtifacts(destRoot, url, idea) {
|
|
190
203
|
const configTs = `/**
|
|
191
|
-
*
|
|
204
|
+
* Set your reference site and positioning, then use /clone-website with the same URL.
|
|
205
|
+
* Written by Launchframe CLI — edit freely.
|
|
192
206
|
*/
|
|
193
207
|
export const LAUNCHFRAME_SOURCE_URL = ${tsStringLiteral(url)} as const;
|
|
194
208
|
|
|
@@ -201,7 +215,7 @@ export const LAUNCHFRAME_SAAS_IDEA = ${tsStringLiteral(idea)} as const;
|
|
|
201
215
|
sourceUrl: url,
|
|
202
216
|
saasIdea: idea,
|
|
203
217
|
notes:
|
|
204
|
-
"Use /clone-website with sourceUrl for pixel-perfect extraction.
|
|
218
|
+
"Edit this file or src/lib/launchframe-config.ts. Use /clone-website with sourceUrl for pixel-perfect extraction.",
|
|
205
219
|
};
|
|
206
220
|
await writeFile(
|
|
207
221
|
join(destRoot, "launchframe.context.json"),
|
|
@@ -219,7 +233,7 @@ ${url}
|
|
|
219
233
|
|
|
220
234
|
${idea}
|
|
221
235
|
|
|
222
|
-
When running \`/clone-website\`, pass the reference URL above. After structural cloning,
|
|
236
|
+
When running \`/clone-website\`, pass the reference URL above (update src/lib/launchframe-config.ts first if you still use the default). After structural cloning, align hero copy with your SaaS idea while respecting attribution and copyright for third-party brands.
|
|
223
237
|
`;
|
|
224
238
|
|
|
225
239
|
await mkdir(join(destRoot, "docs", "research"), { recursive: true });
|
|
@@ -231,7 +245,14 @@ async function writeReadme(destRoot, npmPackageName, url, idea) {
|
|
|
231
245
|
|
|
232
246
|
Created with [\`launchframe\`](https://www.npmjs.com/package/launchframe).
|
|
233
247
|
|
|
234
|
-
##
|
|
248
|
+
## Configure
|
|
249
|
+
|
|
250
|
+
Edit \`src/lib/launchframe-config.ts\`:
|
|
251
|
+
|
|
252
|
+
- \`LAUNCHFRAME_SOURCE_URL\` — site to reverse-engineer
|
|
253
|
+
- \`LAUNCHFRAME_SAAS_IDEA\` — landing copy / positioning
|
|
254
|
+
|
|
255
|
+
Current values (from scaffold):
|
|
235
256
|
|
|
236
257
|
- **Reference site:** ${url}
|
|
237
258
|
- **SaaS idea:** ${idea}
|
|
@@ -243,13 +264,7 @@ npm install
|
|
|
243
264
|
npm run dev
|
|
244
265
|
\`\`\`
|
|
245
266
|
|
|
246
|
-
Open your AI agent (
|
|
247
|
-
|
|
248
|
-
\`\`\`
|
|
249
|
-
/clone-website ${url}
|
|
250
|
-
\`\`\`
|
|
251
|
-
|
|
252
|
-
Keep the SaaS positioning from \`launchframe.context.json\` / \`src/lib/launchframe-config.ts\` when adapting cloned sections.
|
|
267
|
+
Open your AI agent and run \`/clone-website <your-reference-url>\` (same URL as in the config).
|
|
253
268
|
|
|
254
269
|
See \`AGENTS.md\` for full agent instructions.
|
|
255
270
|
`;
|
|
@@ -257,6 +272,21 @@ See \`AGENTS.md\` for full agent instructions.
|
|
|
257
272
|
await writeFile(join(destRoot, "README.md"), body, "utf8");
|
|
258
273
|
}
|
|
259
274
|
|
|
275
|
+
/** Refuse to stomp an existing Next-style project in the target directory. */
|
|
276
|
+
async function assertScaffoldTargetVacant(destRoot) {
|
|
277
|
+
const conflicts = ["package.json", "next.config.ts", "src"];
|
|
278
|
+
for (const c of conflicts) {
|
|
279
|
+
if (await pathExists(join(destRoot, c))) {
|
|
280
|
+
console.error(
|
|
281
|
+
`Refusing to scaffold: "${c}" already exists in this folder.\n` +
|
|
282
|
+
`Create a new empty directory, cd into it, then run:\n` +
|
|
283
|
+
` npx launchframe@latest\n`,
|
|
284
|
+
);
|
|
285
|
+
process.exit(1);
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
|
|
260
290
|
function runNpmInstall(cwd) {
|
|
261
291
|
return new Promise((resolvePromise, reject) => {
|
|
262
292
|
const child = spawn("npm", ["install"], {
|
|
@@ -272,6 +302,17 @@ function runNpmInstall(cwd) {
|
|
|
272
302
|
});
|
|
273
303
|
}
|
|
274
304
|
|
|
305
|
+
function resolveUrlAndIdea(args) {
|
|
306
|
+
const envUrl =
|
|
307
|
+
process.env.LAUNCHFRAME_SOURCE_URL?.trim() || process.env.LAUNCHFRAME_URL?.trim();
|
|
308
|
+
const envIdea = process.env.LAUNCHFRAME_SAAS_IDEA?.trim();
|
|
309
|
+
|
|
310
|
+
let urlRaw = args.url?.trim() || envUrl || DEFAULT_URL;
|
|
311
|
+
let ideaRaw = args.idea?.trim() || envIdea || DEFAULT_SAAS_IDEA;
|
|
312
|
+
|
|
313
|
+
return { urlRaw, ideaRaw };
|
|
314
|
+
}
|
|
315
|
+
|
|
275
316
|
async function main() {
|
|
276
317
|
const args = parseArgs(process.argv.slice(2));
|
|
277
318
|
if (args.help) {
|
|
@@ -279,36 +320,31 @@ async function main() {
|
|
|
279
320
|
process.exit(0);
|
|
280
321
|
}
|
|
281
322
|
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
323
|
+
const destRoot = args.dir?.trim()
|
|
324
|
+
? resolve(process.cwd(), args.dir.trim())
|
|
325
|
+
: resolve(process.cwd());
|
|
326
|
+
|
|
327
|
+
if (isForbiddenOutput(PKG_ROOT, destRoot)) {
|
|
328
|
+
console.error(
|
|
329
|
+
"Output folder cannot be inside the Launchframe package directory. Create a new folder outside this package and cd into it, then run npx launchframe@latest again.",
|
|
330
|
+
);
|
|
285
331
|
process.exit(1);
|
|
286
332
|
}
|
|
287
333
|
|
|
334
|
+
await assertScaffoldTargetVacant(destRoot);
|
|
335
|
+
|
|
336
|
+
const { urlRaw, ideaRaw } = resolveUrlAndIdea(args);
|
|
337
|
+
|
|
288
338
|
let url;
|
|
289
339
|
try {
|
|
290
|
-
url = validateUrl(
|
|
340
|
+
url = validateUrl(urlRaw);
|
|
291
341
|
} catch (e) {
|
|
292
342
|
console.error(String(e.message));
|
|
293
343
|
process.exit(1);
|
|
294
344
|
}
|
|
295
345
|
|
|
296
|
-
const idea =
|
|
297
|
-
|
|
298
|
-
console.error("Error: SaaS idea cannot be empty.");
|
|
299
|
-
process.exit(1);
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
const dirName = args.dir?.trim() || defaultDirName(url);
|
|
303
|
-
const destRoot = resolve(process.cwd(), dirName);
|
|
304
|
-
const npmPackageName = slugFromDir(dirName);
|
|
305
|
-
|
|
306
|
-
if (isForbiddenOutput(PKG_ROOT, destRoot)) {
|
|
307
|
-
console.error(
|
|
308
|
-
"Output folder cannot be inside the Launchframe package directory. Run from a parent folder or choose another path.",
|
|
309
|
-
);
|
|
310
|
-
process.exit(1);
|
|
311
|
-
}
|
|
346
|
+
const idea = ideaRaw.trim() || DEFAULT_SAAS_IDEA;
|
|
347
|
+
const npmPackageName = slugFromDir(destRoot);
|
|
312
348
|
|
|
313
349
|
await mkdir(destRoot, { recursive: true });
|
|
314
350
|
|
|
@@ -318,22 +354,23 @@ async function main() {
|
|
|
318
354
|
await writeLaunchframeArtifacts(destRoot, url, idea);
|
|
319
355
|
await writeReadme(destRoot, npmPackageName, url, idea);
|
|
320
356
|
|
|
321
|
-
console.log(`\
|
|
357
|
+
console.log(`\nScaffolded Launchframe in:\n ${destRoot}`);
|
|
322
358
|
console.log(` Reference URL: ${url}`);
|
|
323
359
|
console.log(` SaaS idea: ${idea}\n`);
|
|
360
|
+
console.log("Edit src/lib/launchframe-config.ts if you need different values.\n");
|
|
324
361
|
|
|
325
362
|
if (!args.skipInstall) {
|
|
326
363
|
console.log("Running npm install...\n");
|
|
327
364
|
try {
|
|
328
365
|
await runNpmInstall(destRoot);
|
|
329
|
-
console.log("\nDone.
|
|
366
|
+
console.log("\nDone. From this folder: npm run dev\n");
|
|
330
367
|
} catch (e) {
|
|
331
368
|
console.error(String(e.message));
|
|
332
|
-
console.error("\
|
|
369
|
+
console.error("\nRun npm install in this folder manually.\n");
|
|
333
370
|
process.exit(1);
|
|
334
371
|
}
|
|
335
372
|
} else {
|
|
336
|
-
console.log("Skipped npm install (--skip-install). Run npm install
|
|
373
|
+
console.log("Skipped npm install (--skip-install). Run npm install in this folder.\n");
|
|
337
374
|
}
|
|
338
375
|
}
|
|
339
376
|
|
|
@@ -4,6 +4,16 @@
|
|
|
4
4
|
|
|
5
5
|
This guide outlines what to capture when inspecting a target website via Chrome MCP or browser DevTools.
|
|
6
6
|
|
|
7
|
+
## Priority: media, SVGs, and motion (do this early)
|
|
8
|
+
|
|
9
|
+
When crawling the DOM and network, **tackle these before fine-tuning copy or spacing**:
|
|
10
|
+
|
|
11
|
+
1. **Raster imagery** — Every `<img>`, `<picture>` / `source`, `srcset` / `sizes`, CDN URLs, lazy-loaded `data-src`, `loading="lazy"` nodes, **CSS `background-image`** on the element and ancestors (including `::before` / `::after`), masks that use `url()`, `<video>` still / poster frames. Prefer **downloading** originals via scripts or MCP; if a URL is blocked or session-gated, **export a screenshot** of the element’s bounding box at a crisp DPR and store it under `public/images/`, and note the substitute in the spec.
|
|
12
|
+
2. **SVGs** — Inline `<svg>`, `<use>` / sprite sheets, **SVG in CSS** (`mask-image`, `background-image`), favicons as SVG, logo marks. Prefer extracting path/viewBox into React components or static files under `public/` — **recreate** from a screenshot/trace only when the markup is obfuscated or blocked.
|
|
13
|
+
3. **Motion & animation** — Inspect Styles for `animation`, `animation-name`, `animation-timeline`, `transition`, `transform`, `@keyframes`; check for libraries (Framer Motion, GSAP, Lottie, Lenis). Capture **durations, easings, delays, fill-modes**, scroll/view triggers, and `prefers-reduced-motion` handling. Motion often defines perceived quality — do not leave it as an afterthought.
|
|
14
|
+
|
|
15
|
+
Then continue with typography, spacing, and component structure as usual.
|
|
16
|
+
|
|
7
17
|
## Phase 1: Visual Audit
|
|
8
18
|
|
|
9
19
|
### Screenshots to Capture
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "launchframe",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.2",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Scaffold a Next.js app from a reference URL plus your SaaS idea — AI-ready website cloning",
|
|
6
6
|
"author": "JCodesMore",
|
|
@@ -62,7 +62,9 @@
|
|
|
62
62
|
".gemini",
|
|
63
63
|
".opencode",
|
|
64
64
|
".windsurf",
|
|
65
|
-
".github"
|
|
65
|
+
".github",
|
|
66
|
+
".amazonq",
|
|
67
|
+
".augment"
|
|
66
68
|
],
|
|
67
69
|
"scripts": {
|
|
68
70
|
"dev": "next dev",
|