@texturehq/edges 1.29.0 → 1.30.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/dist/server.d.cts CHANGED
@@ -1,10 +1,9 @@
1
- export { A as ActionItem, a as ActionMenuProps, b as AppShell, c as AppShellProps, d as AvatarProps, B as BadgeProps, e as BaseDataPoint, C as ChartMargin, f as CodeEditorProps, g as CodeLanguage, h as CodeTheme, E as ENTITY_CONFIG, i as EntityConfig, j as EntityType, H as Heading, I as InteractiveMapProps, L as Loader, k as Logo, M as MapPoint, l as MeterProps, S as SegmentOption, m as SegmentedControlProps, n as SideNav, o as SideNavItem, p as SideNavProps, q as StaticMapProps, T as TextLink, r as TooltipData, s as TooltipSeries, t as TopNav, u as TopNavProps, Y as YFormatSettings, v as YFormatType, w as clearColorCache, x as createCategoryColorMap, y as createXScale, z as createYScale, D as defaultMargin, F as getContrastingTextColor, G as getDefaultChartColor, J as getDefaultColors, K as getEntityConfig, N as getEntityIcon, O as getEntityLabel, P as getResolvedColor, Q as getThemeCategoricalColors, R as getYFormatSettings, U as isLightColor } from './colors-CkJf6_Bz.cjs';
1
+ export { A as ActionItem, a as ActionMenuProps, b as AppShell, c as AppShellProps, d as AvatarProps, B as BadgeProps, e as BaseDataPoint, C as ChartMargin, f as CodeEditorProps, g as CodeLanguage, h as CodeTheme, E as ENTITY_CONFIG, i as EntityConfig, j as EntityType, H as Heading, I as InteractiveMapProps, L as Loader, k as Logo, M as MapPoint, l as MeterProps, S as SegmentOption, m as SegmentedControlProps, n as SideNav, o as SideNavItem, p as SideNavProps, q as StaticMapProps, T as TextLink, r as TooltipData, s as TooltipSeries, t as TopNav, u as TopNavProps, Y as YFormatSettings, v as YFormatType, w as clearColorCache, x as createCategoryColorMap, y as createXScale, z as createYScale, D as defaultMargin, F as getContrastingTextColor, G as getDefaultChartColor, J as getDefaultColors, K as getEntityConfig, N as getEntityIcon, O as getEntityLabel, P as getResolvedColor, Q as getThemeCategoricalColors, R as getYFormatSettings, U as isLightColor } from './colors-BgVu6fze.cjs';
2
2
  export { BreadcrumbProps, BreadcrumbsProps } from 'react-aria-components';
3
3
  export { D as DateFieldProps, F as FileUploadProps, R as RichTextEditorProps } from './RichTextEditor-CxrunTg7.cjs';
4
4
  import * as react_jsx_runtime from 'react/jsx-runtime';
5
5
  import { IconProps } from '@phosphor-icons/react';
6
6
  import React__default from 'react';
7
- import '@visx/vendor/d3-scale';
8
7
  import 'd3-scale';
9
8
  import 'react-map-gl';
10
9
 
