@matteoaliano/forest-ui 0.8.9 → 1.1.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/README.md +56 -12
- package/bin/sync.mjs +37 -72
- package/dist/{chunk-2XQSGP7X.mjs → chunk-XJYBU6LC.mjs} +1768 -1885
- package/dist/chunk-XJYBU6LC.mjs.map +1 -0
- package/dist/index.d.mts +198 -381
- package/dist/index.d.ts +198 -381
- package/dist/index.js +2344 -2565
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +683 -794
- package/dist/index.mjs.map +1 -1
- package/dist/theme.d.mts +14 -17
- package/dist/theme.d.ts +14 -17
- package/dist/theme.js +1755 -1875
- package/dist/theme.js.map +1 -1
- package/dist/theme.mjs +1 -7
- package/package.json +2 -2
- package/skills/forest-alkemy-plus/SKILL.md +25 -112
- package/skills/forest-alkemy-plus/references/components.md +5 -3
- package/skills/forest-alkemy-plus/references/patterns.md +66 -74
- package/skills/forest-alkemy-plus/references/upgrading.md +6 -25
- package/dist/chunk-2XQSGP7X.mjs.map +0 -1
package/README.md
CHANGED
|
@@ -57,20 +57,14 @@ Because `ForestProvider` is a client component, wrap your app in a client bounda
|
|
|
57
57
|
|
|
58
58
|
## Theme Presets
|
|
59
59
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
| Preset | Use Case |
|
|
63
|
-
|--------|----------|
|
|
64
|
-
| `forest-agency` | Internal tools + partner agencies (default) |
|
|
65
|
-
| `forest-external` | Client-facing products |
|
|
66
|
-
| `forest-internal` | Internal tools |
|
|
60
|
+
The built-in preset is `forest-alkemy-plus`, which is also the default theme applied when no `preset` is set. Register your own with `registerPreset()`.
|
|
67
61
|
|
|
68
62
|
### Using a Preset
|
|
69
63
|
|
|
70
64
|
```tsx
|
|
71
65
|
import { ForestProvider } from '@matteoaliano/forest-ui';
|
|
72
66
|
|
|
73
|
-
<ForestProvider preset="forest-
|
|
67
|
+
<ForestProvider preset="forest-alkemy+">
|
|
74
68
|
{/* Your app */}
|
|
75
69
|
</ForestProvider>
|
|
76
70
|
```
|
|
@@ -78,17 +72,67 @@ import { ForestProvider } from '@matteoaliano/forest-ui';
|
|
|
78
72
|
### Using a Custom Theme
|
|
79
73
|
|
|
80
74
|
```tsx
|
|
81
|
-
import { ForestProvider, buildTheme } from '@matteoaliano/forest-ui';
|
|
82
|
-
import { getPreset } from '@matteoaliano/forest-ui';
|
|
75
|
+
import { ForestProvider, buildTheme, getPreset } from '@matteoaliano/forest-ui';
|
|
83
76
|
|
|
84
|
-
const preset = getPreset('forest-
|
|
85
|
-
const theme = buildTheme(preset.
|
|
77
|
+
const preset = getPreset('forest-alkemy+');
|
|
78
|
+
const theme = buildTheme(preset.tokens, preset.tokensDark);
|
|
86
79
|
|
|
87
80
|
<ForestProvider theme={theme}>
|
|
88
81
|
{/* Your app */}
|
|
89
82
|
</ForestProvider>
|
|
90
83
|
```
|
|
91
84
|
|
|
85
|
+
## Dark Mode
|
|
86
|
+
|
|
87
|
+
`forest-alkemy-plus` ships a dark color scheme built on MUI's `colorSchemes` (CSS
|
|
88
|
+
variables). It activates on a **`.dark` class**, the same convention as
|
|
89
|
+
`@matteoaliano/forest-web`.
|
|
90
|
+
|
|
91
|
+
**Recommended: `useColorScheme()`** (below) — it puts the class on `<html>` for you,
|
|
92
|
+
so portaled components (Modal, Menu, Tooltip, Popover, which render at
|
|
93
|
+
`document.body`) switch too.
|
|
94
|
+
|
|
95
|
+
```tsx
|
|
96
|
+
// Manual class toggle: put `dark` on <html> (not a deep wrapper) so portaled
|
|
97
|
+
// content inherits it. A wrapper only darkens its own subtree, leaving
|
|
98
|
+
// modals/menus light.
|
|
99
|
+
document.documentElement.classList.toggle("dark", isDark);
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
Set the initial scheme with `defaultMode` (`"light"` default, `"dark"`, or
|
|
103
|
+
`"system"` to follow `prefers-color-scheme`):
|
|
104
|
+
|
|
105
|
+
```tsx
|
|
106
|
+
<ForestProvider preset="forest-alkemy+" defaultMode="system">
|
|
107
|
+
{/* Your app */}
|
|
108
|
+
</ForestProvider>
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
Toggle programmatically with the re-exported MUI hook:
|
|
112
|
+
|
|
113
|
+
```tsx
|
|
114
|
+
import { useColorScheme } from '@matteoaliano/forest-ui';
|
|
115
|
+
|
|
116
|
+
function ThemeToggle() {
|
|
117
|
+
const { mode, setMode } = useColorScheme();
|
|
118
|
+
return (
|
|
119
|
+
<button onClick={() => setMode(mode === 'dark' ? 'light' : 'dark')}>
|
|
120
|
+
{mode === 'dark' ? '☀️' : '🌙'}
|
|
121
|
+
</button>
|
|
122
|
+
);
|
|
123
|
+
}
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
**Next.js App Router** — render `InitColorSchemeScript` at the top of `<body>` to
|
|
127
|
+
prevent a flash of the wrong scheme on load:
|
|
128
|
+
|
|
129
|
+
```tsx
|
|
130
|
+
import { InitColorSchemeScript } from '@matteoaliano/forest-ui';
|
|
131
|
+
|
|
132
|
+
// app/layout.tsx, first child of <body>
|
|
133
|
+
<InitColorSchemeScript attribute="class" />
|
|
134
|
+
```
|
|
135
|
+
|
|
92
136
|
## Components
|
|
93
137
|
|
|
94
138
|
40+ wrapped MUI components with Forest defaults. See [Storybook](https://storybook.url) for all components.
|
package/bin/sync.mjs
CHANGED
|
@@ -3,94 +3,59 @@
|
|
|
3
3
|
/**
|
|
4
4
|
* forest-ui sync
|
|
5
5
|
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
6
|
+
* DEPRECATED for consumers — documentation + enforcement now ship as a Claude
|
|
7
|
+
* Code plugin. Copying loose skill files is no longer supported because stale
|
|
8
|
+
* copies drift from the plugin's hook.
|
|
9
|
+
*
|
|
10
|
+
* `--internal` (used by the monorepo's `npm run sync`) still propagates the
|
|
11
|
+
* skill source to the dogfood `.claude/skills/` copy and the plugin bundle.
|
|
8
12
|
*/
|
|
9
13
|
|
|
10
|
-
import {
|
|
14
|
+
import { mkdirSync, readdirSync, cpSync } from "node:fs";
|
|
11
15
|
import { resolve, dirname } from "node:path";
|
|
12
16
|
import { fileURLToPath } from "node:url";
|
|
13
17
|
|
|
14
18
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
15
19
|
const skillsDir = resolve(__dirname, "..", "skills");
|
|
16
20
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
while (dir !== dirname(dir)) {
|
|
21
|
-
if (existsSync(resolve(dir, "package.json"))) {
|
|
22
|
-
try {
|
|
23
|
-
const pkg = JSON.parse(readFileSync(resolve(dir, "package.json"), "utf-8"));
|
|
24
|
-
if (pkg.name !== "@matteoaliano/forest-ui") return dir;
|
|
25
|
-
} catch {
|
|
26
|
-
return dir;
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
dir = dirname(dir);
|
|
30
|
-
}
|
|
31
|
-
return process.cwd();
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
const projectRoot = findProjectRoot(process.cwd());
|
|
21
|
+
if (!process.argv.includes("--internal")) {
|
|
22
|
+
console.log(`
|
|
23
|
+
🌲 Forest UI — \`sync\` is deprecated
|
|
35
24
|
|
|
36
|
-
|
|
37
|
-
|
|
25
|
+
Skills are now delivered through the Forest Design System Claude Code
|
|
26
|
+
plugin, which also ships design-system enforcement hooks. Install it:
|
|
38
27
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
resolve(projectRoot, ".claude", "forest-ui.md"),
|
|
42
|
-
resolve(projectRoot, ".cursor", "rules", "forest-ui.mdc"),
|
|
43
|
-
resolve(projectRoot, ".gemini", "forest-ui.md"),
|
|
44
|
-
resolve(projectRoot, ".antigravity", "rules.md"),
|
|
45
|
-
];
|
|
28
|
+
/plugin marketplace add Witailer/wtl-design-system-forest
|
|
29
|
+
/plugin install forest-ui@forest-design-system
|
|
46
30
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
rmSync(legacyPath);
|
|
52
|
-
const rel = legacyPath.replace(projectRoot + "/", "");
|
|
53
|
-
console.log(` 🧹 Removed legacy ${rel}`);
|
|
54
|
-
cleaned++;
|
|
55
|
-
} catch (err) {
|
|
56
|
-
const rel = legacyPath.replace(projectRoot + "/", "");
|
|
57
|
-
console.log(` ⚠️ Could not remove ${rel} (${err.message})`);
|
|
58
|
-
}
|
|
59
|
-
}
|
|
31
|
+
No files were copied. If you previously synced, you can delete
|
|
32
|
+
.claude/skills/forest-alkemy-plus/ — the plugin replaces it.
|
|
33
|
+
`);
|
|
34
|
+
process.exit(0);
|
|
60
35
|
}
|
|
61
|
-
if (cleaned > 0) console.log();
|
|
62
36
|
|
|
63
|
-
|
|
64
|
-
|
|
37
|
+
// ── Internal monorepo sync: skills/ → dogfood + plugin bundle ──
|
|
38
|
+
const repoRoot = resolve(__dirname, "..", "..", "..");
|
|
39
|
+
const targets = [
|
|
40
|
+
resolve(repoRoot, ".claude", "skills"),
|
|
41
|
+
resolve(repoRoot, "plugins", "forest-ui", "skills"),
|
|
42
|
+
];
|
|
65
43
|
|
|
66
|
-
|
|
67
|
-
console.log(` ⚠️ No skills directory found, nothing to sync.\n`);
|
|
68
|
-
process.exit(0);
|
|
69
|
-
}
|
|
44
|
+
console.log(`\n🌲 Forest UI — Syncing skills (internal)\n`);
|
|
70
45
|
|
|
71
46
|
const skillFolders = readdirSync(skillsDir, { withFileTypes: true })
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
if (skillFolders.length === 0) {
|
|
76
|
-
console.log(` ⚠️ No skill folders found, nothing to sync.\n`);
|
|
77
|
-
process.exit(0);
|
|
78
|
-
}
|
|
47
|
+
.filter((d) => d.isDirectory())
|
|
48
|
+
.map((d) => d.name);
|
|
79
49
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
for (const folder of skillFolders) {
|
|
83
|
-
const
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
console.log(` ✅ .claude/skills/${folder}/`);
|
|
90
|
-
synced++;
|
|
91
|
-
} catch (err) {
|
|
92
|
-
console.log(` ⚠️ .claude/skills/${folder}/ skipped (${err.message})`);
|
|
93
|
-
}
|
|
50
|
+
let synced = 0;
|
|
51
|
+
for (const target of targets) {
|
|
52
|
+
for (const folder of skillFolders) {
|
|
53
|
+
const dest = resolve(target, folder);
|
|
54
|
+
mkdirSync(dest, { recursive: true });
|
|
55
|
+
cpSync(resolve(skillsDir, folder), dest, { recursive: true });
|
|
56
|
+
console.log(` ✅ ${dest.replace(repoRoot + "/", "")}/`);
|
|
57
|
+
synced++;
|
|
58
|
+
}
|
|
94
59
|
}
|
|
95
60
|
|
|
96
|
-
console.log(`\n Synced ${
|
|
61
|
+
console.log(`\n Synced ${skillFolders.length} skill(s) to ${targets.length} target(s) (${synced} copies).\n`);
|