@almadar/ui 5.127.0 → 5.129.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.
@@ -0,0 +1,61 @@
1
+ /**
2
+ * Export Static Design System
3
+ *
4
+ * After Storybook build, this script packages the design system
5
+ * into a client-ready deliverable (PDF or HTML bundle).
6
+ */
7
+
8
+ const fs = require('fs');
9
+ const path = require('path');
10
+
11
+ const STORYBOOK_DIR = path.join(__dirname, '..', 'storybook-static');
12
+ const OUTPUT_DIR = path.join(__dirname, '..', 'exports');
13
+
14
+ async function exportDesignSystem() {
15
+ console.log('📦 Exporting design system...');
16
+
17
+ // Ensure output directory exists
18
+ if (!fs.existsSync(OUTPUT_DIR)) {
19
+ fs.mkdirSync(OUTPUT_DIR, { recursive: true });
20
+ }
21
+
22
+ // Check if storybook build exists
23
+ if (!fs.existsSync(STORYBOOK_DIR)) {
24
+ console.error('❌ Storybook build not found. Run `npm run build-storybook` first.');
25
+ process.exit(1);
26
+ }
27
+
28
+ // Copy storybook-static to exports with timestamp
29
+ const timestamp = new Date().toISOString().split('T')[0];
30
+ const exportName = `design-system-${timestamp}`;
31
+ const exportPath = path.join(OUTPUT_DIR, exportName);
32
+
33
+ // Copy directory
34
+ fs.cpSync(STORYBOOK_DIR, exportPath, { recursive: true });
35
+
36
+ console.log(`✅ Design system exported to: ${exportPath}`);
37
+ console.log(` Open ${exportPath}/index.html in a browser to view.`);
38
+
39
+ // Create a manifest
40
+ const manifest = {
41
+ name: '@orbital/design-system',
42
+ version: '1.0.0',
43
+ exportedAt: new Date().toISOString(),
44
+ themes: ['wireframe', 'minimalist', 'almadar', 'winning-11'],
45
+ components: {
46
+ atoms: ['Avatar', 'Badge', 'Button', 'Card', 'Checkbox', 'Divider', 'Input', 'Radio', 'Select', 'Spinner', 'Typography'],
47
+ molecules: ['Accordion', 'Alert', 'Modal', 'Tabs', 'Toast'],
48
+ organisms: ['ConfirmDialog', 'DataTable', 'Header', 'Sidebar', 'StatCard', 'WizardContainer'],
49
+ templates: ['AuthLayout', 'CounterTemplate', 'DashboardLayout', 'GameTemplate', 'GenericAppTemplate'],
50
+ },
51
+ };
52
+
53
+ fs.writeFileSync(
54
+ path.join(exportPath, 'manifest.json'),
55
+ JSON.stringify(manifest, null, 2)
56
+ );
57
+
58
+ console.log(` Manifest created at: ${exportPath}/manifest.json`);
59
+ }
60
+
61
+ exportDesignSystem().catch(console.error);
@@ -0,0 +1,344 @@
1
+ /**
2
+ * Unified Design System Generator
3
+ *
4
+ * Generates a complete client-specific design system from an .orb schema:
5
+ * 1. Generates custom theme CSS
6
+ * 2. Suggests domain-specific components
7
+ * 3. Generates component stubs
8
+ * 4. Builds Storybook documentation
9
+ * 5. Exports static design system package
10
+ */
11
+
12
+ import * as fs from "fs";
13
+ import * as path from "path";
14
+ import { execSync } from "child_process";
15
+ import { generateThemeFromSchema } from "./generate-theme-from-schema";
16
+ import {
17
+ suggestComponents,
18
+ generateComponentStubs,
19
+ } from "./suggest-components";
20
+
21
+ interface GeneratorOptions {
22
+ schemaPath: string;
23
+ outputDir: string;
24
+ clientName?: string;
25
+ buildStorybook?: boolean;
26
+ generateComponents?: boolean;
27
+ clientOnly?: boolean; // Only include client-specific components in Storybook
28
+ }
29
+
30
+ interface GeneratorResult {
31
+ themePath: string;
32
+ suggestedComponents: number;
33
+ generatedComponents: number;
34
+ storybookPath?: string;
35
+ }
36
+
37
+ /**
38
+ * Main generator function
39
+ */
40
+ export async function generateDesignSystem(
41
+ options: GeneratorOptions,
42
+ ): Promise<GeneratorResult> {
43
+ const {
44
+ schemaPath,
45
+ outputDir,
46
+ clientName,
47
+ buildStorybook = true,
48
+ generateComponents = true,
49
+ clientOnly = false,
50
+ } = options;
51
+
52
+ console.log("🎨 Orbital Design System Generator");
53
+ console.log("==================================\n");
54
+
55
+ // Validate schema exists
56
+ if (!fs.existsSync(schemaPath)) {
57
+ throw new Error(`Schema file not found: ${schemaPath}`);
58
+ }
59
+
60
+ // Read schema to get name
61
+ const schemaContent = fs.readFileSync(schemaPath, "utf-8");
62
+ const schema = JSON.parse(schemaContent);
63
+ const name =
64
+ clientName || schema.name.toLowerCase().replace(/[^a-z0-9]+/g, "-");
65
+
66
+ console.log(`📋 Processing schema: ${schema.name}`);
67
+ console.log(`📁 Output directory: ${outputDir}\n`);
68
+
69
+ // Create output directory
70
+ const clientOutputDir = path.join(outputDir, name);
71
+ if (!fs.existsSync(clientOutputDir)) {
72
+ fs.mkdirSync(clientOutputDir, { recursive: true });
73
+ }
74
+
75
+ // Step 1: Generate theme
76
+ console.log("1️⃣ Generating theme...");
77
+ const themePath = path.join(clientOutputDir, `${name}-theme.css`);
78
+ generateThemeFromSchema(schemaPath, themePath);
79
+ console.log(` ✅ Theme generated: ${themePath}\n`);
80
+
81
+ // Step 2: Suggest components
82
+ console.log("2️⃣ Analyzing schema for component suggestions...");
83
+ const { domains, suggestions } = suggestComponents(schemaPath);
84
+ console.log(` 📦 Detected domains: ${domains.join(", ") || "none"}`);
85
+ console.log(` 💡 Suggested components: ${suggestions.length}\n`);
86
+
87
+ // Step 3: Generate component stubs
88
+ let generatedCount = 0;
89
+ if (generateComponents && suggestions.length > 0) {
90
+ console.log("3️⃣ Generating component stubs...");
91
+ const componentsDir = path.join(clientOutputDir, "components");
92
+ generateComponentStubs(suggestions, componentsDir);
93
+ generatedCount = suggestions.length;
94
+ console.log(` ✅ Generated ${generatedCount} component stubs\n`);
95
+ }
96
+
97
+ // Step 4: Copy theme to design system themes folder for Storybook
98
+ const designSystemThemesDir = path.join(__dirname, "..", "themes");
99
+ const targetThemePath = path.join(designSystemThemesDir, `${name}.css`);
100
+ fs.copyFileSync(themePath, targetThemePath);
101
+ console.log(`4️⃣ Theme copied to design system: ${targetThemePath}\n`);
102
+
103
+ // Update index.css to include the new theme
104
+ const indexCssPath = path.join(__dirname, "..", "index.css");
105
+ const indexCss = fs.readFileSync(indexCssPath, "utf-8");
106
+ const importStatement = `@import './themes/${name}.css';`;
107
+
108
+ if (!indexCss.includes(importStatement)) {
109
+ const updatedCss = indexCss.replace(
110
+ "@tailwind base;",
111
+ `${importStatement}\n\n@tailwind base;`,
112
+ );
113
+ fs.writeFileSync(indexCssPath, updatedCss);
114
+ console.log(` Updated index.css with theme import\n`);
115
+ }
116
+
117
+ // Step 5: Update Storybook preview to include the new theme
118
+ const previewPath = path.join(__dirname, "..", ".storybook", "preview.tsx");
119
+ const previewContent = fs.readFileSync(previewPath, "utf-8");
120
+
121
+ if (!previewContent.includes(`'${name}':`)) {
122
+ const updatedPreview = previewContent.replace(
123
+ "defaultTheme: 'minimalist',",
124
+ `'${name}': '${name}',\n },\n defaultTheme: '${name}',`,
125
+ );
126
+ fs.writeFileSync(previewPath, updatedPreview);
127
+ console.log(` Updated Storybook preview with theme: ${name}\n`);
128
+ }
129
+
130
+ // Step 6: Build Storybook (optional)
131
+ let storybookPath: string | undefined;
132
+ if (buildStorybook) {
133
+ console.log("5️⃣ Building Storybook...");
134
+ const designSystemDir = path.join(__dirname, "..");
135
+
136
+ // Determine output path for storybook
137
+ // For client-only, output directly to the output directory (e.g., projects/winning-11/design-system/storybook-static)
138
+ // For full library, output to clientOutputDir/storybook
139
+ storybookPath = clientOnly
140
+ ? path.join(outputDir, "storybook-static")
141
+ : path.join(clientOutputDir, "storybook");
142
+
143
+ try {
144
+ // Install dependencies if needed
145
+ if (!fs.existsSync(path.join(designSystemDir, "node_modules"))) {
146
+ console.log(" Installing dependencies...");
147
+ execSync("npm install", { cwd: designSystemDir, stdio: "inherit" });
148
+ }
149
+
150
+ if (clientOnly) {
151
+ // Use the dedicated .storybook-client config and output directly to project
152
+ console.log(` 📦 Building client-only Storybook for: ${name}`);
153
+ console.log(` 📁 Output: ${storybookPath}`);
154
+
155
+ // Build using client-only config with custom output path
156
+ execSync(
157
+ `npx storybook build -c .storybook-client -o "${storybookPath}"`,
158
+ {
159
+ cwd: designSystemDir,
160
+ stdio: "inherit",
161
+ },
162
+ );
163
+ } else {
164
+ // Build full storybook
165
+ execSync("npm run build-storybook", {
166
+ cwd: designSystemDir,
167
+ stdio: "inherit",
168
+ });
169
+
170
+ // Copy to output
171
+ const storybookStaticDir = path.join(
172
+ designSystemDir,
173
+ "storybook-static",
174
+ );
175
+ if (fs.existsSync(storybookStaticDir)) {
176
+ fs.cpSync(storybookStaticDir, storybookPath, { recursive: true });
177
+ }
178
+ }
179
+
180
+ console.log(` ✅ Storybook built: ${storybookPath}\n`);
181
+ } catch (error) {
182
+ console.log(
183
+ " ⚠️ Storybook build skipped (run manually with npm run build-storybook)\n",
184
+ );
185
+ storybookPath = undefined;
186
+ }
187
+ }
188
+
189
+ // Step 7: Generate client portal and deployment files
190
+ console.log("6️⃣ Generating client portal and deployment files...");
191
+ const templatesDir = path.join(__dirname, "..", "..", "templates");
192
+ const projectRoot = path.join(outputDir, "..");
193
+
194
+ // Template variables
195
+ const projectDisplayName =
196
+ schema.name ||
197
+ name.replace(/-/g, " ").replace(/\b\w/g, (c: string) => c.toUpperCase());
198
+ const projectId = name.toLowerCase().replace(/[^a-z0-9-]/g, "");
199
+ const generatedDate = new Date().toISOString().split("T")[0];
200
+ const version = schema.version || "1.0.0";
201
+
202
+ const replaceTemplateVars = (content: string): string => {
203
+ return content
204
+ .replace(/\{\{PROJECT_NAME\}\}/g, projectDisplayName)
205
+ .replace(/\{\{PROJECT_ID\}\}/g, projectId)
206
+ .replace(/\{\{GENERATED_DATE\}\}/g, generatedDate)
207
+ .replace(/\{\{VERSION\}\}/g, version);
208
+ };
209
+
210
+ // Files to copy and process
211
+ const templateFiles = [
212
+ { src: "client-portal.html", dest: "index.html" },
213
+ { src: "firebase.json", dest: "firebase.json" },
214
+ { src: ".firebaserc", dest: ".firebaserc" },
215
+ { src: "deploy.sh", dest: "deploy.sh", executable: true },
216
+ ];
217
+
218
+ for (const file of templateFiles) {
219
+ const srcPath = path.join(templatesDir, file.src);
220
+ const destPath = path.join(projectRoot, file.dest);
221
+
222
+ if (fs.existsSync(srcPath)) {
223
+ let content = fs.readFileSync(srcPath, "utf-8");
224
+ content = replaceTemplateVars(content);
225
+ fs.writeFileSync(destPath, content);
226
+
227
+ if (file.executable) {
228
+ fs.chmodSync(destPath, "755");
229
+ }
230
+
231
+ console.log(` ✅ Generated: ${file.dest}`);
232
+ }
233
+ }
234
+ console.log("");
235
+
236
+ // Step 8: Generate manifest
237
+ const manifest = {
238
+ name: schema.name,
239
+ clientName: name,
240
+ generatedAt: new Date().toISOString(),
241
+ domains,
242
+ theme: {
243
+ path: `${name}-theme.css`,
244
+ variables: extractCssVariables(themePath),
245
+ },
246
+ components: {
247
+ suggested: suggestions.map((s) => ({
248
+ name: s.name,
249
+ category: s.category,
250
+ priority: s.priority,
251
+ })),
252
+ generated: generatedCount,
253
+ },
254
+ storybook: storybookPath ? "storybook/index.html" : null,
255
+ clientPortal: "../index.html",
256
+ };
257
+
258
+ const manifestPath = path.join(clientOutputDir, "manifest.json");
259
+ fs.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2));
260
+ console.log(`📄 Manifest generated: ${manifestPath}\n`);
261
+
262
+ // Summary
263
+ console.log("🎉 Design System Generation Complete!");
264
+ console.log("=====================================");
265
+ console.log(` Theme: ${themePath}`);
266
+ console.log(` Suggested Components: ${suggestions.length}`);
267
+ console.log(` Generated Components: ${generatedCount}`);
268
+ if (storybookPath) {
269
+ console.log(` Storybook: ${storybookPath}/index.html`);
270
+ }
271
+ console.log(` Manifest: ${manifestPath}`);
272
+ console.log("");
273
+
274
+ return {
275
+ themePath,
276
+ suggestedComponents: suggestions.length,
277
+ generatedComponents: generatedCount,
278
+ storybookPath,
279
+ };
280
+ }
281
+
282
+ /**
283
+ * Extract CSS variable names from a theme file
284
+ */
285
+ function extractCssVariables(themePath: string): string[] {
286
+ const content = fs.readFileSync(themePath, "utf-8");
287
+ const variablePattern = /--([a-z-]+):/g;
288
+ const variables: string[] = [];
289
+ let match;
290
+
291
+ while ((match = variablePattern.exec(content)) !== null) {
292
+ if (!variables.includes(`--${match[1]}`)) {
293
+ variables.push(`--${match[1]}`);
294
+ }
295
+ }
296
+
297
+ return variables;
298
+ }
299
+
300
+ // CLI usage
301
+ if (require.main === module) {
302
+ const args = process.argv.slice(2);
303
+
304
+ if (args.length < 1) {
305
+ console.log(`
306
+ Orbital Design System Generator
307
+
308
+ Usage:
309
+ npx tsx generate-design-system.ts <schema.orb> [options]
310
+
311
+ Options:
312
+ --output <dir> Output directory (default: ./exports)
313
+ --name <name> Client name override
314
+ --no-storybook Skip Storybook build
315
+ --no-components Skip component generation
316
+ --client-only Only include client-specific components in Storybook (not the full library)
317
+
318
+ Examples:
319
+ npx tsx generate-design-system.ts winning-11.orb
320
+ npx tsx generate-design-system.ts winning-11.orb --output ./clients
321
+ npx tsx generate-design-system.ts winning-11.orb --name garden-app --no-storybook
322
+ npx tsx generate-design-system.ts winning-11.orb --client-only
323
+ `);
324
+ process.exit(1);
325
+ }
326
+
327
+ const schemaPath = args[0];
328
+ const outputIndex = args.indexOf("--output");
329
+ const nameIndex = args.indexOf("--name");
330
+
331
+ const options: GeneratorOptions = {
332
+ schemaPath,
333
+ outputDir:
334
+ outputIndex !== -1
335
+ ? args[outputIndex + 1]
336
+ : path.join(__dirname, "..", "exports"),
337
+ clientName: nameIndex !== -1 ? args[nameIndex + 1] : undefined,
338
+ buildStorybook: !args.includes("--no-storybook"),
339
+ generateComponents: !args.includes("--no-components"),
340
+ clientOnly: args.includes("--client-only"),
341
+ };
342
+
343
+ generateDesignSystem(options).catch(console.error);
344
+ }