package/dist/server.d.ts CHANGED
@@ -1,10 +1,9 @@
1
- export { A as ActionItem, a as ActionMenuProps, b as AppShell, c as AppShellProps, d as AvatarProps, B as BadgeProps, e as BaseDataPoint, C as ChartMargin, f as CodeEditorProps, g as CodeLanguage, h as CodeTheme, E as ENTITY_CONFIG, i as EntityConfig, j as EntityType, H as Heading, I as InteractiveMapProps, L as Loader, k as Logo, M as MapPoint, l as MeterProps, S as SegmentOption, m as SegmentedControlProps, n as SideNav, o as SideNavItem, p as SideNavProps, q as StaticMapProps, T as TextLink, r as TooltipData, s as TooltipSeries, t as TopNav, u as TopNavProps, Y as YFormatSettings, v as YFormatType, w as clearColorCache, x as createCategoryColorMap, y as createXScale, z as createYScale, D as defaultMargin, F as getContrastingTextColor, G as getDefaultChartColor, J as getDefaultColors, K as getEntityConfig, N as getEntityIcon, O as getEntityLabel, P as getResolvedColor, Q as getThemeCategoricalColors, R as getYFormatSettings, U as isLightColor } from './colors-DR1jXZhL.js';
1
+ export { A as ActionItem, a as ActionMenuProps, b as AppShell, c as AppShellProps, d as AvatarProps, B as BadgeProps, e as BaseDataPoint, C as ChartMargin, f as CodeEditorProps, g as CodeLanguage, h as CodeTheme, E as ENTITY_CONFIG, i as EntityConfig, j as EntityType, H as Heading, I as InteractiveMapProps, L as Loader, k as Logo, M as MapPoint, l as MeterProps, S as SegmentOption, m as SegmentedControlProps, n as SideNav, o as SideNavItem, p as SideNavProps, q as StaticMapProps, T as TextLink, r as TooltipData, s as TooltipSeries, t as TopNav, u as TopNavProps, Y as YFormatSettings, v as YFormatType, w as clearColorCache, x as createCategoryColorMap, y as createXScale, z as createYScale, D as defaultMargin, F as getContrastingTextColor, G as getDefaultChartColor, J as getDefaultColors, K as getEntityConfig, N as getEntityIcon, O as getEntityLabel, P as getResolvedColor, Q as getThemeCategoricalColors, R as getYFormatSettings, U as isLightColor } from './colors-soT9-oWB.js';
2
2
  export { BreadcrumbProps, BreadcrumbsProps } from 'react-aria-components';
3
3
  export { D as DateFieldProps, F as FileUploadProps, R as RichTextEditorProps } from './RichTextEditor-CxrunTg7.js';
4
4
  import * as react_jsx_runtime from 'react/jsx-runtime';
5
5
  import { IconProps } from '@phosphor-icons/react';
6
6
  import React__default from 'react';
7
- import '@visx/vendor/d3-scale';
8
7
  import 'd3-scale';
9
8
  import 'react-map-gl';
10
9
 
package/dist/styles.css CHANGED
@@ -1,4 +1,4 @@
1
- /*! tailwindcss v4.1.18 | MIT License | https://tailwindcss.com */
1
+ /*! tailwindcss v4.2.2 | MIT License | https://tailwindcss.com */
2
2
  @layer properties;
