@texturehq/edges 0.0.18 → 0.0.20

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/dist/styles.css CHANGED
@@ -7,11 +7,13 @@
7
7
  --text-sm: 0.875rem;
8
8
  --text-base: 1rem;
9
9
  --text-lg: 1.125rem;
10
+ --radius-lg: 0.5rem;
10
11
  --ease-in-out: cubic-bezier(0.4, 0, 0.2, 1);
11
12
  --default-transition-duration: 150ms;
12
13
  --default-transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
13
14
  --default-font-family: "Inter", system-ui, sans-serif;
14
15
  --default-mono-font-family: "Fira Code", monospace;
16
+ --color-brand-primary: #444ae1;
15
17
  --color-neutral-black: #333333;
16
18
  --color-neutral-white: #ffffff;
17
19
  --color-text-body: #333333;
@@ -373,6 +375,9 @@
373
375
  .mb-4 {
374
376
  margin-bottom: calc(0.25rem * 4);
375
377
  }
378
+ .ml-auto {
379
+ margin-left: auto;
380
+ }
376
381
  .box-border {
377
382
  box-sizing: border-box;
378
383
  }
@@ -484,6 +489,9 @@
484
489
  .min-h-\[100px\] {
485
490
  min-height: 100px;
486
491
  }
492
+ .w-2 {
493
+ width: calc(0.25rem * 2);
494
+ }
487
495
  .w-3 {
488
496
  width: calc(0.25rem * 3);
489
497
  }
@@ -823,6 +831,9 @@
823
831
  .rounded {
824
832
  border-radius: 0.25rem;
825
833
  }
834
+ .rounded-\[0\.5rem\] {
835
+ border-radius: 0.5rem;
836
+ }
826
837
  .rounded-\[14px\] {
827
838
  border-radius: 14px;
828
839
  }
@@ -929,6 +940,9 @@
929
940
  .border-border-muted\/50 {
930
941
  border-color: color-mix(in oklab, #e5e7eb 50%, transparent);
931
942
  }
943
+ .border-brand-primary {
944
+ border-color: #444ae1;
945
+ }
932
946
  .border-current {
933
947
  border-color: currentcolor;
934
948
  }
@@ -956,6 +970,9 @@
956
970
  .border-slate-800 {
957
971
  border-color: var(--slate-800);
958
972
  }
973
+ .border-text-body {
974
+ border-color: #333333;
975
+ }
959
976
  .border-transparent {
960
977
  border-color: transparent;
961
978
  }
@@ -971,6 +988,9 @@
971
988
  .\!bg-neutral-black {
972
989
  background-color: #333333 !important;
973
990
  }
991
+ .bg-\[\#444ae1\] {
992
+ background-color: #444ae1;
993
+ }
974
994
  .bg-\[var\(--skeleton-base\)\] {
975
995
  background-color: var(--skeleton-base);
976
996
  }
@@ -1061,18 +1081,12 @@
1061
1081
  .bg-orange-50 {
1062
1082
  background-color: oklch(0.98 0.016 73.684);
1063
1083
  }
1064
- .bg-pink-400 {
1065
- background-color: var(--pink-400);
1066
- }
1067
1084
  .bg-purple-50 {
1068
1085
  background-color: oklch(0.977 0.014 308.299);
1069
1086
  }
1070
1087
  .bg-red-50 {
1071
1088
  background-color: oklch(0.971 0.013 17.38);
1072
1089
  }
1073
- .bg-red-500 {
1074
- background-color: oklch(0.637 0.237 25.331);
1075
- }
1076
1090
  .bg-red-600 {
1077
1091
  background-color: oklch(0.577 0.245 27.325);
1078
1092
  }
@@ -1097,9 +1111,6 @@
1097
1111
  .bg-yellow-50 {
1098
1112
  background-color: oklch(0.987 0.026 102.212);
1099
1113
  }
1100
- .bg-yellow-400 {
1101
- background-color: oklch(0.852 0.199 91.936);
1102
- }
1103
1114
  .bg-gradient-to-r {
1104
1115
  --tw-gradient-position: to right in oklab;
1105
1116
  background-image: linear-gradient(var(--tw-gradient-stops));
@@ -1174,6 +1185,9 @@
1174
1185
  .p-10 {
1175
1186
  padding: calc(0.25rem * 10);
1176
1187
  }
1188
+ .p-\[1rem\] {
1189
+ padding: 1rem;
1190
+ }
1177
1191
  .\!px-2 {
1178
1192
  padding-inline: calc(0.25rem * 2) !important;
1179
1193
  }
@@ -1267,12 +1281,21 @@
1267
1281
  .pl-3 {
1268
1282
  padding-left: calc(0.25rem * 3);
1269
1283
  }
1284
+ .pl-7 {
1285
+ padding-left: calc(0.25rem * 7);
1286
+ }
1270
1287
  .pl-8 {
1271
1288
  padding-left: calc(0.25rem * 8);
1272
1289
  }
1290
+ .pl-9 {
1291
+ padding-left: calc(0.25rem * 9);
1292
+ }
1273
1293
  .pl-10 {
1274
1294
  padding-left: calc(0.25rem * 10);
1275
1295
  }
1296
+ .pl-12 {
1297
+ padding-left: calc(0.25rem * 12);
1298
+ }
1276
1299
  .text-center {
1277
1300
  text-align: center;
1278
1301
  }
@@ -1320,6 +1343,12 @@
1320
1343
  font-size: 0.75rem;
1321
1344
  line-height: var(--tw-leading, calc(1 / 0.75));
1322
1345
  }
1346
+ .text-\[1\.125rem\] {
1347
+ font-size: 1.125rem;
1348
+ }
1349
+ .text-\[1rem\] {
1350
+ font-size: 1rem;
1351
+ }
1323
1352
  .text-\[length\:var\(--control-text-lg\)\] {
1324
1353
  font-size: var(--control-text-lg);
1325
1354
  }
@@ -1344,6 +1373,10 @@
1344
1373
  --tw-leading: 1.625;
1345
1374
  line-height: 1.625;
1346
1375
  }
