@struq/skill 0.2.1 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +15 -5
- package/package.json +4 -4
- package/skills/struq/SKILL.md +304 -200
- package/skills/struq/cinematic/showpieces/README.md +18 -0
- package/skills/struq/cinematic/showpieces/toonstuk-fill-rows.tsx +74 -0
- package/skills/struq/cinematic/showpieces/toonstuk-hero-opener.tsx +128 -0
- package/skills/struq/cinematic/showpieces/toonstuk-image-to-fullbleed.tsx +78 -0
- package/skills/struq/cinematic/showpieces/toonstuk-manifesto-scrub.tsx +81 -0
- package/skills/struq/cinematic/showpieces/toonstuk-panel-wipe.tsx +89 -0
- package/skills/struq/cinematic/showpieces/toonstuk-pixel-resolve.tsx +107 -0
- package/skills/struq/cinematic/showpieces/toonstuk-project-stack.tsx +86 -0
- package/skills/struq/cinematic/showpieces/toonstuk-scrub-connector.tsx +78 -0
- package/skills/struq/cinematic/showpieces/toonstuk-wordmark-finale.tsx +105 -0
- package/skills/struq/recipes/app/fonts.ts +22 -0
- package/skills/struq/recipes/app/globals.css +185 -0
- package/skills/struq/recipes/app/layout.tsx +28 -0
- package/skills/struq/recipes/components/struq/Cursor.tsx +117 -0
- package/skills/struq/recipes/components/struq/HeroCanvas.tsx +278 -0
- package/skills/struq/recipes/components/struq/HorizontalGallery.tsx +106 -0
- package/skills/struq/recipes/components/struq/MagneticButton.tsx +92 -0
- package/skills/struq/recipes/components/struq/MediaFrame.tsx +96 -0
- package/skills/struq/recipes/components/struq/PinnedZoom.tsx +79 -0
- package/skills/struq/recipes/components/struq/Reveal.tsx +64 -0
- package/skills/struq/recipes/components/struq/SmoothScroll.tsx +57 -0
- package/skills/struq/recipes/components/struq/SplitHeadline.tsx +68 -0
- package/skills/struq/recipes/components/struq/scenes/contours.ts +161 -0
- package/skills/struq/recipes/components/struq/scenes/dots.ts +110 -0
- package/skills/struq/recipes/components/struq/scenes/ripple.ts +99 -0
- package/skills/struq/recipes/components/struq/scenes/types.ts +114 -0
- package/skills/struq/recipes/lib/gsap.ts +21 -0
- package/skills/struq/references/anti-patterns.md +71 -6
- package/skills/struq/references/baseline-prompt.md +18 -14
- package/skills/struq/references/brief-router.md +7 -8
- package/skills/struq/references/composition.md +0 -1
- package/skills/struq/references/depth-parallax.md +1 -1
- package/skills/struq/references/direction.md +5 -5
- package/skills/struq/references/hero-scenes.md +107 -0
- package/skills/struq/references/iconography.md +1 -1
- package/skills/struq/references/imagery.md +10 -29
- package/skills/struq/references/micro-interactions.md +1 -1
- package/skills/struq/references/mobile-composition.md +1 -1
- package/skills/struq/references/niche-packets.md +338 -0
- package/skills/struq/references/page-architecture.md +2 -2
- package/skills/struq/references/pinning-scrub.md +3 -4
- package/skills/struq/references/scroll-reveals.md +1 -2
- package/skills/struq/references/sections.md +141 -0
- package/skills/struq/references/seo.md +2 -2
- package/skills/struq/references/showcase.md +2 -2
- package/skills/struq/references/svg-motion.md +390 -390
- package/skills/struq/references/typography.md +39 -39
- package/skills/struq/references/vibe-presets.md +5 -5
- package/skills/struq/references/webgl-scenes.md +593 -593
- package/skills/struq/scripts/canon.mjs +309 -0
- package/skills/struq/scripts/check.mjs +547 -0
- package/skills/struq/scripts/font-names.mjs +40 -0
- package/skills/struq/scripts/images.mjs +360 -0
- package/skills/struq/scripts/log.mjs +92 -0
- package/skills/struq/scripts/packs.mjs +156 -0
- package/skills/struq/scripts/recipes.mjs +135 -0
- /package/{bin → cli}/struq.mjs +0 -0
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Struq recipes: copy the tested cinematic components into a Next.js App Router project.
|
|
3
|
+
//
|
|
4
|
+
// node <skill-dir>/scripts/recipes.mjs run from the project root
|
|
5
|
+
// node <skill-dir>/scripts/recipes.mjs --dir ./my-site
|
|
6
|
+
// node <skill-dir>/scripts/recipes.mjs --force overwrite recipe files you already have
|
|
7
|
+
// node <skill-dir>/scripts/recipes.mjs --no-install skip the npm install of missing packages
|
|
8
|
+
//
|
|
9
|
+
// Copies lib/gsap.ts and components/struq/* (SmoothScroll, SplitHeadline, Reveal, MediaFrame,
|
|
10
|
+
// HorizontalGallery, PinnedZoom, HeroCanvas with three scenes, MagneticButton, Cursor).
|
|
11
|
+
// On a fresh scaffold it also replaces app/globals.css, app/layout.tsx and adds app/fonts.ts with
|
|
12
|
+
// the Struq versions, and removes the starter SVGs. A layout or stylesheet you already wrote is
|
|
13
|
+
// never overwritten: the script tells you what to merge instead.
|
|
14
|
+
// Then it installs whatever is missing of gsap, @gsap/react, lenis, three and @types/three.
|
|
15
|
+
import { spawnSync } from "node:child_process";
|
|
16
|
+
import { copyFileSync, existsSync, mkdirSync, readdirSync, readFileSync, rmSync, statSync } from "node:fs";
|
|
17
|
+
import { dirname, join, relative, resolve } from "node:path";
|
|
18
|
+
import { fileURLToPath } from "node:url";
|
|
19
|
+
import { appendLog } from "./log.mjs";
|
|
20
|
+
|
|
21
|
+
const here = dirname(fileURLToPath(import.meta.url));
|
|
22
|
+
const RECIPES = join(here, "..", "recipes");
|
|
23
|
+
const args = process.argv.slice(2);
|
|
24
|
+
const flagValue = (name) => {
|
|
25
|
+
const i = args.indexOf(name);
|
|
26
|
+
return i >= 0 ? args[i + 1] : undefined;
|
|
27
|
+
};
|
|
28
|
+
const root = resolve(flagValue("--dir") ?? process.cwd());
|
|
29
|
+
const force = args.includes("--force");
|
|
30
|
+
const install = !args.includes("--no-install");
|
|
31
|
+
|
|
32
|
+
const pkgPath = join(root, "package.json");
|
|
33
|
+
if (!existsSync(pkgPath)) {
|
|
34
|
+
console.error(`FAIL no package.json in ${root}. Scaffold first: npx create-next-app@latest . --ts --tailwind --app --eslint --yes`);
|
|
35
|
+
process.exit(2);
|
|
36
|
+
}
|
|
37
|
+
const pkg = JSON.parse(readFileSync(pkgPath, "utf8"));
|
|
38
|
+
const deps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
39
|
+
if (!deps.next) {
|
|
40
|
+
console.error("FAIL this is not a Next.js project. The recipes target Next.js App Router; for Vite read references/stack-targets.md.");
|
|
41
|
+
process.exit(2);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const src = existsSync(join(root, "src", "app")) ? "src" : "";
|
|
45
|
+
const appDir = join(root, src, "app");
|
|
46
|
+
const at = (...p) => join(root, src, ...p);
|
|
47
|
+
|
|
48
|
+
function walk(dir) {
|
|
49
|
+
return readdirSync(dir).flatMap((name) => {
|
|
50
|
+
const full = join(dir, name);
|
|
51
|
+
return statSync(full).isDirectory() ? walk(full) : [full];
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const copied = [];
|
|
56
|
+
const kept = [];
|
|
57
|
+
function place(from, to, { overwrite = force } = {}) {
|
|
58
|
+
if (existsSync(to) && !overwrite) {
|
|
59
|
+
kept.push(relative(root, to));
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
mkdirSync(dirname(to), { recursive: true });
|
|
63
|
+
copyFileSync(from, to);
|
|
64
|
+
copied.push(relative(root, to));
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// 1. The components and the GSAP registry: always ours.
|
|
68
|
+
place(join(RECIPES, "lib", "gsap.ts"), at("lib", "gsap.ts"));
|
|
69
|
+
for (const file of walk(join(RECIPES, "components", "struq"))) {
|
|
70
|
+
place(file, at("components", "struq", relative(join(RECIPES, "components", "struq"), file)));
|
|
71
|
+
}
|
|
72
|
+
// The cinematic showpieces (toonstuk-*), one file per showpiece, named by its stable slug.
|
|
73
|
+
const SHOWPIECES = join(here, "..", "cinematic", "showpieces");
|
|
74
|
+
for (const file of walk(SHOWPIECES).filter((f) => f.endsWith(".tsx"))) {
|
|
75
|
+
place(file, at("components", "struq", "showpieces", relative(SHOWPIECES, file)));
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// 2. The app shell: only over a starter file, never over work.
|
|
79
|
+
const notes = [];
|
|
80
|
+
const read = (p) => (existsSync(p) ? readFileSync(p, "utf8") : "");
|
|
81
|
+
const layoutPath = join(appDir, "layout.tsx");
|
|
82
|
+
const layout = read(layoutPath);
|
|
83
|
+
if (!layout || /Create Next App|Geist/.test(layout) || force) {
|
|
84
|
+
place(join(RECIPES, "app", "layout.tsx"), layoutPath, { overwrite: true });
|
|
85
|
+
} else if (!layout.includes("SmoothScroll")) {
|
|
86
|
+
notes.push("app/layout.tsx is your own: wrap {children} in <SmoothScroll>, add <Cursor />, the `js` class script and the font variables (see the recipe in recipes/app/layout.tsx).");
|
|
87
|
+
}
|
|
88
|
+
const cssPath = join(appDir, "globals.css");
|
|
89
|
+
const css = read(cssPath);
|
|
90
|
+
if (!css || (!css.includes("STRUQ TOKENS") && /--background|--foreground|Arial|prefers-color-scheme/.test(css)) || force) {
|
|
91
|
+
place(join(RECIPES, "app", "globals.css"), cssPath, { overwrite: true });
|
|
92
|
+
} else if (!css.includes("STRUQ TOKENS")) {
|
|
93
|
+
notes.push("app/globals.css is your own: merge the STRUQ TOKENS block, the @theme roles and the STRUQ MOTION GATE from recipes/app/globals.css.");
|
|
94
|
+
}
|
|
95
|
+
place(join(RECIPES, "app", "fonts.ts"), join(appDir, "fonts.ts"));
|
|
96
|
+
|
|
97
|
+
for (const starter of ["next.svg", "vercel.svg", "file.svg", "globe.svg", "window.svg"]) {
|
|
98
|
+
const p = join(root, "public", starter);
|
|
99
|
+
if (existsSync(p)) rmSync(p);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// 3. The alias every recipe imports through.
|
|
103
|
+
const tsconfig = read(join(root, "tsconfig.json"));
|
|
104
|
+
if (tsconfig && !tsconfig.includes('"@/*"')) {
|
|
105
|
+
notes.push('tsconfig.json has no "@/*" path alias: add "paths": { "@/*": ["./' + (src ? "src/" : "") + '*"] } under compilerOptions.');
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// 4. Packages.
|
|
109
|
+
const need = ["gsap", "@gsap/react", "lenis", "three"].filter((d) => !deps[d]);
|
|
110
|
+
const needDev = ["@types/three"].filter((d) => !deps[d]);
|
|
111
|
+
const run = (list, dev) => {
|
|
112
|
+
if (!list.length) return true;
|
|
113
|
+
const cmd = `npm install ${dev ? "-D " : ""}${list.join(" ")}`;
|
|
114
|
+
if (!install) {
|
|
115
|
+
notes.push(`Run: ${cmd}`);
|
|
116
|
+
return true;
|
|
117
|
+
}
|
|
118
|
+
console.log(`> ${cmd}`);
|
|
119
|
+
const r = spawnSync(cmd, { cwd: root, stdio: "inherit", shell: true });
|
|
120
|
+
if (r.status !== 0) notes.push(`The install failed. Run it yourself: ${cmd}`);
|
|
121
|
+
return r.status === 0;
|
|
122
|
+
};
|
|
123
|
+
run(need, false);
|
|
124
|
+
run(needDev, true);
|
|
125
|
+
|
|
126
|
+
console.log(`\nCopied ${copied.length} file(s):`);
|
|
127
|
+
for (const f of copied) console.log(` ${f}`);
|
|
128
|
+
if (kept.length) console.log(`Kept ${kept.length} existing file(s) (use --force to replace): ${kept.join(", ")}`);
|
|
129
|
+
for (const n of notes) console.log(`NOTE ${n}`);
|
|
130
|
+
console.log("\nNext: set the palette in globals.css (STRUQ TOKENS), the two faces in app/fonts.ts, the title in app/layout.tsx.");
|
|
131
|
+
appendLog(root, "Step 3 Recipes", [
|
|
132
|
+
`recipes.mjs copied ${copied.length} file(s), kept ${kept.length}.`,
|
|
133
|
+
...(notes.length ? [`Notes: ${notes.join(" ")}`] : []),
|
|
134
|
+
]);
|
|
135
|
+
console.log("OK recipes ready");
|
/package/{bin → cli}/struq.mjs
RENAMED
|
File without changes
|