3
3
  :root, :host {
4
4
  --color-white: #ffffff;
@@ -79,7 +79,7 @@
79
79
  --motion-opacity-disabled: 0.5;
80
80
  --motion-scale-hover: 1.02;
81
81
  --motion-scale-press: 0.97;
82
- --spacing-0: 0px;
82
+ --spacing-0: 0;
83
83
  --spacing-1: 0.25rem;
84
84
  --spacing-2: 0.5rem;
85
85
  --spacing-3: 0.75rem;
@@ -105,7 +105,7 @@
105
105
  --spacing-72: 18rem;
106
106
  --spacing-80: 20rem;
107
107
  --spacing-96: 24rem;
108
- --radius-none: 0px;
108
+ --radius-none: 0;
109
109
  --radius-sm: 0.25rem;
110
110
  --radius-md: 0.375rem;
111
111
  --radius-lg: 0.5rem;
@@ -477,7 +477,7 @@
477
477
  --motion-opacity-disabled: 0.5;
478
478
  --motion-scale-hover: 1.02;
479
479
  --motion-scale-press: 0.97;
480
- --spacing-0: 0px;
480
+ --spacing-0: 0;
481
481
  --spacing-1: 0.25rem;
482
482
  --spacing-2: 0.5rem;
483
483
  --spacing-3: 0.75rem;
@@ -507,7 +507,7 @@
507
507
  --spacing-72: 18rem;
508
508
  --spacing-80: 20rem;
509
509
  --spacing-96: 24rem;
510
- --radius-none: 0px;
510
+ --radius-none: 0;
511
511
  --radius-xs: 0.125rem;
512
512
  --radius-sm: 0.25rem;
513
513
  --radius-md: 0.375rem;
@@ -537,7 +537,7 @@
537
537
  --font-lineheight-loose: 2;
538
538
  --font-letterspacing-tighter: -0.05em;
539
539
  --font-letterspacing-tight: -0.025em;
540
- --font-letterspacing-normal: 0em;
540
+ --font-letterspacing-normal: 0;
541
541
  --font-letterspacing-wide: 0.025em;
542
542
  --font-letterspacing-wider: 0.05em;
543
543
  --font-letterspacing-widest: 0.1em;
@@ -1612,9 +1612,15 @@
1612
1612
  .inset-y-\[2px\] {
1613
1613
  inset-block: 2px;
1614
1614
  }
1615
+ .start {
1616
+ inset-inline-start: var(--spacing);
1617
+ }
1615
1618
  .start-1 {
1616
1619
  inset-inline-start: var(--spacing-1);
1617
1620
  }
1621
+ .end {
1622
+ inset-inline-end: var(--spacing);
1623
+ }
1618
1624
  .-top-2 {
1619
1625
  top: calc(var(--spacing-2) * -1);
1620
1626
  }
@@ -1625,7 +1631,7 @@
1625
1631
  top: var(--spacing-0);
1626
1632
  }
1627
1633
  .top-1\/2 {
1628
- top: calc(1/2 * 100%);
1634
+ top: calc(1 / 2 * 100%);
1629
1635
  }
1630
1636
  .top-2 {
1631
1637
  top: var(--spacing-2);
@@ -1718,7 +1724,7 @@
1718
1724
  left: var(--spacing-0);
1719
1725
  }
1720
1726
  .left-1\/2 {
1721
- left: calc(1/2 * 100%);
1727
+ left: calc(1 / 2 * 100%);
1722
1728
  }
1723
1729
  .left-2 {
1724
1730
  left: var(--spacing-2);
@@ -2021,7 +2027,7 @@
2021
2027
  height: calc(var(--spacing) * 1.5);
2022
2028
  }
2023
2029
  .h-1\/2 {
2024
- height: calc(1/2 * 100%);
2030
+ height: calc(1 / 2 * 100%);
2025
2031
  }
2026
2032
  .h-2 {
2027
2033
  height: var(--spacing-2);
@@ -2173,9 +2179,6 @@
2173
2179
  .max-h-60 {
2174
2180
  max-height: var(--spacing-60);
2175
2181
  }
2176
- .max-h-\[90vh\] {
2177
- max-height: 90vh;
2178
- }
2179
2182
  .max-h-\[300px\] {
2180
2183
  max-height: 300px;
2181
2184
  }
@@ -2237,7 +2240,7 @@
2237
2240
  width: calc(var(--spacing) * 1.5);
2238
2241
  }
2239
2242
  .w-1\/2 {
2240
- width: calc(1/2 * 100%);
2243
+ width: calc(1 / 2 * 100%);
2241
2244
  }
2242
2245
  .w-2 {
2243
2246
  width: var(--spacing-2);
@@ -2246,7 +2249,7 @@
2246
2249
  width: calc(var(--spacing) * 2.5);
2247
2250
  }
2248
2251
  .w-2\/3 {
2249
- width: calc(2/3 * 100%);
2252
+ width: calc(2 / 3 * 100%);
2250
2253
  }
2251
2254
  .w-3 {
2252
2255
  width: var(--spacing-3);
@@ -2255,7 +2258,7 @@
2255
2258
  width: calc(var(--spacing) * 3.5);
2256
2259
  }
2257
2260
  .w-3\/4 {
2258
- width: calc(3/4 * 100%);
2261
+ width: calc(3 / 4 * 100%);
2259
2262
  }