1376
+ .font-\[500\] {
1377
+ --tw-font-weight: 500;
1378
+ font-weight: 500;
1379
+ }
1347
1380
  .font-bold {
1348
1381
  --tw-font-weight: 700;
1349
1382
  font-weight: 700;
@@ -1381,6 +1414,15 @@
1381
1414
  .\!text-text-caption {
1382
1415
  color: #6b7280 !important;
1383
1416
  }
1417
+ .text-\[\#111827\] {
1418
+ color: #111827;
1419
+ }
1420
+ .text-\[\#333333\] {
1421
+ color: #333333;
1422
+ }
1423
+ .text-\[\#ffffff\] {
1424
+ color: #ffffff;
1425
+ }
1384
1426
  .text-action-destructive {
1385
1427
  color: #b91c1c;
1386
1428
  }
@@ -1420,9 +1462,6 @@
1420
1462
  .text-gray-800 {
1421
1463
  color: var(--gray-800);
1422
1464
  }
1423
- .text-green-500 {
1424
- color: oklch(0.723 0.219 149.579);
1425
- }
1426
1465
  .text-green-600 {
1427
1466
  color: oklch(0.627 0.194 149.214);
1428
1467
  }
@@ -1509,6 +1548,9 @@
1509
1548
  .caret-transparent {
1510
1549
  caret-color: transparent;
1511
1550
  }
1551
+ .opacity-0 {
1552
+ opacity: 0%;
1553
+ }
1512
1554
  .opacity-50 {
1513
1555
  opacity: 50%;
1514
1556
  }
@@ -1534,6 +1576,14 @@
1534
1576
  --tw-shadow: inset 0 2px 4px 0 var(--tw-shadow-color, rgb(0 0 0 / 0.05));
1535
1577
  box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
1536
1578
  }
1579
+ .shadow-lg {
1580
+ --tw-shadow: 0 10px 15px -3px var(--tw-shadow-color, rgb(0 0 0 / 0.1)), 0 4px 6px -4px var(--tw-shadow-color, rgb(0 0 0 / 0.1));
1581
+ box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
1582
+ }
1583
+ .shadow-md {
1584
+ --tw-shadow: 0 4px 6px -1px var(--tw-shadow-color, rgb(0 0 0 / 0.1)), 0 2px 4px -2px var(--tw-shadow-color, rgb(0 0 0 / 0.1));
1585
+ box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
1586
+ }
1537
1587
  .shadow-none {
1538
1588
  --tw-shadow: 0 0 #0000;
1539
1589
  box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
@@ -1578,9 +1628,6 @@
1578
1628
  outline-style: var(--tw-outline-style);
1579
1629
  outline-width: 1.5px;
1580
1630
  }
