@texturehq/edges 1.29.1 → 1.30.1

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.
@@ -1,1127 +0,0 @@
1
- // generate-edges-docs.js
2
- // Unified AI Context Generator for @texturehq/edges
3
- //
4
- // Consolidates and enhances:
5
- // - generate-components-manifest.js
6
- // - generate-utilities-manifest.js
7
- //
8
- // Generates:
9
- // - Component, utility, and hook manifests
10
- // - docs/ai/edges/ markdown documentation
11
- // - .agent-fragments/edges.md (for merging with other fragments)
12
- // - .cursor/rules/edges-components.mdc
13
- // - Distributable context files for consuming apps
14
-
15
- import { execSync } from "child_process";
16
- import fs from "fs";
17
- import path from "path";
18
-
19
- const ROOT = process.cwd();
20
- const SRC_DIR = path.join(ROOT, "src");
21
- const DIST_DIR = path.join(ROOT, "dist");
22
- const MONO_ROOT = path.join(ROOT, "../..");
23
- const DOCS_AI_EDGES = path.join(MONO_ROOT, "docs/ai/edges");
24
-
25
- const PACKAGE_JSON_PATH = path.join(ROOT, "package.json");
26
- const PACKAGE_VERSION = JSON.parse(fs.readFileSync(PACKAGE_JSON_PATH, "utf8")).version;
27
-
28
- // ============================================================================
29
- // UTILITIES
30
- // ============================================================================
31
-
32
- function read(file) {
33
- return fs.existsSync(file) ? fs.readFileSync(file, "utf8") : "";
34
- }
35
-
36
- function ensureDir(dir) {
37
- if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
38
- }
39
-
40
- function escapeMarkdownTableCell(text) {
41
- // Only escape backslashes since the text will be wrapped in backticks (inline code).
42
- // Pipes and backticks don't need escaping inside inline code spans.
43
- return text.replace(/\\/g, "\\\\");
44
- }
45
-
46
- function writeFile(filePath, content) {
47
- ensureDir(path.dirname(filePath));
48
- fs.writeFileSync(filePath, content, "utf8");
49
- console.log(`✓ Generated: ${filePath}`);
50
- }
51
-
52
- // ============================================================================
53
- // COMPONENT MANIFEST GENERATION
54
- // ============================================================================
55
-
56
- function extractExportsFromIndex(indexContent) {
57
- const re = /export\s+\{\s*([A-Za-z0-9_,\s]+)\s*\}\s+from\s+"\.\/components\/([A-Za-z0-9_/.-]+)"/g;
58
- const results = [];
59
- let m;
60
- while ((m = re.exec(indexContent))) {
61
- const exported = m[1]
62
- .split(",")
63
- .map((s) => s.trim())
64
- .filter(Boolean);
65
- const rel = m[2];
66
- for (const name of exported) {
67
- results.push({ name, relPath: rel });
68
- }
69
- }
70
- return results;
71
- }
72
-
73
- function findComponentFile(relPath) {
74
- const base = path.join(SRC_DIR, "components", relPath);
75
- const componentName = relPath.split("/").pop();
76
- const candidates = [
77
- path.join(base, `${componentName}.tsx`),
78
- path.join(base, `${componentName}.ts`),
79
- path.join(base, `index.tsx`),
80
- path.join(base, `index.ts`),
81
- ];
82
- for (const f of candidates) {
83
- if (fs.existsSync(f)) return f;
84
- }
85
- return null;
86
- }
87
-
88
- function findStorybookFile(relPath) {
89
- const base = path.join(SRC_DIR, "components", relPath);
90
- const componentName = relPath.split("/").pop();
91
- const candidates = [
92
- path.join(base, `${componentName}.stories.tsx`),
93
- path.join(base, `${componentName}.stories.ts`),
94
- ];
95
- for (const f of candidates) {
96
- if (fs.existsSync(f)) return f;
97
- }
98
- return null;
99
- }
100
-
101
- function extractStorybookCategory(storyContent) {
102
- // Extract: title: "Form Controls/Button"
103
- const titleMatch = storyContent.match(/title:\s*["']([^"']+)["']/);
104
- if (titleMatch) {
105
- const fullTitle = titleMatch[1];
106
- const parts = fullTitle.split("/");
107
- if (parts.length > 1) {
108
- return parts[0]; // "Form Controls"
109
- }
110
- }
111
- return null;
112
- }
113
-
114
- function extractRelatedComponents(storyContent) {
115
- // Find other component imports in the story file
116
- const importMatch = storyContent.match(/import\s*\{([^}]+)\}\s*from\s*["']\.\.\/(\w+)["']/g);
117
- const related = new Set();
118
- if (importMatch) {
119
- for (const imp of importMatch) {
120
- const m = imp.match(/import\s*\{([^}]+)\}/);
121
- if (m) {
122
- const imports = m[1].split(",").map((s) => s.trim());
123
- for (const name of imports) {
124
- if (/^[A-Z]/.test(name)) {
125
- related.add(name);
126
- }
127
- }
128
- }
129
- }
130
- }
131
- return Array.from(related);
132
- }
133
-
134
- function getJsdocAbove(content, idx) {
135
- const upTo = content.slice(0, idx);
136
- const start = upTo.lastIndexOf("/**");
137
- const end = upTo.lastIndexOf("*/");
138
- if (start !== -1 && end !== -1 && end > start) {
139
- const block = upTo.slice(start + 3, end).trim();
140
- return block
141
- .split("\n")
142
- .map((l) => l.replace(/^\s*\*\s?/, "").trim())
143
- .join(" ")
144
- .trim();
145
- }
146
- return "";
147
- }
148
-
149
- function extractProps(content, componentName) {
150
- const ifaceRe = new RegExp(
151
- `export\\s+interface\\s+${componentName}Props\\s*(?:extends\\s+[^{]+)?\\{([\\s\\S]*?)\\}`,
152
- "m"
153
- );
154
- let m = ifaceRe.exec(content);
155
- if (!m) {
156
- const anyIfaceRe =
157
- /export\s+interface\s+([A-Za-z0-9_]+Props)\s*(?:extends\s+[^{]+)?\{([\s\S]*?)\}/m;
158
- m = anyIfaceRe.exec(content);
159
- }
160
- if (!m) {
161
- const typeRe = new RegExp(
162
- `export\\s+type\\s+${componentName}Props\\s*=\\s*(?:[^{&]+&\s*)?([\\s\\S]*?)\\}`,
163
- "m"
164
- );
165
- m = typeRe.exec(content);
166
- }
167
- const props = [];
168
- if (m) {
169
- const body = m[2] || m[1] || "";
170
- // Parse props more carefully to handle nested types
171
- let currentProp = "";
172
- let braceDepth = 0;
173
- const lines = body.split("\n");
174
-
175
- for (const line of lines) {
176
- const trimmed = line.trim();
177
- if (!trimmed || trimmed.startsWith("//")) continue;
178
-
179
- // Count braces to track nested objects
180
- for (const char of trimmed) {
181
- if (char === "{") braceDepth++;
182
- if (char === "}") braceDepth--;
183
- }
184
-
185
- currentProp += (currentProp ? " " : "") + trimmed;
186
-
187
- // If we're at depth 0 and the line ends with semicolon or we're complete, process the prop
188
- if (braceDepth === 0 && (trimmed.endsWith(";") || trimmed.endsWith(","))) {
189
- const pm = /^(readonly\s+)?([A-Za-z0-9_]+)\??:\s*(.+?)[;,]?\s*$/.exec(currentProp.trim());
190
- if (pm) {
191
- const name = pm[2];
192
- let type = pm[3].trim();
193
- // Clean up type - remove trailing semicolon/comma
194
- type = type.replace(/[;,]\s*$/, "");
195
- // Simplify very long nested types for readability
196
- if (type.includes("{") && type.length > 100) {
197
- // For nested object types, show simplified version
198
- type = type.substring(0, 80) + "...}";
199
- }
200
- props.push({ name, type });
201
- }
202
- currentProp = "";
203
- braceDepth = 0;
204
- }
205
- }
206
- }
207
- return props;
208
- }
209
-
210
- function generateComponentsManifest() {
211
- console.log("\n📦 Generating components manifest...");
212
-
213
- const indexPath = path.join(SRC_DIR, "index.ts");
214
- const indexContent = read(indexPath);
215
- const exports = extractExportsFromIndex(indexContent);
216
-
217
- const components = [];
218
- const categories = {};
219
-
220
- for (const { name, relPath } of exports) {
221
- if (!/^[A-Z]/.test(name)) continue;
222
-
223
- const componentFile = findComponentFile(relPath);
224
- if (!componentFile) continue;
225
- const content = read(componentFile);
226
-
227
- // Extract description from JSDoc
228
- let idx = -1;
229
- let description = "";
230
- // Use regex patterns with word boundaries to avoid substring matches
231
- const patterns = [
232
- new RegExp(`export\\s+function\\s+${name}\\s*[({<]`),
233
- new RegExp(`export\\s+const\\s+${name}\\s*[:=]`),
234
- new RegExp(`export\\s+class\\s+${name}\\s+`),
235
- ];
236
-
237
- for (const pattern of patterns) {
238
- const match = content.search(pattern);
239
- if (match !== -1) {
240
- idx = match;
241
- break;
242
- }
243
- }
244
-
245
- if (idx !== -1) {
246
- description = getJsdocAbove(content, idx);
247
- }
248
-
249
- // Extract props
250
- const props = extractProps(content, name);
251
- // Sort props alphabetically for deterministic output
252
- props.sort((a, b) => a.name.localeCompare(b.name));
253
-
254
- // Extract Storybook metadata
255
- const storyFile = findStorybookFile(relPath);
256
- let category = "Uncategorized";
257
- let relatedComponents = [];
258
-
259
- if (storyFile) {
260
- const storyContent = read(storyFile);
261
- const storyCategory = extractStorybookCategory(storyContent);
262
- if (storyCategory) category = storyCategory;
263
- relatedComponents = extractRelatedComponents(storyContent);
264
- }
265
-
266
- const component = {
267
- name,
268
- category,
269
- description,
270
- importRoot: "@texturehq/edges",
271
- importPath: `@texturehq/edges/components/${name}`,
272
- props,
273
- relatedComponents,
274
- storybookPath: category + "/" + name,
275
- };
276
-
277
- components.push(component);
278
-
279
- // Organize by category
280
- if (!categories[category]) {
281
- categories[category] = [];
282
- }
283
- categories[category].push(name);
284
- }
285
-
286
- // Sort components by name
287
- components.sort((a, b) => a.name.localeCompare(b.name));
288
-
289
- // Sort category arrays
290
- for (const cat in categories) {
291
- categories[cat].sort();
292
- }
293
-
294
- const manifest = {
295
- version: PACKAGE_VERSION,
296
- // generatedAt removed to prevent unnecessary git churn
297
- // Use version field to track which package version generated this
298
- components: components,
299
- categories: categories,
300
- };
301
-
302
- ensureDir(DIST_DIR);
303
- writeFile(path.join(DIST_DIR, "components.manifest.json"), JSON.stringify(manifest, null, 2));
304
-
305
- console.log(
306
- ` Found ${components.length} components in ${Object.keys(categories).length} categories`
307
- );
308
- return manifest;
309
- }
310
-
311
- // ============================================================================
312
- // UTILITIES & HOOKS MANIFEST GENERATION
313
- // ============================================================================
314
-
315
- function extractJSDoc(content, functionName) {
316
- const patterns = [
317
- new RegExp(
318
- `\\/\\*\\*([^*]|\\*(?!\\/))*\\*\\/\\s*export\\s+(?:async\\s+)?function\\s+${functionName}\\b`,
319
- "s"
320
- ),
321
- new RegExp(`\\/\\*\\*([^*]|\\*(?!\\/))*\\*\\/\\s*export\\s+const\\s+${functionName}\\s*=`, "s"),
322
- ];
323
-
324
- for (const pattern of patterns) {
325
- const match = content.match(pattern);
326
- if (match) {
327
- const jsdocMatch = match[0].match(/\/\*\*([\s\S]*?)\*\//);
328
- if (jsdocMatch) {
329
- const lines = jsdocMatch[1]
330
- .split("\n")
331
- .map((line) => line.replace(/^\s*\*\s?/, "").trim());
332
-
333
- // Extract only the main description (before any @tags)
334
- const descriptionLines = [];
335
- for (const line of lines) {
336
- if (line.startsWith("@")) {
337
- break; // Stop at first @tag
338
- }
339
- if (line) {
340
- descriptionLines.push(line);
341
- }
342
- }
343
-
344
- return descriptionLines.join(" ").trim();
345
- }
346
- }
347
- }
348
- return "";
349
- }
350
-
351
- function extractExportsFromFile(filePath) {
352
- const content = read(filePath);
353
- if (!content) return [];
354
-
355
- const utilities = [];
356
-
357
- // Helper to check if a match is inside a comment
358
- function isInComment(matchIndex) {
359
- const beforeMatch = content.slice(0, matchIndex);
360
-
361
- // Check if we're inside a JSDoc comment (/** ... */)
362
- const lastJSDocStart = beforeMatch.lastIndexOf("/**");
363
- const lastJSDocEnd = beforeMatch.lastIndexOf("*/");
364
- if (lastJSDocStart > lastJSDocEnd) {
365
- return true; // Inside JSDoc comment
366
- }
367
-
368
- // Check if we're inside a regular block comment (/* ... */)
369
- const lastBlockStart = beforeMatch.lastIndexOf("/*");
370
- const lastBlockEnd = beforeMatch.lastIndexOf("*/");
371
- if (lastBlockStart > lastBlockEnd) {
372
- // Make sure it's not a JSDoc opener (/**)
373
- const blockOpener = beforeMatch.slice(lastBlockStart, lastBlockStart + 3);
374
- if (blockOpener !== "/**") {
375
- return true; // Inside regular block comment
376
- }
377
- }
378
-
379
- // Check if we're on a line comment (//)
380
- const lastNewline = beforeMatch.lastIndexOf("\n");
381
- const lineStart = lastNewline === -1 ? 0 : lastNewline + 1;
382
- const line = beforeMatch.slice(lineStart);
383
- if (line.includes("//")) {
384
- return true; // Inside line comment
385
- }
386
-
387
- return false;
388
- }
389
-
390
- // Match export function declarations
391
- const functionPattern = /export\s+(?:async\s+)?function\s+(\w+)/g;
392
- let match;
393
- while ((match = functionPattern.exec(content))) {
394
- if (!isInComment(match.index)) {
395
- const name = match[1];
396
- utilities.push({
397
- name,
398
- type: "function",
399
- description: extractJSDoc(content, name),
400
- });
401
- }
402
- }
403
-
404
- // Match export const arrow functions and constants
405
- const constPattern = /export\s+const\s+(\w+)\s*(?::\s*[^=]+)?\s*=/g;
406
- while ((match = constPattern.exec(content))) {
407
- if (!isInComment(match.index)) {
408
- const name = match[1];
409
- const afterMatch = content.slice(
410
- match.index + match[0].length,
411
- match.index + match[0].length + 50
412
- );
413
- const isFunction = afterMatch.includes("=>") || afterMatch.includes("function");
414
-
415
- utilities.push({
416
- name,
417
- type: isFunction ? "function" : "constant",
418
- description: extractJSDoc(content, name),
419
- });
420
- }
421
- }
422
-
423
- return utilities;
424
- }
425
-
426
- function scanDirectory(dir, category) {
427
- const utilities = [];
428
-
429
- if (!fs.existsSync(dir)) return utilities;
430
-
431
- const files = fs.readdirSync(dir);
432
-
433
- for (const file of files) {
434
- const filePath = path.join(dir, file);
435
- const stat = fs.statSync(filePath);
436
-
437
- if (stat.isDirectory() && file !== "__tests__" && file !== "tests") {
438
- utilities.push(...scanDirectory(filePath, `${category}/${file}`));
439
- } else if ((file.endsWith(".ts") || file.endsWith(".tsx")) && !file.endsWith(".d.ts")) {
440
- if (file.includes(".test.") || file.includes(".spec.") || file.includes(".stories.")) {
441
- continue;
442
- }
443
-
444
- const funcs = extractExportsFromFile(filePath);
445
- for (const func of funcs) {
446
- utilities.push({
447
- ...func,
448
- category,
449
- file: path.relative(SRC_DIR, filePath),
450
- });
451
- }
452
- }
453
- }
454
-
455
- return utilities;
456
- }
457
-
458
- function generateUtilitiesManifest() {
459
- console.log("\n🔧 Generating utilities manifest...");
460
-
461
- const manifest = {
462
- version: PACKAGE_VERSION,
463
- // generatedAt removed to prevent unnecessary git churn
464
- // Use version field to track which package version generated this
465
- categories: {},
466
- };
467
-
468
- // Scan hooks directory
469
- const hooksDir = path.join(SRC_DIR, "hooks");
470
- const hooks = scanDirectory(hooksDir, "hooks");
471
- if (hooks.length > 0) {
472
- manifest.categories.hooks = {
473
- description:
474
- "React hooks for common functionality like breakpoints, debouncing, local storage, and media queries",
475
- utilities: hooks.sort((a, b) => a.name.localeCompare(b.name)),
476
- };
477
- }
478
-
479
- // Scan formatting utilities
480
- const formattingDir = path.join(SRC_DIR, "utils/formatting");
481
- const formatting = scanDirectory(formattingDir, "formatting");
482
- if (formatting.length > 0) {
483
- manifest.categories.formatting = {
484
- description:
485
- "Comprehensive formatting utilities for text, numbers, dates, currency, energy, temperature, distance, and more",
486
- utilities: formatting.sort((a, b) => a.name.localeCompare(b.name)),
487
- };
488
- }
489
-
490
- // Chart utilities
491
- const chartsDir = path.join(SRC_DIR, "utils/charts");
492
- const charts = scanDirectory(chartsDir, "charts");
493
- const chartExportUtils = extractExportsFromFile(path.join(SRC_DIR, "utils/chartExport.ts"));
494
- const chartUtils = extractExportsFromFile(path.join(SRC_DIR, "utils/charts.ts"));
495
- const allChartUtils = [
496
- ...charts,
497
- ...chartExportUtils.map((f) => ({ ...f, category: "charts", file: "utils/chartExport.ts" })),
498
- ...chartUtils.map((f) => ({ ...f, category: "charts", file: "utils/charts.ts" })),
499
- ];
500
-
501
- if (allChartUtils.length > 0) {
502
- manifest.categories.charts = {
503
- description: "Chart utilities for data visualization, scaling, and export functionality",
504
- utilities: allChartUtils.sort((a, b) => a.name.localeCompare(b.name)),
505
- };
506
- }
507
-
508
- // Color utilities
509
- const colorUtils = extractExportsFromFile(path.join(SRC_DIR, "utils/colors.ts"));
510
- if (colorUtils.length > 0) {
511
- manifest.categories.colors = {
512
- description:
513
- "Color management utilities for theme-aware color resolution, contrast calculation, and palette generation",
514
- utilities: colorUtils
515
- .map((f) => ({ ...f, category: "colors", file: "utils/colors.ts" }))
516
- .sort((a, b) => a.name.localeCompare(b.name)),
517
- };
518
- }
519
-
520
- const totalUtilities = Object.values(manifest.categories).reduce(
521
- (sum, cat) => sum + cat.utilities.length,
522
- 0
523
- );
524
-
525
- ensureDir(DIST_DIR);
526
- writeFile(path.join(DIST_DIR, "utilities.manifest.json"), JSON.stringify(manifest, null, 2));
527
-
528
- console.log(
529
- ` Found ${totalUtilities} utilities in ${Object.keys(manifest.categories).length} categories`
530
- );
531
- return manifest;
532
- }
533
-
534
- // ============================================================================
535
- // THEME PARSING
536
- // ============================================================================
537
-
538
- function parseThemeColors() {
539
- console.log("\n🎨 Parsing theme colors...");
540
-
541
- const distThemePath = path.join(DIST_DIR, "theme.css");
542
- const sources = [];
543
-
544
- if (fs.existsSync(distThemePath)) {
545
- sources.push(distThemePath);
546
- } else {
547
- const generatedDir = path.join(SRC_DIR, "generated");
548
- const fallbackFiles = [
549
- path.join(generatedDir, "tailwind-tokens-light.css"),
550
- path.join(generatedDir, "tailwind-tokens-dark.css"),
551
- path.join(generatedDir, "viz-runtime.css"),
552
- ];
553
-
554
- for (const file of fallbackFiles) {
555
- if (fs.existsSync(file)) {
556
- sources.push(file);
557
- }
558
- }
559
-
560
- if (sources.length > 0) {
561
- console.log(" ℹ️ Using generated token CSS as fallback (dist/theme.css missing)");
562
- }
563
- }
564
-
565
- if (sources.length === 0) {
566
- console.log(" ⚠️ No theme CSS sources found, skipping color extraction");
567
- return {};
568
- }
569
-
570
- const colorVars = {};
571
- const seen = new Set();
572
- const colorRegex = /--color-([a-zA-Z0-9-]+):\s*([^;]+);/g;
573
-
574
- for (const source of sources) {
575
- const themeContent = read(source);
576
- let match;
577
-
578
- while ((match = colorRegex.exec(themeContent))) {
579
- const varName = match[1];
580
- const value = match[2].trim();
581
-
582
- if (!seen.has(varName)) {
583
- seen.add(varName);
584
- colorVars[varName] = value;
585
- }
586
- }
587
- }
588
-
589
- console.log(` Found ${Object.keys(colorVars).length} color variables`);
590
- return colorVars;
591
- }
592
-
593
- // ============================================================================
594
- // MARKDOWN DOCUMENTATION GENERATION
595
- // ============================================================================
596
-
597
- function generateComponentsReference(componentsManifest) {
598
- console.log("\n📝 Generating components reference...");
599
-
600
- const { components, categories } = componentsManifest;
601
-
602
- let content = `# Edges Components Reference
603
-
604
- > **Auto-generated** - Do not edit manually. Run \`node scripts/generate-edges-docs.js\` to regenerate.
605
-
606
- This document provides a complete reference of all ${components.length} components in the @texturehq/edges design system, organized by functional category.
607
-
608
- ## Quick Navigation
609
-
610
- `;
611
-
612
- // Table of contents by category
613
- const sortedCategories = Object.keys(categories).sort();
614
- for (const category of sortedCategories) {
615
- const count = categories[category].length;
616
- content += `- [${category}](#${category.toLowerCase().replace(/\s+/g, "-")}) (${count} component${count > 1 ? "s" : ""})\n`;
617
- }
618
-
619
- content += `\n---\n\n`;
620
-
621
- // Generate content by category
622
- for (const category of sortedCategories) {
623
- content += `## ${category}\n\n`;
624
-
625
- const categoryComponents = components.filter((c) => c.category === category);
626
- categoryComponents.sort((a, b) => a.name.localeCompare(b.name));
627
-
628
- for (const component of categoryComponents) {
629
- content += `### ${component.name}\n\n`;
630
-
631
- if (component.description) {
632
- content += `${component.description}\n\n`;
633
- }
634
-
635
- content += `**Import:**\n\n`;
636
- content += `\`\`\`typescript\n`;
637
- content += `import { ${component.name} } from "${component.importRoot}";\n`;
638
- content += `// or\n`;
639
- content += `import { ${component.name} } from "${component.importPath}";\n`;
640
- content += `\`\`\`\n\n`;
641
-
642
- if (component.props && component.props.length > 0) {
643
- content += `**Props:**\n\n`;
644
- content += `| Prop | Type |\n`;
645
- content += `|------|------|\n`;
646
- for (const prop of component.props) {
647
- const safeType = escapeMarkdownTableCell(prop.type);
648
- content += `| \`${prop.name}\` | \`${safeType}\` |\n`;
649
- }
650
- content += `\n`;
651
- }
652
-
653
- if (component.relatedComponents && component.relatedComponents.length > 0) {
654
- content += `**Related:** ${component.relatedComponents.join(", ")}\n\n`;
655
- }
656
-
657
- if (component.storybookPath) {
658
- content += `**Storybook:** \`${component.storybookPath}\`\n\n`;
659
- }
660
-
661
- content += `---\n\n`;
662
- }
663
- }
664
-
665
- writeFile(path.join(DOCS_AI_EDGES, "components-reference.md"), content);
666
- }
667
-
668
- function generateHooksReference(utilitiesManifest) {
669
- console.log("\n🪝 Generating hooks reference...");
670
-
671
- const hooksCategory = utilitiesManifest.categories.hooks;
672
- if (!hooksCategory) {
673
- console.log(" No hooks found");
674
- return;
675
- }
676
-
677
- const hooks = hooksCategory.utilities;
678
-
679
- let content = `# Edges Hooks Reference
680
-
681
- > **Auto-generated** - Do not edit manually. Run \`node scripts/generate-edges-docs.js\` to regenerate.
682
-
683
- This document provides a complete reference of all ${hooks.length} React hooks in the @texturehq/edges design system.
684
-
685
- ${hooksCategory.description}
686
-
687
- ## Available Hooks
688
-
689
- `;
690
-
691
- for (const hook of hooks) {
692
- content += `### ${hook.name}\n\n`;
693
-
694
- if (hook.description) {
695
- content += `${hook.description}\n\n`;
696
- }
697
-
698
- content += `**Import:**\n\n`;
699
- content += `\`\`\`typescript\n`;
700
- content += `import { ${hook.name} } from "@texturehq/edges";\n`;
701
- content += `\`\`\`\n\n`;
702
-
703
- content += `**Source:** \`src/${hook.file}\`\n\n`;
704
- content += `---\n\n`;
705
- }
706
-
707
- writeFile(path.join(DOCS_AI_EDGES, "hooks-reference.md"), content);
708
- }
709
-
710
- function generateUtilitiesReference(utilitiesManifest) {
711
- console.log("\n🛠️ Generating utilities reference...");
712
-
713
- const categories = Object.keys(utilitiesManifest.categories).filter((c) => c !== "hooks");
714
- if (categories.length === 0) {
715
- console.log(" No utilities found");
716
- return;
717
- }
718
-
719
- let content = `# Edges Utilities Reference
720
-
721
- > **Auto-generated** - Do not edit manually. Run \`node scripts/generate-edges-docs.js\` to regenerate.
722
-
723
- This document provides a complete reference of all utility functions in the @texturehq/edges design system.
724
-
725
- ## Categories
726
-
727
- `;
728
-
729
- for (const category of categories) {
730
- const cat = utilitiesManifest.categories[category];
731
- content += `- [${category}](#${category}) (${cat.utilities.length} utilities)\n`;
732
- }
733
-
734
- content += `\n---\n\n`;
735
-
736
- for (const category of categories) {
737
- const cat = utilitiesManifest.categories[category];
738
- content += `## ${category}\n\n`;
739
- content += `${cat.description}\n\n`;
740
-
741
- for (const util of cat.utilities) {
742
- content += `### ${util.name}\n\n`;
743
-
744
- if (util.description) {
745
- content += `${util.description}\n\n`;
746
- }
747
-
748
- content += `**Type:** ${util.type}\n\n`;
749
- content += `**Import:**\n\n`;
750
- content += `\`\`\`typescript\n`;
751
- content += `import { ${util.name} } from "@texturehq/edges";\n`;
752
- content += `\`\`\`\n\n`;
753
-
754
- content += `**Source:** \`src/${util.file}\`\n\n`;
755
- content += `---\n\n`;
756
- }
757
- }
758
-
759
- writeFile(path.join(DOCS_AI_EDGES, "utilities-reference.md"), content);
760
- }
761
-
762
- function generateThemeSystemDoc(colorVars) {
763
- console.log("\n🎨 Generating theme system documentation...");
764
-
765
- let content = `# Edges Theme System
766
-
767
- > **Auto-generated** - Do not edit manually. Run \`node scripts/generate-edges-docs.js\` to regenerate.
768
-
769
- ## Overview
770
-
771
- The @texturehq/edges design system uses Tailwind 4 with CSS variables. All design tokens are defined as CSS variables and automatically become available as Tailwind utility classes.
772
-
773
- ## Setup
774
-
775
- \`\`\`css
776
- @import "@texturehq/edges/theme.css";
777
- \`\`\`
778
-
779
- ## How It Works
780
-
781
- CSS variables automatically become Tailwind classes:
782
- - \`--color-brand-primary\` → \`bg-brand-primary\`, \`text-brand-primary\`, \`border-brand-primary\`
783
- - \`--spacing-md\` → \`p-md\`, \`m-md\`, \`gap-md\`
784
- - \`--text-lg\` → \`text-lg\`
785
- - \`--radius-lg\` → \`rounded-lg\`
786
-
787
- ## Color System
788
-
789
- The theme includes ${Object.keys(colorVars).length} color variables organized by purpose:
790
-
791
- ### Brand Colors
792
- - \`brand-primary\` - Primary brand color
793
- - \`brand-light\` - Light brand variant
794
- - \`brand-dark\` - Dark brand variant
795
-
796
- ### Text Colors
797
- - \`text-body\` - Body text
798
- - \`text-heading\` - Heading text
799
- - \`text-muted\` - Muted/secondary text
800
- - \`text-caption\` - Caption/helper text
801
- - \`text-on-primary\` - Text on primary backgrounds
802
-
803
- ### Background Colors
804
- - \`background-body\` - Main page background
805
- - \`background-surface\` - Card/surface background
806
- - \`background-muted\` - Muted background
807
- - \`background-subtle\` - Subtle background
808
-
809
- ### Border Colors
810
- - \`border-default\` - Default border
811
- - \`border-focus\` - Focused border
812
- - \`border-muted\` - Muted border
813
- - \`border-subtle\` - Subtle border
814
-
815
- ### Action Colors
816
- - \`action-primary\` - Primary actions
817
- - \`action-brand\` - Brand-focused actions
818
- - \`action-secondary\` - Secondary actions
819
- - \`action-default\` - Default gray actions
820
- - \`action-destructive\` - Destructive actions
821
-
822
- ### Feedback Colors
823
- - \`feedback-success\` - Success state
824
- - \`feedback-error\` - Error state
825
- - \`feedback-warning\` - Warning state
826
- - \`feedback-info\` - Info state
827
-
828
- ## Usage Guidelines
829
-
830
- **✅ Good - Use semantic classes:**
831
- \`\`\`tsx
832
- <div className="bg-brand-primary text-text-on-primary p-md rounded-lg shadow-md">
833
- <h2 className="text-text-heading text-lg font-medium">Title</h2>
834
- <p className="text-text-body text-base">Content</p>
835
- </div>
836
- \`\`\`
837
-
838
- **❌ Avoid - Arbitrary values:**
839
- \`\`\`tsx
840
- <div className="bg-[#444ae1] text-[#ffffff] p-[1rem] rounded-[0.5rem]">
841
- <h2 className="text-[#111827] text-[1.125rem] font-[500]">Title</h2>
842
- <p className="text-[#333333] text-[1rem]">Content</p>
843
- </div>
844
- \`\`\`
845
-
846
- ## Spacing Scale
847
-
848
- - \`xs\`, \`sm\`, \`md\`, \`lg\`, \`xl\`, \`2xl\`, \`3xl\`, \`4xl\`
849
-
850
- ## Typography Scale
851
-
852
- - \`xs\`, \`sm\`, \`base\`, \`lg\`, \`xl\`, \`2xl\`, \`3xl\`, \`4xl\`
853
-
854
- ## Border Radius Scale
855
-
856
- - \`xs\`, \`sm\`, \`md\`, \`lg\`, \`xl\`, \`2xl\`, \`3xl\`, \`4xl\`
857
-
858
- ## Dark Mode
859
-
860
- All colors automatically adapt to dark mode when the \`.theme-dark\` class is present on a parent element.
861
-
862
- ## Available Variables
863
-
864
- The theme includes:
865
- - Complete color system (${Object.keys(colorVars).length} color variables)
866
- - Spacing scale
867
- - Typography scale
868
- - Border radius scale
869
- - Shadow system
870
- - Animation definitions
871
- - Form control specifications
872
- `;
873
-
874
- writeFile(path.join(DOCS_AI_EDGES, "theme-system.md"), content);
875
- }
876
-
877
- function generateEdgesReadme(componentsManifest, utilitiesManifest) {
878
- console.log("\n📄 Generating edges README...");
879
-
880
- const componentCount = componentsManifest.components.length;
881
- const categoryCount = Object.keys(componentsManifest.categories).length;
882
- const hookCount = utilitiesManifest.categories.hooks?.utilities.length || 0;
883
- // Use version instead of timestamp to avoid git churn
884
- const version = componentsManifest.version || PACKAGE_VERSION;
885
-
886
- let utilityCount = 0;
887
- for (const cat in utilitiesManifest.categories) {
888
- if (cat !== "hooks") {
889
- utilityCount += utilitiesManifest.categories[cat].utilities.length;
890
- }
891
- }
892
-
893
- let content = `# Edges Design System - AI Context
894
-
895
- > **Auto-generated** - Do not edit manually. Run \`node scripts/generate-edges-docs.js\` to regenerate.
896
-
897
- 👋 **AI Agents: Start here!**
898
-
899
- Complete documentation for the @texturehq/edges design system.
900
-
901
- ## What's Inside
902
-
903
- - **${componentCount} Components** across ${categoryCount} categories
904
- - **${hookCount} React Hooks** for common functionality
905
- - **${utilityCount} Utility Functions** for formatting, charts, colors, and more
906
- - **Complete Theme System** with Tailwind 4 + CSS variables
907
- - **Integrations** (Optional) like React Hook Form support
908
-
909
- ## Quick Links
910
-
911
- ### Component Library
912
- - [Components Reference](components-reference.md) - All ${componentCount} components organized by category
913
- - [Component Categories](#component-categories) - Quick navigation by function
914
-
915
- ### Utilities & Hooks
916
- - [Hooks Reference](hooks-reference.md) - All ${hookCount} React hooks
917
- - [Utilities Reference](utilities-reference.md) - All ${utilityCount} utility functions
918
-
919
- ### Design System
920
- - [Theme System](theme-system.md) - Colors, spacing, typography with Tailwind 4
921
- - [Patterns](patterns.md) - Common UI patterns and best practices
922
-
923
- ### Integrations (Optional)
924
- - [React Hook Form (RHF)](integrations-rhf.md) - Opt-in adapters and utilities for fast form building
925
-
926
- ## Component Categories
927
-
928
- `;
929
-
930
- const sortedCategories = Object.keys(componentsManifest.categories).sort();
931
- for (const category of sortedCategories) {
932
- const components = componentsManifest.categories[category];
933
- content += `### ${category}\n\n`;
934
- content += `${components.join(", ")}\n\n`;
935
- }
936
-
937
- content += `## Installation
938
-
939
- \`\`\`bash
940
- yarn add @texturehq/edges
941
- \`\`\`
942
-
943
- ## Setup
944
-
945
- \`\`\`css
946
- @import "@texturehq/edges/theme.css";
947
- \`\`\`
948
-
949
- \`\`\`typescript
950
- import { Button, TextField, DataTable } from "@texturehq/edges";
951
- \`\`\`
952
-
953
- ## Integrations
954
-
955
- React Hook Form integration is available as an optional subpath and tree-shakes when unused.
956
-
957
- \`\`\`bash
958
- yarn add react-hook-form @hookform/resolvers zod
959
- \`\`\`
960
-
961
- \`\`\`typescript
962
- import { RHFForm, RHFTextField } from "@texturehq/edges/rhf";
963
- \`\`\`
964
-
965
- ## For Dashboard Development
966
-
967
- When building UI in \`apps/dashboard\`, reference:
968
- - **Components**: \`docs/ai/edges/components-reference.md\`
969
- - **Hooks**: \`docs/ai/edges/hooks-reference.md\`
970
- - **Utilities**: \`docs/ai/edges/utilities-reference.md\`
971
- - **Theme**: \`docs/ai/edges/theme-system.md\`
972
- - **Patterns**: \`docs/ai/edges/patterns.md\`
973
- - **Integrations (Optional)**: \`docs/ai/edges/integrations-rhf.md\`
974
-
975
- ---
976
-
977
- **Package Version:** ${version}
978
- `;
979
-
980
- writeFile(path.join(DOCS_AI_EDGES, "README.md"), content);
981
- }
982
-
983
- // ============================================================================
984
- // AGENT INSTRUCTION FRAGMENT
985
- // ============================================================================
986
-
987
- function generateEdgesFragment(componentsManifest, utilitiesManifest) {
988
- console.log("\n📄 Generating edges fragment...");
989
-
990
- const componentCount = componentsManifest.components.length;
991
- const hookCount = utilitiesManifest.categories.hooks?.utilities.length || 0;
992
-
993
- let utilityCount = 0;
994
- for (const cat in utilitiesManifest.categories) {
995
- if (cat !== "hooks") {
996
- utilityCount += utilitiesManifest.categories[cat].utilities.length;
997
- }
998
- }
999
-
1000
- const content = `<!-- priority: 20 -->
1001
- ## Edges Design System Context
1002
-
1003
- The @texturehq/edges design system provides ${componentCount} React components, ${hookCount} hooks, and ${utilityCount} utility functions.
1004
-
1005
- **Complete component documentation is in**: \`docs/ai/edges/\`
1006
-
1007
- ### Quick Start
1008
-
1009
- 1. Read \`docs/ai/edges/README.md\` for an overview and component categories
1010
- 2. Reference \`docs/ai/edges/components-reference.md\` for detailed component API
1011
- 3. Review \`docs/ai/edges/patterns.md\` for common UI patterns and best practices
1012
- 4. Check \`docs/ai/edges/theme-system.md\` for Tailwind 4 color tokens and styling
1013
-
1014
- ### Forms + RHF (Optional)
1015
-
1016
- - Import adapters from \`@texturehq/edges/rhf\`; install optional peers: \`react-hook-form @hookform/resolvers zod\`
1017
- - Quickstart:
1018
- \`\`\`tsx
1019
- import { RHFForm, RHFTextField, SubmitButton, ErrorSummary } from "@texturehq/edges/rhf";
1020
- \`\`\`
1021
- - Full guide: \`docs/ai/edges/integrations-rhf.md\`
1022
-
1023
- ### Additional Resources
1024
-
1025
- - Hooks: \`docs/ai/edges/hooks-reference.md\`
1026
- - Utilities: \`docs/ai/edges/utilities-reference.md\`
1027
-
1028
- ### For Dashboard Development
1029
-
1030
- **UI (Edges)**:
1031
- - All components, hooks, and utilities from \`@texturehq/edges\`
1032
- - Use semantic Tailwind classes from theme (see theme-system.md)
1033
- - Follow patterns from \`docs/ai/edges/patterns.md\`
1034
-
1035
- ### Common Patterns
1036
-
1037
- **Import**: \`import { Button, TextField } from "@texturehq/edges"\`
1038
- **Styling**: Use semantic classes like \`bg-brand-primary\`, \`text-text-body\`
1039
- **Forms**: Wrap in \`<Form>\`, use validation with \`errorMessage\`
1040
- **Loading**: Show \`<Skeleton>\` during data fetching
1041
- **Responsive**: Use \`useBreakpoint()\` hook or responsive grid cols
1042
- `;
1043
-
1044
- const fragmentsDir = path.join(MONO_ROOT, ".agent-fragments");
1045
- ensureDir(fragmentsDir);
1046
- writeFile(path.join(fragmentsDir, "edges.md"), content);
1047
- }
1048
-
1049
- // ============================================================================
1050
- // INTEGRATIONS DOCS
1051
- // ============================================================================
1052
-
1053
- function generateIntegrationsDocs() {
1054
- console.log("\n🔗 Generating integrations docs...");
1055
- const rhfReadmeSrc = path.join(SRC_DIR, "integrations", "rhf", "README.md");
1056
- const rhfDocDest = path.join(DOCS_AI_EDGES, "integrations-rhf.md");
1057
- const content = read(rhfReadmeSrc);
1058
- if (content) {
1059
- writeFile(rhfDocDest, content);
1060
- } else {
1061
- console.log(" ℹ️ RHF README not found, skipping integrations-rhf.md");
1062
- }
1063
- }
1064
-
1065
- // ============================================================================
1066
- // MAIN
1067
- // ============================================================================
1068
-
1069
- async function main() {
1070
- console.log("🚀 Generating Edges AI Context...\n");
1071
-
1072
- // Ensure output directories exist
1073
- ensureDir(DOCS_AI_EDGES);
1074
- ensureDir(DIST_DIR);
1075
-
1076
- // Generate manifests
1077
- const componentsManifest = generateComponentsManifest();
1078
- const utilitiesManifest = generateUtilitiesManifest();
1079
-
1080
- // Parse theme
1081
- const colorVars = parseThemeColors();
1082
-
1083
- // Generate markdown documentation
1084
- generateComponentsReference(componentsManifest);
1085
- generateHooksReference(utilitiesManifest);
1086
- generateUtilitiesReference(utilitiesManifest);
1087
- generateThemeSystemDoc(colorVars);
1088
- generateEdgesReadme(componentsManifest, utilitiesManifest);
1089
- generateIntegrationsDocs();
1090
-
1091
- // Generate agent instruction fragment
1092
- generateEdgesFragment(componentsManifest, utilitiesManifest);
1093
-
1094
- // Merge all agent instruction fragments
1095
- console.log("\n🔀 Merging agent instruction fragments...");
1096
- try {
1097
- const output = execSync("ts-node scripts/merge-agent-instructions.ts", {
1098
- cwd: MONO_ROOT,
1099
- encoding: "utf8",
1100
- stdio: "pipe",
1101
- });
1102
- console.log(output);
1103
- } catch (_err) {
1104
- console.log(
1105
- "⚠️ Note: Could not merge agent instructions (merge script may not be available yet)"
1106
- );
1107
- }
1108
-
1109
- console.log("\n✅ Edges AI context generation complete!");
1110
- console.log("\nGenerated files:");
1111
- console.log(" - dist/components.manifest.json");
1112
- console.log(" - dist/utilities.manifest.json");
1113
- console.log(" - docs/ai/edges/README.md");
1114
- console.log(" - docs/ai/edges/components-reference.md");
1115
- console.log(" - docs/ai/edges/hooks-reference.md");
1116
- console.log(" - docs/ai/edges/utilities-reference.md");
1117
- console.log(" - docs/ai/edges/theme-system.md");
1118
- console.log(" - .agent-fragments/edges.md");
1119
- console.log(" - .claude/instructions.md (merged)");
1120
- console.log(" - .codex/instructions.md (merged)");
1121
- console.log(" - .github/copilot-instructions.md (merged)");
1122
- }
1123
-
1124
- main().catch((err) => {
1125
- console.error("❌ Failed to generate AI context:", err);
1126
- process.exit(1);
1127
- });