2260
2263
  .w-4 {
2261
2264
  width: var(--spacing-4);
@@ -2549,7 +2552,7 @@
2549
2552
  transform-origin: 100% 0;
2550
2553
  }
2551
2554
  .-translate-x-1\/2 {
2552
- --tw-translate-x: calc(calc(1/2 * 100%) * -1);
2555
+ --tw-translate-x: calc(calc(1 / 2 * 100%) * -1);
2553
2556
  translate: var(--tw-translate-x) var(--tw-translate-y);
2554
2557
  }
2555
2558
  .-translate-x-full {
@@ -2565,7 +2568,7 @@
2565
2568
  translate: var(--tw-translate-x) var(--tw-translate-y);
2566
2569
  }
2567
2570
  .-translate-y-1\/2 {
2568
- --tw-translate-y: calc(calc(1/2 * 100%) * -1);
2571
+ --tw-translate-y: calc(calc(1 / 2 * 100%) * -1);
2569
2572
  translate: var(--tw-translate-x) var(--tw-translate-y);
2570
2573
  }
2571
2574
  .rotate-0 {
@@ -4074,9 +4077,6 @@
4074
4077
  .pt-8 {
4075
4078
  padding-top: var(--spacing-8);
4076
4079
  }
4077
- .pt-\[15vh\] {
4078
- padding-top: 15vh;
4079
- }
4080
4080
  .pr-0 {
4081
4081
  padding-right: var(--spacing-0);
4082
4082
  }
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "1.29.0",
2
+ "version": "1.30.0",
3
3
  "categories": {
4
4
  "hooks": {
5
5
  "description": "React hooks for common functionality like breakpoints, debouncing, local storage, and media queries",
@@ -94,6 +94,13 @@
94
94
  "description": "Hook for exporting DataTable data as CSV. Provides a stable callback for triggering exports with memoized dependencies. For client-side DataTables where all data is in the browser. For server-side exports, use the `onExport` callback pattern instead.",
95
95
  "category": "hooks",
96
96
  "file": "hooks/useTableExport.ts"
97
+ },
98
+ {
99
+ "name": "useVisualViewportHeight",
100
+ "type": "function",
101
+ "description": "useVisualViewportHeight Tracks `window.visualViewport` to handle the iOS soft keyboard. On iOS Safari, `position: fixed` elements are anchored to the layout viewport (full page size) and don't move when the keyboard opens. This hook returns: - `height`: the visible viewport height (shrinks when keyboard opens) - `keyboardOffset`: how many px to translate a fixed element upward to clear the keyboard Returns `null` on SSR or in browsers that don't support `visualViewport`.",
102
+ "category": "hooks",
103
+ "file": "hooks/useVisualViewportHeight.ts"
97
104
  }
98
105
  ]
99
106
  },
@@ -1049,6 +1056,13 @@
1049
1056
  "description": "Utility to check if export is supported in the current environment",
1050
1057
  "category": "charts",
1051
1058
  "file": "utils/chartExport.ts"
1059
+ },
1060
+ {
1061
+ "name": "resolveCSSVariables",
1062
+ "type": "function",
1063
+ "description": "Resolves CSS custom properties (e.g. `var(--color-brand-primary)`) in a cloned SVG by reading computed styles from the original DOM-attached SVG. When an SVG is cloned and serialized for export (PNG/SVG), it loses access to the page's CSS context, so any `var(--*)` values become empty/transparent. This function walks both trees in parallel and replaces unresolved variables with their computed values.",
1064
+ "category": "charts",
1065
+ "file": "utils/chartExport.ts"
1052
1066
  }
1053
1067
  ]
1054
1068
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@texturehq/edges",
3
- "version": "1.29.0",
3
+ "version": "1.30.0",
4
4
  "author": "Nicholas Brown <nick@texturehq.com>",
5
5
  "description": "A shared component library for Texture",
6
6
  "type": "module",
@@ -133,7 +133,7 @@
133
133
  "tailwind-merge": "^3.2.0"
