@matteoaliano/forest-ui 0.3.6 → 0.4.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.
package/bin/sync.mjs CHANGED
@@ -3,60 +3,17 @@
3
3
  /**
4
4
  * forest-ui sync
5
5
  *
6
- * Copies guideline files into the AI-tool config directories
7
- * of the consuming project so coding assistants automatically follow
8
- * Forest UI conventions.
9
- *
10
- * Supported targets:
11
- * .claude/<name>.md — Claude Code
12
- * .cursor/rules/<name>.mdc — Cursor
13
- * .gemini/<name>.md — Gemini CLI
14
- * .antigravity/rules.md — Antigravity
15
- *
16
- * Guideline categories:
17
- * - FOREST_UI_GUIDELINES.md — Design system components, tokens, patterns
18
- * - FOREST_FE_GUIDELINES.md — Next.js frontend best practices
19
- * - FOREST_BE_GUIDELINES.md — FastAPI backend best practices
6
+ * Copies skill folders into the consuming project's .claude/skills/
7
+ * directory so Claude Code automatically discovers Forest UI preset rules.
20
8
  */
21
9
 
22
- import { readFileSync, writeFileSync, mkdirSync, existsSync, readdirSync, cpSync } from "node:fs";
23
- import { resolve, dirname, basename } from "node:path";
10
+ import { readFileSync, mkdirSync, existsSync, readdirSync, cpSync } from "node:fs";
11
+ import { resolve, dirname } from "node:path";
24
12
  import { fileURLToPath } from "node:url";
25
13
 
26
- const __filename = fileURLToPath(import.meta.url);
27
- const __dirname = dirname(__filename);
28
-
29
- const pkgPath = resolve(__dirname, "..", "package.json");
30
- const pkgVersion = JSON.parse(readFileSync(pkgPath, "utf-8")).version;
31
-
32
- const guidelinesDir = resolve(__dirname, "..", "guidelines");
14
+ const __dirname = dirname(fileURLToPath(import.meta.url));
33
15
  const skillsDir = resolve(__dirname, "..", "skills");
34
16
 
35
- // Static guideline entries
36
- const staticEntries = [
37
- {
38
- source: resolve(guidelinesDir, "FOREST_UI_GUIDELINES.md"),
39
- claudeName: "forest-ui.md",
40
- geminiName: "forest-ui.md",
41
- cursorName: "forest-ui.mdc",
42
- cursorDescription: "Forest UI Design System guidelines — components, tokens, and patterns",
43
- },
44
- {
45
- source: resolve(guidelinesDir, "FOREST_FE_GUIDELINES.md"),
46
- claudeName: "forest-fe.md",
47
- geminiName: "forest-fe.md",
48
- cursorName: "forest-fe.mdc",
49
- cursorDescription: "Next.js frontend best practices",
50
- },
51
- {
52
- source: resolve(guidelinesDir, "FOREST_BE_GUIDELINES.md"),
53
- claudeName: "forest-be.md",
54
- geminiName: "forest-be.md",
55
- cursorName: "forest-be.mdc",
56
- cursorDescription: "FastAPI backend requirements and best practices",
57
- },
58
- ];
59
-
60
17
  // Find the consuming project root (walk up until we find package.json)
