@texturehq/edges 0.0.15 → 0.0.19
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 +71 -57
- package/dist/components.manifest.json +5 -0
- package/dist/index.cjs +2 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +534 -300
- package/dist/index.d.ts +534 -300
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/styles.css +709 -421
- package/dist/theme.css +88 -3
- package/package.json +15 -5
- package/scripts/generate-components-manifest.js +154 -0
- package/scripts/setup-cursor-rules-manual.js +167 -0
- package/scripts/setup-cursor-rules.js +113 -0
- package/scripts/setup-cursor-rules.sh +95 -0
- package/templates/cursor-rules/edges-usage.mdc +65 -0
package/dist/theme.css
CHANGED
|
@@ -8,7 +8,61 @@
|
|
|
8
8
|
}
|
|
9
9
|
}
|
|
10
10
|
|
|
11
|
-
@
|
|
11
|
+
@keyframes slide-in-from-right {
|
|
12
|
+
from {
|
|
13
|
+
transform: translateX(100%);
|
|
14
|
+
}
|
|
15
|
+
to {
|
|
16
|
+
transform: translateX(0);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
@keyframes slide-in-from-left {
|
|
21
|
+
from {
|
|
22
|
+
transform: translateX(-100%);
|
|
23
|
+
}
|
|
24
|
+
to {
|
|
25
|
+
transform: translateX(0);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
@keyframes slide-out-to-right {
|
|
30
|
+
from {
|
|
31
|
+
transform: translateX(0);
|
|
32
|
+
}
|
|
33
|
+
to {
|
|
34
|
+
transform: translateX(100%);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
@keyframes slide-out-to-left {
|
|
39
|
+
from {
|
|
40
|
+
transform: translateX(0);
|
|
41
|
+
}
|
|
42
|
+
to {
|
|
43
|
+
transform: translateX(-100%);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
@keyframes fade-in {
|
|
48
|
+
from {
|
|
49
|
+
opacity: 0;
|
|
50
|
+
}
|
|
51
|
+
to {
|
|
52
|
+
opacity: 1;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
@keyframes fade-out {
|
|
57
|
+
from {
|
|
58
|
+
opacity: 1;
|
|
59
|
+
}
|
|
60
|
+
to {
|
|
61
|
+
opacity: 0;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
@theme inline {
|
|
12
66
|
/* Base Font Families */
|
|
13
67
|
--font-sans: "Inter", system-ui, sans-serif;
|
|
14
68
|
--font-brand: "TT Firs Neue", "Helvetica Neue", "Arial", system-ui, sans-serif;
|
|
@@ -280,8 +334,8 @@
|
|
|
280
334
|
--color-stone-900: var(--stone-900);
|
|
281
335
|
--color-stone-950: var(--stone-950);
|
|
282
336
|
|
|
283
|
-
--color-black: var(--black);
|
|
284
|
-
--color-white: var(--white);
|
|
337
|
+
--color-black: var(--color-neutral-black);
|
|
338
|
+
--color-white: var(--color-neutral-white);
|
|
285
339
|
|
|
286
340
|
--spacing: 0.25rem;
|
|
287
341
|
|
|
@@ -407,6 +461,12 @@
|
|
|
407
461
|
--animate-ping: ping 1s cubic-bezier(0, 0, 0.2, 1) infinite;
|
|
408
462
|
--animate-pulse: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;
|
|
409
463
|
--animate-bounce: bounce 1s infinite;
|
|
464
|
+
--animate-slide-in-from-right: slide-in-from-right 0.3s ease-out;
|
|
465
|
+
--animate-slide-in-from-left: slide-in-from-left 0.3s ease-out;
|
|
466
|
+
--animate-slide-out-to-right: slide-out-to-right 0.3s ease-in;
|
|
467
|
+
--animate-slide-out-to-left: slide-out-to-left 0.3s ease-in;
|
|
468
|
+
--animate-fade-in: fade-in 0.2s ease-out;
|
|
469
|
+
--animate-fade-out: fade-out 0.15s ease-in;
|
|
410
470
|
|
|
411
471
|
@keyframes spin {
|
|
412
472
|
to {
|
|
@@ -1015,3 +1075,28 @@
|
|
|
1015
1075
|
--skeleton-highlight: #374151;
|
|
1016
1076
|
--skeleton-wave: rgba(255, 255, 255, 0.1);
|
|
1017
1077
|
}
|
|
1078
|
+
|
|
1079
|
+
/* Define the slide animation utilities */
|
|
1080
|
+
.slide-in-from-right {
|
|
1081
|
+
animation: slide-in-from-right 0.3s ease-out;
|
|
1082
|
+
}
|
|
1083
|
+
|
|
1084
|
+
.slide-in-from-left {
|
|
1085
|
+
animation: slide-in-from-left 0.3s ease-out;
|
|
1086
|
+
}
|
|
1087
|
+
|
|
1088
|
+
.slide-out-to-right {
|
|
1089
|
+
animation: slide-out-to-right 0.15s ease-in;
|
|
1090
|
+
}
|
|
1091
|
+
|
|
1092
|
+
.slide-out-to-left {
|
|
1093
|
+
animation: slide-out-to-left 0.15s ease-in;
|
|
1094
|
+
}
|
|
1095
|
+
|
|
1096
|
+
.fade-in {
|
|
1097
|
+
animation: fade-in 0.2s ease-out;
|
|
1098
|
+
}
|
|
1099
|
+
|
|
1100
|
+
.fade-out {
|
|
1101
|
+
animation: fade-out 0.15s ease-in;
|
|
1102
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@texturehq/edges",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.19",
|
|
4
4
|
"author": "Nicholas Brown <nick@texturehq.com>",
|
|
5
5
|
"description": "A shared component library for Texture",
|
|
6
6
|
"type": "module",
|
|
@@ -9,7 +9,9 @@
|
|
|
9
9
|
"types": "./dist/index.d.ts",
|
|
10
10
|
"sideEffects": false,
|
|
11
11
|
"files": [
|
|
12
|
-
"dist/**"
|
|
12
|
+
"dist/**",
|
|
13
|
+
"templates/**",
|
|
14
|
+
"scripts/**"
|
|
13
15
|
],
|
|
14
16
|
"exports": {
|
|
15
17
|
".": {
|
|
@@ -18,12 +20,17 @@
|
|
|
18
20
|
"default": "./dist/index.js"
|
|
19
21
|
},
|
|
20
22
|
"./styles.css": "./dist/styles.css",
|
|
21
|
-
"./
|
|
23
|
+
"./dist/styles.css": "./dist/styles.css",
|
|
24
|
+
"./theme.css": "./dist/theme.css",
|
|
25
|
+
"./dist/theme.css": "./dist/theme.css"
|
|
22
26
|
},
|
|
23
27
|
"scripts": {
|
|
24
28
|
"dev": "yarn watch",
|
|
25
29
|
"watch": "tsup --watch",
|
|
26
30
|
"build": "tsup",
|
|
31
|
+
"build:yalc": "tsup && npx yalc publish --sig && npx yalc push",
|
|
32
|
+
"dev:yalc": "tsup && npx yalc publish --sig && npx yalc push && echo 'Package updated! Run in target project: yarn install && yarn dev'",
|
|
33
|
+
"dev:yalc:force": "tsup && npx yalc publish --sig && npx yalc push && echo 'Package updated! Run in target project: rm -rf .vite && yarn cache clean && yarn dev --force'",
|
|
27
34
|
"clean": "rm -rf dist",
|
|
28
35
|
"lint": "eslint --ext .ts,.tsx src/",
|
|
29
36
|
"lint:quiet": "eslint --ext .ts,.tsx src/ --quiet",
|
|
@@ -33,7 +40,8 @@
|
|
|
33
40
|
"test:coverage": "vitest run --coverage",
|
|
34
41
|
"test:ui": "vitest --ui",
|
|
35
42
|
"storybook": "storybook dev -p 6010 --no-open",
|
|
36
|
-
"build-storybook": "storybook build"
|
|
43
|
+
"build-storybook": "storybook build",
|
|
44
|
+
"postinstall": "node scripts/setup-cursor-rules.js || echo \"! setup-cursor-rules: non-fatal error\""
|
|
37
45
|
},
|
|
38
46
|
"peerDependencies": {
|
|
39
47
|
"react": "^18.2.0",
|
|
@@ -47,6 +55,7 @@
|
|
|
47
55
|
"next-intl": "^4.0.2",
|
|
48
56
|
"react-ace": "^14.0.1",
|
|
49
57
|
"react-aria-components": "^1.7.1",
|
|
58
|
+
"react-stately": "^3.35.0",
|
|
50
59
|
"tailwind-merge": "^3.2.0"
|
|
51
60
|
},
|
|
52
61
|
"devDependencies": {
|
|
@@ -84,6 +93,7 @@
|
|
|
84
93
|
"typescript": "~5.4.2",
|
|
85
94
|
"vite": "^5.1.6",
|
|
86
95
|
"vite-plugin-svgr": "^4.3.0",
|
|
87
|
-
"vitest": "^3.1.1"
|
|
96
|
+
"vitest": "^3.1.1",
|
|
97
|
+
"yalc": "^1.0.0-pre.53"
|
|
88
98
|
}
|
|
89
99
|
}
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
// generate-components-manifest.js
|
|
2
|
+
// Build-time script that scans exported components and creates dist/components.manifest.json
|
|
3
|
+
|
|
4
|
+
import fs from "fs";
|
|
5
|
+
import path from "path";
|
|
6
|
+
|
|
7
|
+
const ROOT = process.cwd();
|
|
8
|
+
const SRC_DIR = path.join(ROOT, "src");
|
|
9
|
+
const DIST_DIR = path.join(ROOT, "dist");
|
|
10
|
+
|
|
11
|
+
function read(file) {
|
|
12
|
+
return fs.existsSync(file) ? fs.readFileSync(file, "utf8") : "";
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function ensureDir(dir) {
|
|
16
|
+
if (!fs.existsSync(dir)) fs.mkdirSync(dir);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function extractExportsFromIndex(indexContent) {
|
|
20
|
+
// Matches: export { Button } from "./components/Button";
|
|
21
|
+
const re = /export\s+\{\s*([A-Za-z0-9_,\s]+)\s*\}\s+from\s+"\.\/components\/([A-Za-z0-9_/.-]+)"/g;
|
|
22
|
+
const results = [];
|
|
23
|
+
let m;
|
|
24
|
+
while ((m = re.exec(indexContent))) {
|
|
25
|
+
const exported = m[1]
|
|
26
|
+
.split(",")
|
|
27
|
+
.map((s) => s.trim())
|
|
28
|
+
.filter(Boolean);
|
|
29
|
+
const rel = m[2];
|
|
30
|
+
exported.forEach((name) => {
|
|
31
|
+
// Only keep PascalCase (components); ignore sub-exports like Tab, TabList if not directory-named
|
|
32
|
+
results.push({ name, relPath: rel });
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
return results;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function findComponentFile(relPath) {
|
|
39
|
+
// Prefer ComponentName/ComponentName.tsx
|
|
40
|
+
const base = path.join(SRC_DIR, relPath);
|
|
41
|
+
const dir = path.dirname(base);
|
|
42
|
+
const compName = path.basename(base);
|
|
43
|
+
const candidates = [
|
|
44
|
+
path.join(dir, `${compName}.tsx`),
|
|
45
|
+
path.join(dir, `${compName}.ts`),
|
|
46
|
+
path.join(base, `${compName}.tsx`),
|
|
47
|
+
path.join(base, `index.tsx`),
|
|
48
|
+
path.join(base, `index.ts`),
|
|
49
|
+
];
|
|
50
|
+
for (const f of candidates) {
|
|
51
|
+
if (fs.existsSync(f)) return f;
|
|
52
|
+
}
|
|
53
|
+
return null;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function getJsdocAbove(content, idx) {
|
|
57
|
+
// Find the last JSDoc block before idx
|
|
58
|
+
const upTo = content.slice(0, idx);
|
|
59
|
+
const start = upTo.lastIndexOf("/**");
|
|
60
|
+
const end = upTo.lastIndexOf("*/");
|
|
61
|
+
if (start !== -1 && end !== -1 && end > start) {
|
|
62
|
+
const block = upTo.slice(start + 3, end).trim();
|
|
63
|
+
return block
|
|
64
|
+
.split("\n")
|
|
65
|
+
.map((l) => l.replace(/^\s*\*\s?/, "").trim())
|
|
66
|
+
.join(" ")
|
|
67
|
+
.trim();
|
|
68
|
+
}
|
|
69
|
+
return "";
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function extractProps(content, componentName) {
|
|
73
|
+
// Try `export interface <Name>Props` first
|
|
74
|
+
const ifaceRe = new RegExp(`export\\s+interface\\s+${componentName}Props\\s*(?:extends\\s+[^{]+)?\\{([\\s\\S]*?)\\}`, "m");
|
|
75
|
+
let m = ifaceRe.exec(content);
|
|
76
|
+
if (!m) {
|
|
77
|
+
// Try generic name: any exported *Props
|
|
78
|
+
const anyIfaceRe = /export\s+interface\s+([A-Za-z0-9_]+Props)\s*(?:extends\s+[^{]+)?\{([\s\S]*?)\}/m;
|
|
79
|
+
m = anyIfaceRe.exec(content);
|
|
80
|
+
}
|
|
81
|
+
if (!m) {
|
|
82
|
+
// Try type alias
|
|
83
|
+
const typeRe = new RegExp(`export\\s+type\\s+${componentName}Props\\s*=\\s*(?:[^{&]+&\s*)?([\\s\\S]*?)\\}`, "m");
|
|
84
|
+
m = typeRe.exec(content);
|
|
85
|
+
}
|
|
86
|
+
const props = [];
|
|
87
|
+
if (m) {
|
|
88
|
+
const body = m[2] || m[1] || "";
|
|
89
|
+
const lines = body
|
|
90
|
+
.split("\n")
|
|
91
|
+
.map((l) => l.trim())
|
|
92
|
+
.filter((l) => !!l && !l.startsWith("//"));
|
|
93
|
+
for (const line of lines) {
|
|
94
|
+
// Match: name?: Type; or name: Type;
|
|
95
|
+
const pm = /^(readonly\s+)?([A-Za-z0-9_]+)\??:\s*([^;]+);?/.exec(line);
|
|
96
|
+
if (pm) {
|
|
97
|
+
const name = pm[2];
|
|
98
|
+
const type = pm[3].trim();
|
|
99
|
+
props.push({ name, type });
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
return props;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function run() {
|
|
107
|
+
try {
|
|
108
|
+
const indexPath = path.join(SRC_DIR, "index.ts");
|
|
109
|
+
const indexContent = read(indexPath);
|
|
110
|
+
const exports = extractExportsFromIndex(indexContent);
|
|
111
|
+
|
|
112
|
+
const components = [];
|
|
113
|
+
for (const { name, relPath } of exports) {
|
|
114
|
+
// Only treat PascalCase names as components
|
|
115
|
+
if (!/^[A-Z]/.test(name)) continue;
|
|
116
|
+
|
|
117
|
+
const componentFile = findComponentFile(relPath);
|
|
118
|
+
if (!componentFile) continue;
|
|
119
|
+
const content = read(componentFile);
|
|
120
|
+
|
|
121
|
+
// Description: JSDoc above `export function Name` or above `export interface NameProps`
|
|
122
|
+
let idx = content.indexOf(`export function ${name}`);
|
|
123
|
+
if (idx === -1) idx = content.search(new RegExp(`export\\s+(const|class)\\s+${name}\b`));
|
|
124
|
+
if (idx === -1) idx = content.search(new RegExp(`export\\s+(interface|type)\\s+${name}Props\b`));
|
|
125
|
+
const description = idx !== -1 ? getJsdocAbove(content, idx) : "";
|
|
126
|
+
|
|
127
|
+
const props = extractProps(content, name);
|
|
128
|
+
|
|
129
|
+
components.push({
|
|
130
|
+
name,
|
|
131
|
+
importRoot: "@texturehq/edges",
|
|
132
|
+
importPath: `@texturehq/edges/components/${name}`,
|
|
133
|
+
description,
|
|
134
|
+
props,
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
ensureDir(DIST_DIR);
|
|
139
|
+
const manifest = {
|
|
140
|
+
version: process.env.npm_package_version || "unknown",
|
|
141
|
+
generatedAt: new Date().toISOString(),
|
|
142
|
+
components: components.sort((a, b) => a.name.localeCompare(b.name)),
|
|
143
|
+
};
|
|
144
|
+
fs.writeFileSync(path.join(DIST_DIR, "components.manifest.json"), JSON.stringify(manifest, null, 2));
|
|
145
|
+
console.log(`Generated ${path.join("dist", "components.manifest.json")} with ${components.length} components.`);
|
|
146
|
+
} catch (err) {
|
|
147
|
+
console.error("Failed to generate components manifest:", err);
|
|
148
|
+
process.exitCode = 1;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
run();
|
|
153
|
+
|
|
154
|
+
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// setup-cursor-rules-manual.js (manual installer for Cursor rules)
|
|
3
|
+
const fs = require("fs");
|
|
4
|
+
const path = require("path");
|
|
5
|
+
|
|
6
|
+
const setupCursorRules = () => {
|
|
7
|
+
const cwd = process.cwd();
|
|
8
|
+
|
|
9
|
+
// Don't run inside the edges package itself
|
|
10
|
+
try {
|
|
11
|
+
const pkgJsonPath = path.join(cwd, "package.json");
|
|
12
|
+
if (fs.existsSync(pkgJsonPath)) {
|
|
13
|
+
const pkg = JSON.parse(fs.readFileSync(pkgJsonPath, "utf8"));
|
|
14
|
+
if (pkg.name === "@texturehq/edges") return;
|
|
15
|
+
}
|
|
16
|
+
} catch {
|
|
17
|
+
// ignore
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// Try multiple possible locations for the templates directory
|
|
21
|
+
const possibleTemplatePaths = [
|
|
22
|
+
// Yalc path (the actual files)
|
|
23
|
+
path.join(cwd, 'node_modules/.yalc/@texturehq/edges/templates/cursor-rules'),
|
|
24
|
+
// Regular npm install path
|
|
25
|
+
path.join(__dirname, '../templates/cursor-rules'),
|
|
26
|
+
// Alternative paths
|
|
27
|
+
path.join(__dirname, '../../templates/cursor-rules'),
|
|
28
|
+
path.join(__dirname, '../../../templates/cursor-rules'),
|
|
29
|
+
// Direct path from node_modules
|
|
30
|
+
path.join(cwd, 'node_modules/@texturehq/edges/templates/cursor-rules'),
|
|
31
|
+
];
|
|
32
|
+
|
|
33
|
+
let templatesDir = null;
|
|
34
|
+
for (const templatePath of possibleTemplatePaths) {
|
|
35
|
+
if (fs.existsSync(templatePath)) {
|
|
36
|
+
templatesDir = templatePath;
|
|
37
|
+
break;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if (!templatesDir) {
|
|
42
|
+
console.log('⚠️ Could not find cursor rules templates. Tried paths:');
|
|
43
|
+
possibleTemplatePaths.forEach(path => console.log(` - ${path}`));
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
console.log(`✅ Found templates at: ${templatesDir}`);
|
|
48
|
+
|
|
49
|
+
// Try to read package version from multiple locations
|
|
50
|
+
const possiblePackageJsonPaths = [
|
|
51
|
+
// Yalc path (the actual files)
|
|
52
|
+
path.join(cwd, 'node_modules/.yalc/@texturehq/edges/package.json'),
|
|
53
|
+
// Regular npm install path
|
|
54
|
+
path.join(__dirname, '../package.json'),
|
|
55
|
+
path.join(__dirname, '../../package.json'),
|
|
56
|
+
path.join(__dirname, '../../../package.json'),
|
|
57
|
+
path.join(cwd, 'node_modules/@texturehq/edges/package.json'),
|
|
58
|
+
];
|
|
59
|
+
|
|
60
|
+
let packageVersion = 'unknown';
|
|
61
|
+
for (const packageJsonPath of possiblePackageJsonPaths) {
|
|
62
|
+
if (fs.existsSync(packageJsonPath)) {
|
|
63
|
+
try {
|
|
64
|
+
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
|
|
65
|
+
packageVersion = packageJson.version;
|
|
66
|
+
console.log(`📦 Found package version: ${packageVersion}`);
|
|
67
|
+
break;
|
|
68
|
+
} catch (error) {
|
|
69
|
+
console.log(`⚠️ Could not read package.json at: ${packageJsonPath}`);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const templateFiles = fs.readdirSync(templatesDir).filter(file => file.endsWith('.mdc'));
|
|
75
|
+
|
|
76
|
+
if (templateFiles.length === 0) {
|
|
77
|
+
console.log('⚠️ No .mdc files found in templates directory');
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// Always use package-level rules for version-specific behavior
|
|
82
|
+
const cursorDir = path.join(cwd, '.cursor');
|
|
83
|
+
const rulesDir = path.join(cursorDir, 'rules');
|
|
84
|
+
|
|
85
|
+
// Create directories recursively
|
|
86
|
+
fs.mkdirSync(cursorDir, { recursive: true });
|
|
87
|
+
fs.mkdirSync(rulesDir, { recursive: true });
|
|
88
|
+
|
|
89
|
+
let copiedCount = 0;
|
|
90
|
+
|
|
91
|
+
templateFiles.forEach(file => {
|
|
92
|
+
const templatePath = path.join(templatesDir, file);
|
|
93
|
+
|
|
94
|
+
// Read template content and inject version info
|
|
95
|
+
let templateContent = fs.readFileSync(templatePath, 'utf8');
|
|
96
|
+
|
|
97
|
+
// Add version information to the rule
|
|
98
|
+
const versionHeader = `\n\n<!-- @texturehq/edges version: ${packageVersion} -->\n`;
|
|
99
|
+
templateContent = templateContent + versionHeader;
|
|
100
|
+
|
|
101
|
+
const targetPath = path.join(rulesDir, file);
|
|
102
|
+
|
|
103
|
+
// Backup existing rule before overwriting
|
|
104
|
+
if (fs.existsSync(targetPath)) {
|
|
105
|
+
const backupsDir = path.join(rulesDir, "_backups");
|
|
106
|
+
fs.mkdirSync(backupsDir, { recursive: true });
|
|
107
|
+
const ts = new Date().toISOString().replace(/[:.]/g, "-");
|
|
108
|
+
fs.copyFileSync(targetPath, path.join(backupsDir, `${file}.${ts}.bak`));
|
|
109
|
+
}
|
|
110
|
+
// Always overwrite to ensure version-specific rules
|
|
111
|
+
fs.writeFileSync(targetPath, templateContent, "utf8");
|
|
112
|
+
copiedCount++;
|
|
113
|
+
console.log(`✅ Updated .cursor/rules/${file} for @texturehq/edges v${packageVersion}`);
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
// Additionally, try to render a components list rule from the manifest if available
|
|
117
|
+
const possibleManifestPaths = [
|
|
118
|
+
path.join(cwd, 'node_modules/.yalc/@texturehq/edges/dist/components.manifest.json'),
|
|
119
|
+
path.join(cwd, 'node_modules/@texturehq/edges/dist/components.manifest.json')
|
|
120
|
+
];
|
|
121
|
+
let manifestPath = null;
|
|
122
|
+
for (const p of possibleManifestPaths) {
|
|
123
|
+
if (fs.existsSync(p)) { manifestPath = p; break; }
|
|
124
|
+
}
|
|
125
|
+
if (manifestPath) {
|
|
126
|
+
try {
|
|
127
|
+
const manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf8'));
|
|
128
|
+
const lines = [
|
|
129
|
+
'---',
|
|
130
|
+
'alwaysApply: true',
|
|
131
|
+
'---',
|
|
132
|
+
'',
|
|
133
|
+
`## @texturehq/edges Components (v${manifest.version || 'unknown'})`,
|
|
134
|
+
''
|
|
135
|
+
];
|
|
136
|
+
for (const c of manifest.components || []) {
|
|
137
|
+
lines.push(`- ${c.name}`);
|
|
138
|
+
if (c.importRoot) lines.push(` - Import: \`import { ${c.name} } from "${c.importRoot}"\``);
|
|
139
|
+
if (c.importPath) lines.push(` - Subpath: \`import { ${c.name} } from "${c.importPath}"\``);
|
|
140
|
+
if (c.props && c.props.length) {
|
|
141
|
+
const propNames = c.props.slice(0, 8).map(p => p.name).join(', ');
|
|
142
|
+
lines.push(` - Props: ${propNames}${c.props.length > 8 ? ', …' : ''}`);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
const outPath = path.join(rulesDir, 'edges-components.mdc');
|
|
146
|
+
// Backup existing components manifest before overwriting
|
|
147
|
+
if (fs.existsSync(outPath)) {
|
|
148
|
+
const backupsDir = path.join(rulesDir, "_backups");
|
|
149
|
+
fs.mkdirSync(backupsDir, { recursive: true });
|
|
150
|
+
const ts = new Date().toISOString().replace(/[:.]/g, "-");
|
|
151
|
+
fs.copyFileSync(outPath, path.join(backupsDir, `edges-components.mdc.${ts}.bak`));
|
|
152
|
+
}
|
|
153
|
+
fs.writeFileSync(outPath, lines.join('\n'), "utf8");
|
|
154
|
+
console.log(`✅ Wrote .cursor/rules/edges-components.mdc from manifest (${manifest.components?.length || 0} components)`);
|
|
155
|
+
} catch (err) {
|
|
156
|
+
console.log('⚠️ Failed to read components.manifest.json:', err.message);
|
|
157
|
+
}
|
|
158
|
+
} else {
|
|
159
|
+
console.log('ℹ️ No components.manifest.json found; skipping edges-components.mdc generation');
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
if (copiedCount > 0) {
|
|
163
|
+
console.log(`🎨 Updated ${copiedCount} cursor rule(s) for @texturehq/edges v${packageVersion}`);
|
|
164
|
+
}
|
|
165
|
+
};
|
|
166
|
+
|
|
167
|
+
setupCursorRules();
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
// scripts/setup-cursor-rules.js
|
|
2
|
+
import fs from 'fs';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
|
|
5
|
+
const setupCursorRules = () => {
|
|
6
|
+
const cwd = process.env.INIT_CWD || process.cwd();
|
|
7
|
+
|
|
8
|
+
// Don't run inside the edges package itself
|
|
9
|
+
try {
|
|
10
|
+
const pkgJsonPath = path.join(cwd, "package.json");
|
|
11
|
+
if (fs.existsSync(pkgJsonPath)) {
|
|
12
|
+
const pkg = JSON.parse(fs.readFileSync(pkgJsonPath, "utf8"));
|
|
13
|
+
if (pkg.name === "@texturehq/edges") return;
|
|
14
|
+
}
|
|
15
|
+
} catch {
|
|
16
|
+
// ignore
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const templatesDir = path.join(path.dirname(new URL(import.meta.url).pathname), '../templates/cursor-rules');
|
|
20
|
+
const cursorDir = path.join(cwd, '.cursor');
|
|
21
|
+
const rulesDir = path.join(cursorDir, 'rules');
|
|
22
|
+
|
|
23
|
+
// Create directories recursively
|
|
24
|
+
fs.mkdirSync(cursorDir, { recursive: true });
|
|
25
|
+
fs.mkdirSync(rulesDir, { recursive: true });
|
|
26
|
+
|
|
27
|
+
// Check if templates directory exists
|
|
28
|
+
if (!fs.existsSync(templatesDir)) {
|
|
29
|
+
console.log('⚠️ No cursor rules templates found');
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Copy all .mdc files from templates to the project's cursor rules
|
|
34
|
+
const templateFiles = fs.readdirSync(templatesDir).filter(file => file.endsWith('.mdc'));
|
|
35
|
+
|
|
36
|
+
if (templateFiles.length === 0) {
|
|
37
|
+
console.log('⚠️ No .mdc files found in templates directory');
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
let copiedCount = 0;
|
|
42
|
+
|
|
43
|
+
templateFiles.forEach(file => {
|
|
44
|
+
const templatePath = path.join(templatesDir, file);
|
|
45
|
+
const targetPath = path.join(rulesDir, file);
|
|
46
|
+
|
|
47
|
+
// Backup existing rule before overwriting
|
|
48
|
+
if (fs.existsSync(targetPath)) {
|
|
49
|
+
const backupsDir = path.join(rulesDir, "_backups");
|
|
50
|
+
fs.mkdirSync(backupsDir, { recursive: true });
|
|
51
|
+
const ts = new Date().toISOString().replace(/[:.]/g, "-");
|
|
52
|
+
fs.copyFileSync(targetPath, path.join(backupsDir, `${file}.${ts}.bak`));
|
|
53
|
+
}
|
|
54
|
+
// Always copy to ensure latest version
|
|
55
|
+
fs.copyFileSync(templatePath, targetPath);
|
|
56
|
+
copiedCount++;
|
|
57
|
+
console.log(`✅ Updated .cursor/rules/${file}`);
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
if (copiedCount > 0) {
|
|
61
|
+
console.log(`🎨 Added ${copiedCount} cursor rule(s) for @texturehq/edges design system`);
|
|
62
|
+
} else {
|
|
63
|
+
console.log('ℹ️ All cursor rules already exist');
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Also render edges-components.mdc from manifest when present
|
|
67
|
+
const manifestPath = [
|
|
68
|
+
path.join(cwd, 'node_modules/@texturehq/edges/dist/components.manifest.json')
|
|
69
|
+
].find(p => fs.existsSync(p));
|
|
70
|
+
|
|
71
|
+
if (manifestPath) {
|
|
72
|
+
try {
|
|
73
|
+
const manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf8'));
|
|
74
|
+
const lines = [
|
|
75
|
+
'---',
|
|
76
|
+
'alwaysApply: true',
|
|
77
|
+
'---',
|
|
78
|
+
'',
|
|
79
|
+
`## @texturehq/edges Components (v${manifest.version || 'unknown'})`,
|
|
80
|
+
''
|
|
81
|
+
];
|
|
82
|
+
for (const c of manifest.components || []) {
|
|
83
|
+
lines.push(`- ${c.name}`);
|
|
84
|
+
if (c.importRoot) lines.push(` - Import: \`import { ${c.name} } from "${c.importRoot}"\``);
|
|
85
|
+
if (c.importPath) lines.push(` - Subpath: \`import { ${c.name} } from "${c.importPath}"\``);
|
|
86
|
+
if (c.props && c.props.length) {
|
|
87
|
+
const propNames = c.props.slice(0, 8).map(p => p.name).join(', ');
|
|
88
|
+
lines.push(` - Props: ${propNames}${c.props.length > 8 ? ', …' : ''}`);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
const outPath = path.join(rulesDir, 'edges-components.mdc');
|
|
92
|
+
// Backup existing components manifest before overwriting
|
|
93
|
+
if (fs.existsSync(outPath)) {
|
|
94
|
+
const backupsDir = path.join(rulesDir, "_backups");
|
|
95
|
+
fs.mkdirSync(backupsDir, { recursive: true });
|
|
96
|
+
const ts = new Date().toISOString().replace(/[:.]/g, "-");
|
|
97
|
+
fs.copyFileSync(outPath, path.join(backupsDir, `edges-components.mdc.${ts}.bak`));
|
|
98
|
+
}
|
|
99
|
+
fs.writeFileSync(outPath, lines.join('\n'), "utf8");
|
|
100
|
+
console.log(`✅ Wrote .cursor/rules/edges-components.mdc from manifest (${manifest.components?.length || 0} components)`);
|
|
101
|
+
} catch (err) {
|
|
102
|
+
console.log('⚠️ Failed to read components.manifest.json:', err.message);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
try {
|
|
108
|
+
setupCursorRules();
|
|
109
|
+
} catch (err) {
|
|
110
|
+
console.warn("setup-cursor-rules error:", err);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
|