134
134
  },
135
135
  "devDependencies": {
136
- "@biomejs/biome": "^2.2.4",
136
+ "@biomejs/biome": "2.4.9",
137
137
  "@hookform/resolvers": "^3.9.0",
138
138
  "@storybook/addon-essentials": "^8.6.14",
139
139
  "@storybook/addon-interactions": "^8.6.14",
@@ -88,10 +88,7 @@ function findComponentFile(relPath) {
88
88
  function findStorybookFile(relPath) {
89
89
  const base = path.join(SRC_DIR, "components", relPath);
90
90
  const componentName = relPath.split("/").pop();
91
- const candidates = [
92
- path.join(base, `${componentName}.stories.tsx`),
93
- path.join(base, `${componentName}.stories.ts`),
94
- ];
91
+ const candidates = [path.join(base, `${componentName}.stories.tsx`), path.join(base, `${componentName}.stories.ts`)];
95
92
  for (const f of candidates) {
96
93
  if (fs.existsSync(f)) return f;
97
94
  }
@@ -153,15 +150,11 @@ function extractProps(content, componentName) {
153
150
  );
154
151
  let m = ifaceRe.exec(content);
155
152
  if (!m) {
156
- const anyIfaceRe =
157
- /export\s+interface\s+([A-Za-z0-9_]+Props)\s*(?:extends\s+[^{]+)?\{([\s\S]*?)\}/m;
153
+ const anyIfaceRe = /export\s+interface\s+([A-Za-z0-9_]+Props)\s*(?:extends\s+[^{]+)?\{([\s\S]*?)\}/m;
158
154
  m = anyIfaceRe.exec(content);
159
155
  }
160
156
  if (!m) {
161
- const typeRe = new RegExp(
162
- `export\\s+type\\s+${componentName}Props\\s*=\\s*(?:[^{&]+&\s*)?([\\s\\S]*?)\\}`,
163
- "m"
164
- );
157
+ const typeRe = new RegExp(`export\\s+type\\s+${componentName}Props\\s*=\\s*(?:[^{&]+&\s*)?([\\s\\S]*?)\\}`, "m");
165
158
  m = typeRe.exec(content);
166
159
  }
167
160
  const props = [];
@@ -302,9 +295,7 @@ function generateComponentsManifest() {
302
295
  ensureDir(DIST_DIR);
303
296
  writeFile(path.join(DIST_DIR, "components.manifest.json"), JSON.stringify(manifest, null, 2));
304
297
 
305
- console.log(
306
- ` Found ${components.length} components in ${Object.keys(categories).length} categories`
307
- );
298
+ console.log(` Found ${components.length} components in ${Object.keys(categories).length} categories`);
308
299
  return manifest;
309
300
  }
310
301
 
@@ -314,10 +305,7 @@ function generateComponentsManifest() {
314
305
 
315
306
  function extractJSDoc(content, functionName) {
316
307
  const patterns = [
317
- new RegExp(
318
- `\\/\\*\\*([^*]|\\*(?!\\/))*\\*\\/\\s*export\\s+(?:async\\s+)?function\\s+${functionName}\\b`,
319
- "s"
320
- ),
308
+ new RegExp(`\\/\\*\\*([^*]|\\*(?!\\/))*\\*\\/\\s*export\\s+(?:async\\s+)?function\\s+${functionName}\\b`, "s"),
321
309
  new RegExp(`\\/\\*\\*([^*]|\\*(?!\\/))*\\*\\/\\s*export\\s+const\\s+${functionName}\\s*=`, "s"),
322
310
  ];
323
311
 
@@ -326,9 +314,7 @@ function extractJSDoc(content, functionName) {
326
314
  if (match) {
327
315
  const jsdocMatch = match[0].match(/\/\*\*([\s\S]*?)\*\//);
328
316
  if (jsdocMatch) {
329
- const lines = jsdocMatch[1]
330
- .split("\n")
331
- .map((line) => line.replace(/^\s*\*\s?/, "").trim());
317
+ const lines = jsdocMatch[1].split("\n").map((line) => line.replace(/^\s*\*\s?/, "").trim());
332
318
 
333
319
  // Extract only the main description (before any @tags)
334
320
  const descriptionLines = [];
@@ -406,10 +392,7 @@ function extractExportsFromFile(filePath) {
406
392
  while ((match = constPattern.exec(content))) {
407
393
  if (!isInComment(match.index)) {
408
394
  const name = match[1];
409
- const afterMatch = content.slice(
410
- match.index + match[0].length,
411
- match.index + match[0].length + 50
412
- );
395
+ const afterMatch = content.slice(match.index + match[0].length, match.index + match[0].length + 50);
413
396
  const isFunction = afterMatch.includes("=>") || afterMatch.includes("function");
414
397
 
415
398
  utilities.push({
@@ -517,17 +500,12 @@ function generateUtilitiesManifest() {
517
500
  };
518
501
  }
519
502
 
520
- const totalUtilities = Object.values(manifest.categories).reduce(
521
- (sum, cat) => sum + cat.utilities.length,
522
- 0
523
- );
503
+ const totalUtilities = Object.values(manifest.categories).reduce((sum, cat) => sum + cat.utilities.length, 0);
524
504
 
525
505
  ensureDir(DIST_DIR);
526
506
  writeFile(path.join(DIST_DIR, "utilities.manifest.json"), JSON.stringify(manifest, null, 2));
527
507
 
528
- console.log(
529
- ` Found ${totalUtilities} utilities in ${Object.keys(manifest.categories).length} categories`
530
- );
508
+ console.log(` Found ${totalUtilities} utilities in ${Object.keys(manifest.categories).length} categories`);
531
509
  return manifest;
532
510
  }
533
511
 
@@ -1101,9 +1079,7 @@ async function main() {
1101
1079
  });
1102
1080
  console.log(output);
1103
1081
  } catch (_err) {
1104
- console.log(
1105
- "⚠️ Note: Could not merge agent instructions (merge script may not be available yet)"
1106
- );
1082
+ console.log("⚠️ Note: Could not merge agent instructions (merge script may not be available yet)");
1107
1083
  }
1108
1084
 
1109
1085
  console.log("\n✅ Edges AI context generation complete!");
@@ -132,8 +132,7 @@ const setupCursorRules = () => {
132
132
  for (const c of manifest.components || []) {
133
133
  lines.push(`- ${c.name}`);
134
134
  if (c.importRoot) lines.push(` - Import: \`import { ${c.name} } from "${c.importRoot}"\``);
135
- if (c.importPath)
136
- lines.push(` - Subpath: \`import { ${c.name} } from "${c.importPath}"\``);
135
+ if (c.importPath) lines.push(` - Subpath: \`import { ${c.name} } from "${c.importPath}"\``);
137
136
  if (c.props && c.props.length) {
138
137
  const propNames = c.props
139
138
  .slice(0, 8)
@@ -34,18 +34,9 @@ const setupCursorRules = () => {
34
34
  }
35
35
  });
36
36
 
37
- const cursorTemplatesDir = path.join(
38
- path.dirname(new URL(import.meta.url).pathname),
39
- "../templates/cursor-rules"
40
- );
41
- const claudeTemplatesDir = path.join(
42
- path.dirname(new URL(import.meta.url).pathname),
43
- "../templates/claude-rules"
44
- );
45
- const codexTemplatesDir = path.join(
46
- path.dirname(new URL(import.meta.url).pathname),
47
- "../templates/codex-rules"
48
- );
37
+ const cursorTemplatesDir = path.join(path.dirname(new URL(import.meta.url).pathname), "../templates/cursor-rules");
38
+ const claudeTemplatesDir = path.join(path.dirname(new URL(import.meta.url).pathname), "../templates/claude-rules");
39
+ const codexTemplatesDir = path.join(path.dirname(new URL(import.meta.url).pathname), "../templates/codex-rules");
49
40
  const cursorDir = path.join(cwd, ".cursor");
50
41
  const rulesDir = path.join(cursorDir, "rules");
51
42
 
@@ -55,9 +46,7 @@ const setupCursorRules = () => {
55
46
 
56
47
  // Setup Cursor rules
57
48
  if (fs.existsSync(cursorTemplatesDir)) {
58
- const templateFiles = fs
59
- .readdirSync(cursorTemplatesDir)
60
- .filter((file) => file.endsWith(".mdc"));
49
+ const templateFiles = fs.readdirSync(cursorTemplatesDir).filter((file) => file.endsWith(".mdc"));
61
50
 
62
51
  if (templateFiles.length > 0) {
63
52
  let copiedCount = 0;
@@ -80,18 +69,16 @@ const setupCursorRules = () => {
80
69
 
81
70
  // Setup Claude rules
82
71
  if (fs.existsSync(claudeTemplatesDir)) {
83
- const _claudeTemplateFiles = fs
84
- .readdirSync(claudeTemplatesDir)
85
- .filter((file) => file.endsWith(".md"));
72
+ const _claudeTemplateFiles = fs.readdirSync(claudeTemplatesDir).filter((file) => file.endsWith(".md"));
86
73
 
87
74
  // Claude template processing is done later with component injection (lines 221-236)
88
75
  // so we don't need to copy templates here
89
76
  }
90
77
 
91
78
  // Also render edges-components.mdc from manifest when present
92
- const manifestPath = [
93
- path.join(cwd, "node_modules/@texturehq/edges/dist/components.manifest.json"),
94
- ].find((p) => fs.existsSync(p));
79
+ const manifestPath = [path.join(cwd, "node_modules/@texturehq/edges/dist/components.manifest.json")].find((p) =>
80
+ fs.existsSync(p)
81
+ );
95
82
 
96
83
  // Track what we generated for summary
97
84
  let cursorComponentCount = 0;
@@ -153,9 +140,7 @@ const setupCursorRules = () => {
153
140
 
154
141
  fs.writeFileSync(cursorOutPath, cursorLines.join("\n"), "utf8");
155
142
  cursorComponentCount = manifest.components?.length || 0;
156
- console.log(
157
- `✅ Wrote .cursor/rules/edges-components.mdc from manifest (${cursorComponentCount} components)`
158
- );
143
+ console.log(`✅ Wrote .cursor/rules/edges-components.mdc from manifest (${cursorComponentCount} components)`);
159
144
 
160
145
  // Generate Claude components list
161
146
  const claudeLines = [];
@@ -207,9 +192,7 @@ const setupCursorRules = () => {
207
192
  const claudeOutPath = path.join(claudeDir, "edges.md");
208
193
  fs.writeFileSync(claudeOutPath, claudeContent, "utf8");
209
194
  claudeComponentCount = manifest.components?.length || 0;
210
- console.log(
211
- `✅ Wrote .claude/edges.md with components list (${claudeComponentCount} components)`
212
- );
195
+ console.log(`✅ Wrote .claude/edges.md with components list (${claudeComponentCount} components)`);
213
196
  }
214
197
 
215
198
  // Generate Codex docs using template
@@ -224,9 +207,7 @@ const setupCursorRules = () => {
224
207
  const codexOutPath = path.join(codexDir, "edges.md");
225
208
  fs.writeFileSync(codexOutPath, codexContent, "utf8");
226
209
  codexComponentCount = manifest.components?.length || 0;
227
- console.log(
228
- `✅ Wrote .codex/edges.md with components list (${codexComponentCount} components)`
229
- );
210
+ console.log(`✅ Wrote .codex/edges.md with components list (${codexComponentCount} components)`);
230
211
  }
231
212
  } catch (err) {
232
213
  console.log("⚠️ Failed to read components.manifest.json:", err.message);