1581
- .-outline-offset-1 {
1582
- outline-offset: calc(1px * -1);
1583
- }
1584
1631
  .-outline-offset-2 {
1585
1632
  outline-offset: calc(2px * -1);
1586
1633
  }
@@ -1605,9 +1652,6 @@
1605
1652
  .outline-feedback-error-border {
1606
1653
  outline-color: #ef4444;
1607
1654
  }
1608
- .outline-transparent {
1609
- outline-color: transparent;
1610
- }
1611
1655
  .outline-white {
1612
1656
  outline-color: var(--color-neutral-white);
1613
1657
  }
@@ -1748,6 +1792,14 @@
1748
1792
  color: #9ca3af;
1749
1793
  }
1750
1794
  }
1795
+ .placeholder\:text-text-placeholder {
1796
+ &::-moz-placeholder {
1797
+ color: #9ca3af;
1798
+ }
1799
+ &::placeholder {
1800
+ color: #9ca3af;
1801
+ }
1802
+ }
1751
1803
  .before\:absolute {
1752
1804
  &::before {
1753
1805
  content: var(--tw-content);
@@ -2118,6 +2170,11 @@
2118
2170
  background-color: #f3f4f6;
2119
2171
  }
2120
2172
  }
2173
+ .data-\[focused\]\:opacity-100 {
2174
+ &[data-focused] {
2175
+ opacity: 100%;
2176
+ }
2177
+ }
2121
2178
  .data-\[placeholder\]\:text-text-placeholder {
2122
2179
  &[data-placeholder] {
2123
2180
  color: #9ca3af;
@@ -2128,6 +2185,11 @@
2128
2185
  background-color: #f3f4f6;
2129
2186
  }
2130
2187
  }
