@schandlergarcia/sf-web-components 2.0.0 → 2.2.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,179 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * apply-brand.mjs — Apply a brand theme to the project.
5
+ *
6
+ * Usage:
7
+ * node scripts/apply-brand.mjs engine # Apply Engine brand
8
+ * node scripts/apply-brand.mjs --list # List available brands
9
+ * node scripts/apply-brand.mjs --reset # Remove brand, restore neutral theme
10
+ *
11
+ * When run from a consuming project (via npm run brand:engine), the script
12
+ * detects the package location automatically.
13
+ */
14
+
15
+ import fs from 'fs';
16
+ import path from 'path';
17
+ import { fileURLToPath } from 'url';
18
+
19
+ const __filename = fileURLToPath(import.meta.url);
20
+ const __dirname = path.dirname(__filename);
21
+
22
+ const arg = process.argv[2];
23
+
24
+ if (!arg) {
25
+ console.error('Usage: node scripts/apply-brand.mjs <brand-name|--list|--reset>');
26
+ process.exit(1);
27
+ }
28
+
29
+ const cwd = process.cwd();
30
+ const PACKAGE_NAME = '@schandlergarcia/sf-web-components';
31
+
32
+ function findPackageRoot() {
33
+ const fromNodeModules = path.join(cwd, 'node_modules', PACKAGE_NAME);
34
+ if (fs.existsSync(fromNodeModules)) return fromNodeModules;
35
+
36
+ const fromScript = path.resolve(__dirname, '..');
37
+ if (fs.existsSync(path.join(fromScript, 'brands'))) return fromScript;
38
+
39
+ return null;
40
+ }
41
+
42
+ const packageRoot = findPackageRoot();
43
+ if (!packageRoot) {
44
+ console.error('Could not find package root. Run from the project directory.');
45
+ process.exit(1);
46
+ }
47
+
48
+ const brandsDir = path.join(packageRoot, 'brands');
49
+
50
+ if (arg === '--list') {
51
+ if (!fs.existsSync(brandsDir)) {
52
+ console.log('No brands available.');
53
+ process.exit(0);
54
+ }
55
+ const brands = fs.readdirSync(brandsDir).filter(d =>
56
+ fs.statSync(path.join(brandsDir, d)).isDirectory()
57
+ );
58
+ console.log(`Available brands: ${brands.join(', ') || '(none)'}`);
59
+ process.exit(0);
60
+ }
61
+
62
+ if (arg === '--reset') {
63
+ const neutralCSS = path.join(packageRoot, 'src/styles/global.css');
64
+ const targetCSS = path.join(cwd, 'src/styles/global.css');
65
+ if (fs.existsSync(neutralCSS)) {
66
+ fs.copyFileSync(neutralCSS, targetCSS);
67
+ console.log(' ✓ Restored neutral theme (global.css)');
68
+ }
69
+ const brandMarker = path.join(cwd, '.brand');
70
+ if (fs.existsSync(brandMarker)) fs.unlinkSync(brandMarker);
71
+ console.log('\n✅ Brand reset to neutral.\n');
72
+ process.exit(0);
73
+ }
74
+
75
+ const brandName = arg;
76
+ const brandDir = path.join(brandsDir, brandName);
77
+
78
+ if (!fs.existsSync(brandDir)) {
79
+ console.error(`Brand "${brandName}" not found in ${brandsDir}`);
80
+ const available = fs.existsSync(brandsDir)
81
+ ? fs.readdirSync(brandsDir).filter(d => fs.statSync(path.join(brandsDir, d)).isDirectory())
82
+ : [];
83
+ if (available.length) console.error(`Available: ${available.join(', ')}`);
84
+ process.exit(1);
85
+ }
86
+
87
+ console.log(`\n🎨 Applying "${brandName}" brand...\n`);
88
+
89
+ let installed = 0;
90
+
91
+ // 1. global.css → src/styles/global.css (always overwrite)
92
+ const brandCSS = path.join(brandDir, 'global.css');
93
+ const targetCSS = path.join(cwd, 'src/styles/global.css');
94
+ if (fs.existsSync(brandCSS)) {
95
+ fs.mkdirSync(path.dirname(targetCSS), { recursive: true });
96
+ fs.copyFileSync(brandCSS, targetCSS);
97
+ console.log(' ✓ Brand theme applied (global.css)');
98
+ installed++;
99
+ }
100
+
101
+ // 2. Sample data files → src/data/
102
+ const dataFiles = [
103
+ { src: 'engine-sample-data.js', dst: 'src/data/engine-sample-data.js' },
104
+ { src: 'engine-live-data.js', dst: 'src/data/engine-live-data.js' },
105
+ { src: 'partner-hub-sample-data.js', dst: 'src/data/partner-hub-sample-data.js' },
106
+ ];
107
+ for (const { src, dst } of dataFiles) {
108
+ const srcPath = path.join(brandDir, src);
109
+ const dstPath = path.join(cwd, dst);
110
+ if (fs.existsSync(srcPath)) {
111
+ fs.mkdirSync(path.dirname(dstPath), { recursive: true });
112
+ fs.copyFileSync(srcPath, dstPath);
113
+ console.log(` ✓ Installed ${dst}`);
114
+ installed++;
115
+ }
116
+ }
117
+
118
+ // 3. Hooks → src/hooks/
119
+ const hooks = [
120
+ { src: 'useEngineLiveData.ts', dst: 'src/hooks/useEngineLiveData.ts' },
121
+ { src: 'useEvaAgent.ts', dst: 'src/hooks/useEvaAgent.ts' },
122
+ ];
123
+ for (const { src, dst } of hooks) {
124
+ const srcPath = path.join(brandDir, src);
125
+ const dstPath = path.join(cwd, dst);
126
+ if (fs.existsSync(srcPath)) {
127
+ fs.mkdirSync(path.dirname(dstPath), { recursive: true });
128
+ fs.copyFileSync(srcPath, dstPath);
129
+ console.log(` ✓ Installed ${dst}`);
130
+ installed++;
131
+ }
132
+ }
133
+
134
+ // 4. Config files → src/config/
135
+ const configs = [
136
+ { src: 'agentApiConfig.ts', dst: 'src/config/agentApi.ts' },
137
+ ];
138
+ for (const { src, dst } of configs) {
139
+ const srcPath = path.join(brandDir, src);
140
+ const dstPath = path.join(cwd, dst);
141
+ if (fs.existsSync(srcPath)) {
142
+ fs.mkdirSync(path.dirname(dstPath), { recursive: true });
143
+ fs.copyFileSync(srcPath, dstPath);
144
+ console.log(` ✓ Installed ${dst}`);
145
+ installed++;
146
+ }
147
+ }
148
+
149
+ // 5. PRDs → project root
150
+ const prds = fs.readdirSync(brandDir).filter(f => f.endsWith('_PRD.md') || f.endsWith('-prd.md'));
151
+ for (const prdFile of prds) {
152
+ fs.copyFileSync(path.join(brandDir, prdFile), path.join(cwd, prdFile));
153
+ console.log(` ✓ Installed ${prdFile}`);
154
+ installed++;
155
+ }
156
+
157
+ // 6. Schema → project root
158
+ const schema = path.join(brandDir, 'schema.graphql');
159
+ if (fs.existsSync(schema)) {
160
+ fs.copyFileSync(schema, path.join(cwd, 'schema.graphql'));
161
+ console.log(' ✓ Installed schema.graphql');
162
+ installed++;
163
+ }
164
+
165
+ // 7. Logo → src/assets/images/
166
+ const logo = path.join(brandDir, 'engine_logo.png');
167
+ if (fs.existsSync(logo)) {
168
+ const logoTarget = path.join(cwd, 'src/assets/images/engine_logo.png');
169
+ fs.mkdirSync(path.dirname(logoTarget), { recursive: true });
170
+ fs.copyFileSync(logo, logoTarget);
171
+ console.log(' ✓ Installed engine_logo.png');
172
+ installed++;
173
+ }
174
+
175
+ // 8. Write .brand marker so reset script knows which brand is active
176
+ fs.writeFileSync(path.join(cwd, '.brand'), brandName + '\n', 'utf-8');
177
+
178
+ console.log(`\n✅ "${brandName}" brand applied (${installed} files installed).`);
179
+ console.log(' Run "npm run brand:reset" to revert to neutral theme.\n');
@@ -335,6 +335,26 @@ if (fs.existsSync(packageJsonPath)) {
335
335
  scriptsAdded.push('validate:dashboard');
336
336
  }