61
18
  function findProjectRoot(startDir) {
62
19
  let dir = startDir;
@@ -75,122 +32,41 @@ function findProjectRoot(startDir) {
75
32
  }
76
33
 
77
34
  const projectRoot = findProjectRoot(process.cwd());
78
- const entries = [...staticEntries];
79
35
 
80
- console.log(`\n🌲 Forest UI — Syncing AI guidelines\n`);
81
- console.log(` Project root: ${projectRoot}`);
82
- console.log(` Found ${entries.length} guideline file(s)\n`);
36
+ console.log(`\n🌲 Forest UI — Syncing skills\n`);
37
+ console.log(` Project root: ${projectRoot}\n`);
83
38
 
84
39
  let synced = 0;
85
40
  let total = 0;
86
41
 
87
- for (const entry of entries) {
88
- if (!existsSync(entry.source)) {
89
- console.log(` ⚠️ ${basename(entry.source)} — not found, skipping`);
90
- continue;
91
- }
42
+ if (!existsSync(skillsDir)) {
43
+ console.log(` ⚠️ No skills directory found, nothing to sync.\n`);
44
+ process.exit(0);
45
+ }
92
46
 
93
- const raw = readFileSync(entry.source, "utf-8");
94
- const content = raw.replace(
95
- />\s*\*\*forest-ui v[^*]*\*\*/,
96
- `> **forest-ui v${pkgVersion}**`,
97
- );
47
+ const skillFolders = readdirSync(skillsDir, { withFileTypes: true })
48
+ .filter((d) => d.isDirectory())
49
+ .map((d) => d.name);
98
50
 
99
- // Claude Code target
100
- const claudeDir = resolve(projectRoot, ".claude");
101
- const claudePath = resolve(claudeDir, entry.claudeName);
102
- total++;
103
- try {
104
- mkdirSync(claudeDir, { recursive: true });
105
- writeFileSync(claudePath, content, "utf-8");
106
- console.log(` ✅ Claude Code → .claude/${entry.claudeName}`);
107
- synced++;
108
- } catch (err) {
109
- console.log(` ⚠️ Claude Code — .claude/${entry.claudeName} skipped (${err.message})`);
110
- }
111
-
112
- // Gemini target
113
- const geminiDir = resolve(projectRoot, ".gemini");
114
- const geminiPath = resolve(geminiDir, entry.geminiName);
115
- total++;
116
- try {
117
- mkdirSync(geminiDir, { recursive: true });
118
- writeFileSync(geminiPath, content, "utf-8");
119
- console.log(` ✅ Gemini → .gemini/${entry.geminiName}`);
120
-
121
- // Update .gemini/GEMINI.md to import all synced files
122
- const geminiMainPath = resolve(geminiDir, "GEMINI.md");
123
- const geminiMainContent = entries
124
- .map(e => `@${e.geminiName}`)
125
- .join("\n");
126
- writeFileSync(geminiMainPath, `# Forest UI Guidelines\n\n${geminiMainContent}\n`, "utf-8");
127
-
128
- synced++;
129
- } catch (err) {
130
- console.log(` ⚠️ Gemini — .gemini/${entry.geminiName} skipped (${err.message})`);
131
- }
51
+ if (skillFolders.length === 0) {
52
+ console.log(` ⚠️ No skill folders found, nothing to sync.\n`);
53
+ process.exit(0);
54
+ }
132
55
 
133
- // Antigravity target
134
- const antiDir = resolve(projectRoot, ".antigravity");
135
- const antiPath = resolve(antiDir, "rules.md");
136
- total++;
137
- try {
138
- mkdirSync(antiDir, { recursive: true });
139
- // Concatenate all content into rules.md or use imports if supported.
140
- // Using imports to keep it clean, pointing to the .gemini folder.
141
- const antiContent = entries
142
- .map(e => `@../.gemini/${e.geminiName}`)
143
- .join("\n");
144
- writeFileSync(antiPath, `# Forest UI — Antigravity Rules\n\n${antiContent}\n`, "utf-8");
145
- console.log(` ✅ Antigravity → .antigravity/rules.md`);
146
- synced++;
147
- } catch (err) {
148
- console.log(` ⚠️ Antigravity — .antigravity/rules.md skipped (${err.message})`);
149
- }
56
+ const claudeSkillsDir = resolve(projectRoot, ".claude", "skills");
150
57
 
151
- // Cursor target
152
- const cursorDir = resolve(projectRoot, ".cursor", "rules");
153
- const cursorPath = resolve(cursorDir, entry.cursorName);
58
+ for (const folder of skillFolders) {
59
+ const src = resolve(skillsDir, folder);
60
+ const dest = resolve(claudeSkillsDir, folder);
154
61
  total++;
155
62
  try {
156
- mkdirSync(cursorDir, { recursive: true });
157
- const cursorContent =
158
- `---\ndescription: ${entry.cursorDescription}\nglobs: **/*.{ts,tsx,js,jsx}\nalwaysApply: false\n---\n\n` +
159
- content;
160
- writeFileSync(cursorPath, cursorContent, "utf-8");
161
- console.log(` ✅ Cursor → .cursor/rules/${entry.cursorName}`);
63
+ mkdirSync(dest, { recursive: true });
64
+ cpSync(src, dest, { recursive: true });
65
+ console.log(` ✅ .claude/skills/${folder}/`);
162
66
  synced++;
163
67
  } catch (err) {
164
- console.log(` ⚠️ Cursor — .cursor/rules/${entry.cursorName} skipped (${err.message})`);
165
- }
166
- }
167
-
168
- // ─── Claude Code Skills ────────────────────────────────────────────────
169
- // Copy skill folders into .claude/skills/ for auto-discovery
170
-
171
- if (existsSync(skillsDir)) {
172
- const skillFolders = readdirSync(skillsDir, { withFileTypes: true })
173
- .filter((d) => d.isDirectory())
174
- .map((d) => d.name);
175
-
176
- if (skillFolders.length > 0) {
177
- console.log(` --- Claude Code Skills ---`);
178
- const claudeSkillsDir = resolve(projectRoot, ".claude", "skills");
179
-
180
- for (const folder of skillFolders) {
181
- const src = resolve(skillsDir, folder);
182
- const dest = resolve(claudeSkillsDir, folder);
183
- total++;
184
- try {
185
- mkdirSync(dest, { recursive: true });
186
- cpSync(src, dest, { recursive: true });
187
- console.log(` ✅ Skill → .claude/skills/${folder}/`);
188
- synced++;
189
- } catch (err) {
190
- console.log(` ⚠️ Skill — .claude/skills/${folder}/ skipped (${err.message})`);
191
- }
192
- }
68
+ console.log(` ⚠️ .claude/skills/${folder}/ skipped (${err.message})`);
193
69
  }
194
70
  }
195
71
 
196
- console.log(`\n Synced ${synced}/${total} targets.\n`);
72
+ console.log(`\n Synced ${synced}/${total} skills.\n`);
@@ -215,17 +215,6 @@ var misc = {
215
215
  950: "#0d2d3a"
216
216
  }
217
217
  };
218
- var radiusSharp = {
219
- xxs: 1,
220
- xs: 2,
221
- sm: 3,
222
- md: 4,
223
- lg: 5,
224
- xl: 6,
225
- "2xl": 8,
226
- "3xl": 10,
227
- "4xl": 12
228
- };
229
218
  var base = {
230
219
  white: "#ffffff",
231
220
  black: "#000000",
@@ -338,7 +327,8 @@ var focusRings = {
338
327
  error: "0px 0px 0px 4px #f044383d",
339
328
  errorShadowXs: "0px 1px 2px 0px #1018280d, 0px 0px 0px 4px #f044383d"
340
329
  };
341
- var fontFamily = '"Poppins", sans-serif';
330
+ var fontFamily = '"Aeonik", sans-serif';
331
+ var fontFamilyMono = '"Aeonik Mono", monospace';
342
332
  var fontSize = {
343
333
  xxs: 11,
344
334
  xs: 12,
@@ -694,18 +684,6 @@ function buildPalette(tokens) {
694
684
  chart: (_a = tokens.chartColors) != null ? _a : chartColors
695
685
  };
696
686
  }
697
- var palette = buildPalette({
698
- base,
699
- error,
700
- warning,
701
- success,
702
- info,
703
- text,
704
- bg,
705
- fg,
706
- border,
707
- chartColors
708
- });
709
687
 
710
688
  // src/theme/typography.ts
711
689
  function buildTypography(tokens) {
@@ -789,13 +767,6 @@ function buildTypography(tokens) {
789
767
  }
790
768
  };
791
769
  }
792
- var typography = buildTypography({
793
- fontFamily,
794
- display,
795
- fontSize,
796
- lineHeight,
797
- fontWeight
798
- });
799
770
 
800
771
  // src/theme/shadows.ts
801
772
  function buildShadows(tokens) {
@@ -853,9 +824,6 @@ function buildShadows(tokens) {
853
824
  // 24
854
825
  ];
855
826
  }
856
- var shadows2 = buildShadows({
857
- shadows
858
- });
859
827
 
860
828
  // src/theme/components.ts
861
829
  function buildComponents(tokens) {
@@ -948,7 +916,11 @@ function buildComponents(tokens) {
948
916
  MuiIconButton: {
949
917
  styleOverrides: {
950
918
  root: {
951
- borderRadius: radius5.md
919
+ borderRadius: radius5.md,
920
+ padding: 6,
921
+ "& .MuiSvgIcon-root": {
922
+ fontSize: 20
923
+ }
952
924
  }
953
925
  }
954
926
  },
@@ -978,6 +950,15 @@ function buildComponents(tokens) {
978
950
  variant: "outlined"
979
951
  }
980
952
  },
953
+ MuiFormControl: {
954
+ styleOverrides: {
955
+ root: {
956
+ "&:has(.Mui-disabled)": {
957
+ opacity: 0.5
958
+ }
959
+ }
960
+ }
961
+ },
981
962
  MuiInputBase: {
982
963
  styleOverrides: {
983
964
  input: {
@@ -995,20 +976,27 @@ function buildComponents(tokens) {
995
976
  root: {
996
977
  borderRadius: radius5.md,
997
978
  boxShadow: tokenShadows.xs,
998
- "&:hover .MuiOutlinedInput-notchedOutline": {
979
+ "&&:hover:not(.Mui-focused):not(.Mui-disabled) .MuiOutlinedInput-notchedOutline": {
999
980
  borderColor: border5.brand
1000
981
  },
1001
- "&.Mui-focused .MuiOutlinedInput-notchedOutline": {
982
+ "&&.Mui-focused .MuiOutlinedInput-notchedOutline": {
1002
983
  borderColor: border5.brandSolid,
1003
984
  borderWidth: 1,
1004
985
  boxShadow: "none"
1005
986
  },
1006
- "&.Mui-error .MuiOutlinedInput-notchedOutline": {
987
+ "&&.Mui-error:not(.Mui-focused) .MuiOutlinedInput-notchedOutline": {
1007
988
  borderColor: border5.error
1008
989
  },
1009
- "&.Mui-error.Mui-focused .MuiOutlinedInput-notchedOutline": {
990
+ "&&.Mui-error.Mui-focused .MuiOutlinedInput-notchedOutline": {
1010
991
  borderColor: border5.errorSolid,
1011
992
  boxShadow: "none"
993
+ },
994
+ "& .MuiInputAdornment-root .MuiIconButton-root": {
995
+ color: fg5.quaternary,
996
+ "&:hover": {
997
+ backgroundColor: bg5.primaryHover,
998
+ color: fg5.brandPrimary
999
+ }
1012
1000
  }
1013
1001
  },
1014
1002
  notchedOutline: {
@@ -1480,6 +1468,40 @@ function buildComponents(tokens) {
1480
1468
  }
1481
1469
  }
1482
1470
  },
1471
+ // ─── Tabs ──────────────────────────────────────────────────────────
1472
+ MuiTabs: {
1473
+ styleOverrides: {
1474
+ root: {
1475
+ minHeight: 48
1476
+ },
1477
+ indicator: {
1478
+ backgroundColor: fg5.brandPrimary,
1479
+ height: 2
1480
+ }
1481
+ }
1482
+ },
1483
+ MuiTab: {
1484
+ styleOverrides: {
1485
+ root: {
1486
+ minHeight: 48,
1487
+ height: 48,
1488
+ padding: "12px 16px",
1489
+ fontSize: 14,
1490
+ fontWeight: 600,
1491
+ lineHeight: "20px",
1492
+ textTransform: "none",
1493
+ color: text5.tertiary,
1494
+ borderRadius: `${radius5.md}px ${radius5.md}px 0 0`,
1495
+ "&:hover": {
1496
+ backgroundColor: bg5.primaryHover
1497
+ },
1498
+ "&.Mui-selected": {
1499
+ color: fg5.brandPrimary,
1500
+ backgroundColor: bg5.brandPrimary
1501
+ }
1502
+ }
1503
+ }
1504
+ },
1483
1505
  // ─── Chip ───────────────────────────────────────────────────────
1484
1506
  MuiChip: {
1485
1507
  styleOverrides: {
@@ -1500,15 +1522,15 @@ function buildComponents(tokens) {
1500
1522
  },
1501
1523
  colorDefault: {
1502
1524
  "&.MuiChip-filled": {
1503
- backgroundColor: gray[50],
1504
- color: gray[700],
1505
- "& .MuiChip-deleteIcon": { color: gray[400] }
1525
+ backgroundColor: tokens.bg.secondaryHover,
1526
+ color: tokens.fg.secondary,
1527
+ "& .MuiChip-deleteIcon": { color: tokens.fg.quinary }
1506
1528
  }
1507
1529
  },
1508
1530
  colorPrimary: {
1509
1531
  "&.MuiChip-filled": {
1510
1532
  backgroundColor: tokens.bg.brandPrimary,
1511
- color: tokens.text.brandPrimary,
1533
+ color: tokens.fg.brandPrimary,
1512
1534
  "& .MuiChip-deleteIcon": { color: tokens.fg.brandPrimary }
1513
1535
  }
1514
1536
  },
@@ -1598,13 +1620,16 @@ function buildComponents(tokens) {
1598
1620
  styleOverrides: {
1599
1621
  root: {
1600
1622
  backgroundColor: bg5.secondary,
1601
- borderBottom: `1px solid ${border5.secondary}`
1623
+ "& .MuiTableRow-root:last-child .MuiTableCell-root": {
1624
+ borderBottom: `1px solid ${border5.secondary}`
1625
+ }
1602
1626
  }
1603
1627
  }
1604
1628
  },
1605
1629
  MuiTableRow: {
1606
1630
  styleOverrides: {
1607
1631
  root: {
1632
+ backgroundColor: bg5.secondary,
1608
1633
  "&:last-child td, &:last-child th": {
1609
1634
  border: 0
1610
1635
  },
@@ -1960,6 +1985,7 @@ function buildComponents(tokens) {
1960
1985
  }
1961
1986
  },
1962
1987
  row: {
1988
+ backgroundColor: bg5.secondary,
1963
1989
  "&:hover": {
1964
1990
  backgroundColor: bg5.primaryHover
1965
1991
  },
@@ -2002,7 +2028,7 @@ function buildComponents(tokens) {
2002
2028
  "& .MuiOutlinedInput-notchedOutline": {
2003
2029
  borderColor: border5.primary
2004
2030
  },
2005
- "&:hover .MuiOutlinedInput-notchedOutline": {
2031
+ "&:hover:not(:has(.Mui-disabled)) .MuiOutlinedInput-notchedOutline": {
2006
2032
  borderColor: border5.brand
2007
2033
  },
2008
2034
  "& .Mui-focused .MuiOutlinedInput-notchedOutline": {
@@ -2012,7 +2038,6 @@ function buildComponents(tokens) {
2012
2038
  },
2013
2039
  "& .MuiInputAdornment-root .MuiIconButton-root": {
2014
2040
  color: fg5.quaternary,
2015
- padding: 4,
2016
2041
  "&:hover": {
2017
2042
  backgroundColor: bg5.primaryHover,
2018
2043
  color: fg5.brandPrimary
@@ -2164,21 +2189,6 @@ function buildComponents(tokens) {
2164
2189
  }
2165
2190
  };
2166
2191
  }
2167
- var components = buildComponents({
2168
- base,
2169
- error,
2170
- warning,
2171
- success,
2172
- info,
2173
- radius,
2174
- focusRings,
2175
- shadows,
2176
- text,
2177
- bg,
2178
- fg,
2179
- border,
2180
- buttonColors,
2181
- sliderColors});
2182
2192
 
2183
2193
  // src/theme/buildTheme.ts
2184
2194
  function buildThemeOptions(tokens) {
@@ -2455,6 +2465,7 @@ var forestExternalPreset = {
2455
2465
  shadows,
2456
2466
  focusRings: focusRings2,
2457
2467
  fontFamily: '"Aeonik", sans-serif',
2468
+ fontFamilyMono: '"Aeonik Mono", monospace',
2458
2469
  fontSize,
2459
2470
  lineHeight,
2460
2471
  fontWeight,
@@ -2595,7 +2606,7 @@ var border3 = {
2595
2606
  tertiary: gray[100],
2596
2607
  disabled: gray[300],
2597
2608
  disabledSubtle: gray[200],
2598
- brand: colors.brand[300],
2609
+ brand: gray[400],
2599
2610
  brandSolid: colors.brand[600],
2600
2611
  brandSolidAlt: colors.brand[600],
2601
2612
  error: error[300],
@@ -2755,6 +2766,7 @@ var forestAgencyPreset = {
2755
2766
  shadows,
2756
2767
  focusRings: focusRings3,
2757
2768
  fontFamily: '"Aeonik", sans-serif',
2769
+ fontFamilyMono: '"Aeonik Mono", monospace',
2758
2770
  fontSize,
2759
2771
  lineHeight,
2760
2772
  fontWeight,
@@ -3038,6 +3050,7 @@ var forestInternalPreset = {
3038
3050
  shadows,
3039
3051
  focusRings: focusRings4,
3040
3052
  fontFamily: '"Aeonik", sans-serif',
3053
+ fontFamilyMono: '"Aeonik Mono", monospace',
3041
3054
  fontSize,
3042
3055
  lineHeight,
3043
3056
  fontWeight,
@@ -3082,6 +3095,6 @@ var agencyTokens = forestAgencyPreset.variants.light.tokens;
3082
3095
  var forestTheme = buildTheme(agencyTokens);
3083
3096
  var forestThemeOptions = buildThemeOptions(agencyTokens);
3084
3097
 
3085
- export { alpha, avatarColors, base, bg, border, breadcrumbColors, buildTheme, buildThemeOptions, buttonColors, chartColors, components, container, display, error, fg, focusRings, fontFamily, fontSize, fontWeight, forestAgencyPreset, forestExternalPreset, forestInternalPreset, forestTheme, forestThemeOptions, getAvailablePresets, getPreset, gray, iconColors, info, lineHeight, magenta, misc, navColors, palette, radius, radiusSharp, red, registerPreset, shadows, shadows2, sliderColors, spacing, success, text, toggleColors, typography, violet, warning, widths };
3086
- //# sourceMappingURL=chunk-G2ZXCZKP.mjs.map
3087
- //# sourceMappingURL=chunk-G2ZXCZKP.mjs.map
3098
+ export { alpha, avatarColors, base, bg, border, breadcrumbColors, buildTheme, buildThemeOptions, buttonColors, chartColors, container, display, error, fg, focusRings, fontFamily, fontFamilyMono, fontSize, fontWeight, forestAgencyPreset, forestExternalPreset, forestInternalPreset, forestTheme, forestThemeOptions, getAvailablePresets, getPreset, gray, iconColors, info, lineHeight, magenta, misc, navColors, radius, red, registerPreset, shadows, sliderColors, spacing, success, text, toggleColors, violet, warning, widths };
3099
+ //# sourceMappingURL=chunk-IWYN6REP.mjs.map
3100
+ //# sourceMappingURL=chunk-IWYN6REP.mjs.map