2188
+ .data-\[selected\]\:opacity-100 {
2189
+ &[data-selected] {
2190
+ opacity: 100%;
2191
+ }
2192
+ }
2131
2193
  .supports-\[-moz-appearance\:none\]\:bg-gray-100 {
2132
2194
  @supports (-moz-appearance:none) {
2133
2195
  background-color: var(--gray-100);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@texturehq/edges",
3
- "version": "0.0.18",
3
+ "version": "0.0.20",
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
  ".": {
@@ -38,7 +40,8 @@
38
40
  "test:coverage": "vitest run --coverage",
39
41
  "test:ui": "vitest --ui",
40
42
  "storybook": "storybook dev -p 6010 --no-open",
41
- "build-storybook": "storybook build"
43
+ "build-storybook": "storybook build",
44
+ "postinstall": "node scripts/setup-cursor-rules.js || echo \"! setup-cursor-rules: non-fatal error\""
42
45
  },
43
46
  "peerDependencies": {
44
47
  "react": "^18.2.0",
@@ -52,6 +55,7 @@
52
55
  "next-intl": "^4.0.2",
53
56
  "react-ace": "^14.0.1",
54
57
  "react-aria-components": "^1.7.1",
58
+ "react-stately": "^3.35.0",
55
59
  "tailwind-merge": "^3.2.0"
56
60
  },
57
61
  "devDependencies": {
@@ -0,0 +1,151 @@
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
+ // Components are in directories like Button/Button.tsx or Button/index.ts
40
+ const base = path.join(SRC_DIR, "components", relPath);
41
+ const candidates = [
42
+ path.join(base, `${relPath}.tsx`),
43
+ path.join(base, `${relPath}.ts`),
44
+ path.join(base, `index.tsx`),
45
+ path.join(base, `index.ts`),
46
+ ];
47
+ for (const f of candidates) {
48
+ if (fs.existsSync(f)) return f;
49
+ }
50
+ return null;
51
+ }
52
+
53
+ function getJsdocAbove(content, idx) {
54
+ // Find the last JSDoc block before idx
55
+ const upTo = content.slice(0, idx);
56
+ const start = upTo.lastIndexOf("/**");
57
+ const end = upTo.lastIndexOf("*/");
58
+ if (start !== -1 && end !== -1 && end > start) {
59
+ const block = upTo.slice(start + 3, end).trim();
60
+ return block
61
+ .split("\n")
62
+ .map((l) => l.replace(/^\s*\*\s?/, "").trim())
63
+ .join(" ")
64
+ .trim();
65
+ }
66
+ return "";
67
+ }
68
+
69
+ function extractProps(content, componentName) {
70
+ // Try `export interface <Name>Props` first
71
+ const ifaceRe = new RegExp(`export\\s+interface\\s+${componentName}Props\\s*(?:extends\\s+[^{]+)?\\{([\\s\\S]*?)\\}`, "m");
72
+ let m = ifaceRe.exec(content);
73
+ if (!m) {
74
+ // Try generic name: any exported *Props
75
+ const anyIfaceRe = /export\s+interface\s+([A-Za-z0-9_]+Props)\s*(?:extends\s+[^{]+)?\{([\s\S]*?)\}/m;
76
+ m = anyIfaceRe.exec(content);
77
+ }
78
+ if (!m) {
79
+ // Try type alias
80
+ const typeRe = new RegExp(`export\\s+type\\s+${componentName}Props\\s*=\\s*(?:[^{&]+&\s*)?([\\s\\S]*?)\\}`, "m");
81
+ m = typeRe.exec(content);
82
+ }
83
+ const props = [];
84
+ if (m) {
85
+ const body = m[2] || m[1] || "";
86
+ const lines = body
87
+ .split("\n")
88
+ .map((l) => l.trim())
89
+ .filter((l) => !!l && !l.startsWith("//"));
90
+ for (const line of lines) {
91
+ // Match: name?: Type; or name: Type;
92
+ const pm = /^(readonly\s+)?([A-Za-z0-9_]+)\??:\s*([^;]+);?/.exec(line);
93
+ if (pm) {
94
+ const name = pm[2];
95
+ const type = pm[3].trim();
96
+ props.push({ name, type });
97
+ }
98
+ }
99
+ }
100
+ return props;
101
+ }
102
+
103
+ function run() {
104
+ try {
105
+ const indexPath = path.join(SRC_DIR, "index.ts");
106
+ const indexContent = read(indexPath);
107
+ const exports = extractExportsFromIndex(indexContent);
108
+
109
+ const components = [];
110
+ for (const { name, relPath } of exports) {
111
+ // Only treat PascalCase names as components
112
+ if (!/^[A-Z]/.test(name)) continue;
113
+
114
+ const componentFile = findComponentFile(relPath);
115
+ if (!componentFile) continue;
116
+ const content = read(componentFile);
117
+
118
+ // Description: JSDoc above `export function Name` or above `export interface NameProps`
119
+ let idx = content.indexOf(`export function ${name}`);
120
+ if (idx === -1) idx = content.search(new RegExp(`export\\s+(const|class)\\s+${name}\b`));
121
+ if (idx === -1) idx = content.search(new RegExp(`export\\s+(interface|type)\\s+${name}Props\b`));
122
+ const description = idx !== -1 ? getJsdocAbove(content, idx) : "";
123
+
124
+ const props = extractProps(content, name);
125
+
126
+ components.push({
127
+ name,
128
+ importRoot: "@texturehq/edges",
129
+ importPath: `@texturehq/edges/components/${name}`,
130
+ description,
131
+ props,
132
+ });
133
+ }
134
+
135
+ ensureDir(DIST_DIR);
136
+ const manifest = {
137
+ version: process.env.npm_package_version || "unknown",
138
+ generatedAt: new Date().toISOString(),
139
+ components: components.sort((a, b) => a.name.localeCompare(b.name)),
140
+ };
141
+ fs.writeFileSync(path.join(DIST_DIR, "components.manifest.json"), JSON.stringify(manifest, null, 2));
142
+ console.log(`Generated ${path.join("dist", "components.manifest.json")} with ${components.length} components.`);
143
+ } catch (err) {
144
+ console.error("Failed to generate components manifest:", err);
145
+ process.exitCode = 1;
146
+ }
147
+ }
148
+
149
+ run();
150
+
151
+
@@ -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();