337
337
 
338
+ // Add manual setup script (re-runs postinstall if it was missed)
339
+ if (!packageJson.scripts['setup']) {
340
+ packageJson.scripts['setup'] = 'node node_modules/@schandlergarcia/sf-web-components/scripts/postinstall.mjs';
341
+ scriptsAdded.push('setup');
342
+ }
343
+
344
+ // Add brand scripts
345
+ if (!packageJson.scripts['brand:engine']) {
346
+ packageJson.scripts['brand:engine'] = 'node node_modules/@schandlergarcia/sf-web-components/scripts/apply-brand.mjs engine';
347
+ scriptsAdded.push('brand:engine');
348
+ }
349
+ if (!packageJson.scripts['brand:reset']) {
350
+ packageJson.scripts['brand:reset'] = 'node node_modules/@schandlergarcia/sf-web-components/scripts/apply-brand.mjs --reset';
351
+ scriptsAdded.push('brand:reset');
352
+ }
353
+ if (!packageJson.scripts['brand:list']) {
354
+ packageJson.scripts['brand:list'] = 'node node_modules/@schandlergarcia/sf-web-components/scripts/apply-brand.mjs --list';
355
+ scriptsAdded.push('brand:list');
356
+ }
357
+
338
358
  if (scriptsAdded.length > 0) {
339
359
  fs.writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2) + '\n', 'utf-8');
340
360
  scriptsAdded.forEach(script => {
@@ -442,6 +462,7 @@ console.log(` - Installed ${templatesInstalled} page templates`);
442
462
  console.log(` - Installed ${scriptsInstalled} utility scripts`);
443
463
  console.log(` - Installed CommandCenter.tsx for dashboard management`);
444
464
  console.log(` - Added "npm run reset:command-center" script`);
465
+ console.log(` - Added "npm run setup" script (re-run this setup anytime)`);
445
466
  console.log(` - Installed AI assistant rules (.a4drules)`);
446
467
  if (migratedFiles > 0) {
447
468
  console.log(` - Migrated ${migratedFiles} dashboard files to correct location`);
@@ -398,24 +398,45 @@ echo "→ Cleaning caches…"
398
398
  rm -rf node_modules/.vite 2>/dev/null && echo " ✓ Cleared Vite cache" || true
399
399
  echo ""
400
400
 
401
- # ── 9. Restore neutral global.css ────────────────────────────────────────────
401
+ # ── 9. Restore global.css (brand-aware) ──────────────────────────────────────
402
402
 
403
403
  mkdir -p src/styles
404
404
 
405
405
  GLOBAL_CSS="src/styles/global.css"
406
406
  echo "→ Restoring ${GLOBAL_CSS}..."
407
407
 
408
- # Detect package location for global.css
408
+ # Check if a brand is active
409
+ ACTIVE_BRAND=""
410
+ if [ -f ".brand" ]; then
411
+ ACTIVE_BRAND="$(cat .brand | tr -d '[:space:]')"
412
+ fi
413
+
409
414
  PACKAGE_CSS=""
410
- if [ -f "node_modules/@schandlergarcia/sf-web-components/src/styles/global.css" ]; then
411
- PACKAGE_CSS="node_modules/@schandlergarcia/sf-web-components/src/styles/global.css"
412
- elif [ -f "$SCRIPT_DIR/../src/styles/global.css" ]; then
413
- PACKAGE_CSS="$SCRIPT_DIR/../src/styles/global.css"
415
+ if [ -n "$ACTIVE_BRAND" ]; then
416
+ # Use the brand's global.css
417
+ if [ -f "node_modules/@schandlergarcia/sf-web-components/brands/$ACTIVE_BRAND/global.css" ]; then
418
+ PACKAGE_CSS="node_modules/@schandlergarcia/sf-web-components/brands/$ACTIVE_BRAND/global.css"
419
+ elif [ -f "$SCRIPT_DIR/../brands/$ACTIVE_BRAND/global.css" ]; then
420
+ PACKAGE_CSS="$SCRIPT_DIR/../brands/$ACTIVE_BRAND/global.css"
421
+ fi
422
+ fi
423
+
424
+ # Fall back to neutral if no brand or brand CSS not found
425
+ if [ -z "$PACKAGE_CSS" ]; then
426
+ if [ -f "node_modules/@schandlergarcia/sf-web-components/src/styles/global.css" ]; then
427
+ PACKAGE_CSS="node_modules/@schandlergarcia/sf-web-components/src/styles/global.css"
428
+ elif [ -f "$SCRIPT_DIR/../src/styles/global.css" ]; then
429
+ PACKAGE_CSS="$SCRIPT_DIR/../src/styles/global.css"
430
+ fi
414
431
  fi
415
432
 
416
433
  if [ -n "$PACKAGE_CSS" ] && [ -f "$PACKAGE_CSS" ]; then
417
434
  cp "$PACKAGE_CSS" "$GLOBAL_CSS"
418
- echo " ✓ Theme restored from package"
435
+ if [ -n "$ACTIVE_BRAND" ]; then
436
+ echo " ✓ Theme restored (brand: $ACTIVE_BRAND)"
437
+ else
438
+ echo " ✓ Theme restored (neutral)"
439
+ fi
419
440
  else
420
441
  echo " ⚠ Skipped global.css (package CSS not found)"
421
442
  fi