@onexapis/cli 1.1.16 → 1.1.18

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.
Files changed (63) hide show
  1. package/README.md +117 -51
  2. package/bin/onexthm.js +4 -0
  3. package/dist/cli.js +750 -328
  4. package/dist/cli.js.map +1 -1
  5. package/dist/cli.mjs +747 -325
  6. package/dist/cli.mjs.map +1 -1
  7. package/dist/index.js +162 -299
  8. package/dist/index.js.map +1 -1
  9. package/dist/index.mjs +162 -299
  10. package/dist/index.mjs.map +1 -1
  11. package/dist/preview/preview-app.tsx +175 -53
  12. package/package.json +14 -12
  13. package/templates/default/.env.example +1 -1
  14. package/templates/default/.mcp.json +8 -0
  15. package/templates/default/CLAUDE.md +941 -0
  16. package/templates/default/bundle-entry.ts +18 -0
  17. package/templates/default/index.ts +26 -0
  18. package/templates/default/package.json +37 -0
  19. package/templates/default/pages/about.ts +66 -0
  20. package/templates/default/pages/home.ts +93 -0
  21. package/templates/default/pages/showcase.ts +146 -0
  22. package/templates/default/sections/about/about-default.tsx +237 -0
  23. package/templates/default/sections/about/about.schema.ts +259 -0
  24. package/templates/default/sections/about/index.ts +15 -0
  25. package/templates/default/sections/cta/cta-default.tsx +180 -0
  26. package/templates/default/sections/cta/cta.schema.ts +210 -0
  27. package/templates/default/sections/cta/index.ts +11 -0
  28. package/templates/default/sections/features/features-default.tsx +154 -0
  29. package/templates/default/sections/features/features.schema.ts +330 -0
  30. package/templates/default/sections/features/index.ts +11 -0
  31. package/templates/default/sections/gallery/gallery-default.tsx +134 -0
  32. package/templates/default/sections/gallery/gallery.schema.ts +397 -0
  33. package/templates/default/sections/gallery/index.ts +11 -0
  34. package/templates/default/sections/hero/hero-default.tsx +212 -0
  35. package/templates/default/sections/hero/hero.schema.ts +273 -0
  36. package/templates/default/sections/hero/index.ts +15 -0
  37. package/templates/default/sections/stats/index.ts +11 -0
  38. package/templates/default/sections/stats/stats-default.tsx +103 -0
  39. package/templates/default/sections/stats/stats.schema.ts +266 -0
  40. package/templates/default/sections/testimonials/index.ts +11 -0
  41. package/templates/default/sections/testimonials/testimonials-default.tsx +130 -0
  42. package/templates/default/sections/testimonials/testimonials.schema.ts +371 -0
  43. package/templates/default/sections-registry.ts +32 -0
  44. package/templates/default/theme.config.ts +107 -0
  45. package/templates/default/theme.layout.ts +21 -0
  46. package/templates/default/tsconfig.json +16 -7
  47. package/templates/default/README.md.ejs +0 -129
  48. package/templates/default/esbuild.config.js +0 -81
  49. package/templates/default/package.json.ejs +0 -31
  50. package/templates/default/src/config.ts.ejs +0 -98
  51. package/templates/default/src/index.ts.ejs +0 -11
  52. package/templates/default/src/layout.ts +0 -23
  53. package/templates/default/src/manifest.ts.ejs +0 -47
  54. package/templates/default/src/pages/home.ts.ejs +0 -37
  55. package/templates/default/src/sections/footer/footer-default.tsx +0 -28
  56. package/templates/default/src/sections/footer/footer.schema.ts +0 -45
  57. package/templates/default/src/sections/footer/index.ts +0 -2
  58. package/templates/default/src/sections/header/header-default.tsx +0 -61
  59. package/templates/default/src/sections/header/header.schema.ts +0 -46
  60. package/templates/default/src/sections/header/index.ts +0 -2
  61. package/templates/default/src/sections/hero/hero-default.tsx +0 -52
  62. package/templates/default/src/sections/hero/hero.schema.ts +0 -52
  63. package/templates/default/src/sections/hero/index.ts +0 -2
@@ -171,7 +171,102 @@ function toPascalCase(str: string): string {
171
171
  .join("");
172
172
  }
173
173
 
174
- // ===== 4. SECTION DATA ENRICHMENT =====
174
+ // ===== 4. THEME CSS VARIABLE INJECTION =====
175
+
176
+ const PALETTE_TO_CSS_VAR: Record<string, string> = {
177
+ primary: "--primary",
178
+ secondary: "--secondary",
179
+ accent: "--accent",
180
+ background: "--background",
181
+ foreground: "--foreground",
182
+ muted: "--muted",
183
+ mutedForeground: "--muted-foreground",
184
+ card: "--card",
185
+ cardForeground: "--card-foreground",
186
+ popover: "--popover",
187
+ popoverForeground: "--popover-foreground",
188
+ border: "--border",
189
+ input: "--input",
190
+ ring: "--ring",
191
+ destructive: "--destructive",
192
+ };
193
+
194
+ function hexToHsl(hex: string): string {
195
+ const cleaned = hex.replace(/^#/, "");
196
+ let r: number, g: number, b: number;
197
+ if (cleaned.length === 3) {
198
+ r = parseInt(cleaned[0] + cleaned[0], 16) / 255;
199
+ g = parseInt(cleaned[1] + cleaned[1], 16) / 255;
200
+ b = parseInt(cleaned[2] + cleaned[2], 16) / 255;
201
+ } else {
202
+ r = parseInt(cleaned.substring(0, 2), 16) / 255;
203
+ g = parseInt(cleaned.substring(2, 4), 16) / 255;
204
+ b = parseInt(cleaned.substring(4, 6), 16) / 255;
205
+ }
206
+ const max = Math.max(r, g, b);
207
+ const min = Math.min(r, g, b);
208
+ const l = (max + min) / 2;
209
+ if (max === min) return `0 0% ${Math.round(l * 100)}%`;
210
+ const d = max - min;
211
+ const s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
212
+ let h = 0;
213
+ if (max === r) h = ((g - b) / d + (g < b ? 6 : 0)) / 6;
214
+ else if (max === g) h = ((b - r) / d + 2) / 6;
215
+ else h = ((r - g) / d + 4) / 6;
216
+ return `${Math.round(h * 360)} ${Math.round(s * 100)}% ${Math.round(l * 100)}%`;
217
+ }
218
+
219
+ function paletteToCSS(palette: Record<string, string>): string {
220
+ const lines: string[] = [];
221
+ for (const [field, cssVar] of Object.entries(PALETTE_TO_CSS_VAR)) {
222
+ const value = palette[field];
223
+ if (value && value.startsWith("#")) {
224
+ lines.push(` ${cssVar}: ${hexToHsl(value)};`);
225
+ }
226
+ }
227
+ return lines.join("\n");
228
+ }
229
+
230
+ function buildThemeStyleCSS(themeConfig: any): string | null {
231
+ if (!themeConfig?.colors?.light) return null;
232
+ const lightCSS = paletteToCSS(themeConfig.colors.light);
233
+ const darkCSS = themeConfig.colors.dark
234
+ ? paletteToCSS(themeConfig.colors.dark)
235
+ : "";
236
+ if (!lightCSS && !darkCSS) return null;
237
+ return [
238
+ lightCSS ? `:root {\n${lightCSS}\n}` : "",
239
+ darkCSS ? `.dark {\n${darkCSS}\n}` : "",
240
+ ]
241
+ .filter(Boolean)
242
+ .join("\n");
243
+ }
244
+
245
+ // ===== 5. RESOLVE SECTION LIST =====
246
+
247
+ function resolveSections(
248
+ sectionConfigs: any[],
249
+ themeExports: ThemeExports,
250
+ themePrefix: string
251
+ ) {
252
+ return (sectionConfigs || [])
253
+ .filter((s: any) => s.enabled !== false)
254
+ .sort((a: any, b: any) => a.order - b.order)
255
+ .map((section: any) => {
256
+ const reg = getSectionComponent(
257
+ themeExports,
258
+ themePrefix,
259
+ section.type,
260
+ section.template || "default"
261
+ );
262
+ const enriched = reg?.schema
263
+ ? enrichSectionWithDefaults(section, reg.schema)
264
+ : section;
265
+ return { section: enriched, registration: reg };
266
+ });
267
+ }
268
+
269
+ // ===== 6. SECTION DATA ENRICHMENT =====
175
270
 
176
271
  /**
177
272
  * Merge schema defaults into section instances from page config.
@@ -199,7 +294,7 @@ function enrichSectionWithDefaults(section: any, schema: any): any {
199
294
  };
200
295
  }
201
296
 
202
- // ===== 5. PAGE CONFIG DISCOVERY =====
297
+ // ===== 7. PAGE CONFIG DISCOVERY =====
203
298
 
204
299
  function discoverPageConfigs(
205
300
  themeExports: ThemeExports
@@ -231,7 +326,7 @@ function discoverPageConfigs(
231
326
  return pages;
232
327
  }
233
328
 
234
- // ===== 6. PREVIEW APP COMPONENT =====
329
+ // ===== 8. PREVIEW APP COMPONENT =====
235
330
 
236
331
  /**
237
332
  * Match URL pathname to a page index.
@@ -297,7 +392,6 @@ function PreviewApp() {
297
392
 
298
393
  ws.onopen = () => {
299
394
  setWsStatus("connected");
300
-
301
395
  };
302
396
 
303
397
  ws.onmessage = (event) => {
@@ -306,7 +400,6 @@ function PreviewApp() {
306
400
  setWsStatus("rebuilding");
307
401
  loadTheme(msg.timestamp).then(() => {
308
402
  setWsStatus("connected");
309
-
310
403
  });
311
404
  } else if (msg.type === "error") {
312
405
  setError(msg.message);
@@ -437,62 +530,91 @@ function PreviewApp() {
437
530
  );
438
531
  }
439
532
 
440
- // Get sections for current page, enriched with schema defaults
441
- const sections = (currentPage.config.sections || [])
442
- .filter((s: any) => s.enabled !== false)
443
- .sort((a: any, b: any) => a.order - b.order)
444
- .map((section: any) => {
445
- const reg = getSectionComponent(
446
- themeExports,
447
- themePrefix,
448
- section.type,
449
- section.template || "default"
533
+ // Build section data (Gap 4)
534
+ const sectionData = {
535
+ theme: themeExports.themeConfig || {},
536
+ page: currentPage.config,
537
+ products: [],
538
+ blogs: [],
539
+ settings: {},
540
+ };
541
+
542
+ // Resolve layout sections (Gap 1)
543
+ const layoutConfig = themeExports.layoutConfig;
544
+ const headerSections = resolveSections(
545
+ layoutConfig?.headerSections || [],
546
+ themeExports,
547
+ themePrefix
548
+ );
549
+ const footerSections = resolveSections(
550
+ layoutConfig?.footerSections || [],
551
+ themeExports,
552
+ themePrefix
553
+ );
554
+
555
+ // Resolve page sections
556
+ const pageSections = resolveSections(
557
+ currentPage.config.sections || [],
558
+ themeExports,
559
+ themePrefix
560
+ );
561
+
562
+ // Build theme CSS (Gap 2)
563
+ const themeCSS = buildThemeStyleCSS(themeExports.themeConfig);
564
+
565
+ const renderSection = ({ section, registration }: any) => {
566
+ if (!registration) {
567
+ return (
568
+ <div
569
+ key={section.id}
570
+ style={{
571
+ padding: "2rem",
572
+ border: "2px dashed #f59e0b",
573
+ margin: "1rem 0",
574
+ background: "#fffbeb",
575
+ }}
576
+ >
577
+ <h3 style={{ margin: 0, fontSize: "1.2rem", color: "#d97706" }}>
578
+ Section Not Found: {section.type}
579
+ </h3>
580
+ </div>
450
581
  );
451
- const enriched = reg?.schema
452
- ? enrichSectionWithDefaults(section, reg.schema)
453
- : section;
454
- return { section: enriched, registration: reg };
455
- });
582
+ }
583
+ const { Component, schema, template } = registration;
584
+ return (
585
+ <Component
586
+ key={section.id}
587
+ section={section}
588
+ schema={schema}
589
+ template={template}
590
+ isEditing={false}
591
+ data={sectionData}
592
+ />
593
+ );
594
+ };
456
595
 
457
596
  return (
458
597
  <>
459
- {/* Render sections */}
460
- {sections.map(({ section, registration }: any) => {
461
- if (!registration) {
462
- return (
463
- <div
464
- key={section.id}
465
- style={{
466
- padding: "2rem",
467
- border: "2px dashed #f59e0b",
468
- margin: "1rem 0",
469
- background: "#fffbeb",
470
- }}
471
- >
472
- <h3 style={{ margin: 0, fontSize: "1.2rem", color: "#d97706" }}>
473
- Section Not Found: {section.type}
474
- </h3>
475
- </div>
476
- );
477
- }
478
- const { Component, schema, template } = registration;
479
- return (
480
- <Component
481
- key={section.id}
482
- section={section}
483
- schema={schema}
484
- template={template}
485
- isEditing={false}
486
- data={{}}
487
- />
488
- );
489
- })}
598
+ {/* Inject theme CSS variables (Gap 2) */}
599
+ {themeCSS && <style dangerouslySetInnerHTML={{ __html: themeCSS }} />}
600
+
601
+ {/* Header (Gap 1) */}
602
+ {headerSections.length > 0 && (
603
+ <header>{headerSections.map(renderSection)}</header>
604
+ )}
605
+
606
+ {/* Page sections */}
607
+ <main>{pageSections.map(renderSection)}</main>
608
+
609
+ {/* Footer (Gap 1) */}
610
+ {footerSections.length > 0 && (
611
+ <footer>{footerSections.map(renderSection)}</footer>
612
+ )}
490
613
  </>
491
614
  );
492
615
  }
493
616
 
494
-
495
- // ===== 7. MOUNT =====
617
+ // ===== 9. MOUNT =====
496
618
 
497
619
  const queryClient = new QueryClient({
498
620
  defaultOptions: {
package/package.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@onexapis/cli",
3
- "version": "1.1.16",
3
+ "version": "1.1.18",
4
4
  "description": "CLI tool for OneX theme development - scaffolds themes using @onexapis/core",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",
7
7
  "types": "./dist/index.d.ts",
8
8
  "bin": {
9
- "onex": "./bin/onex.js"
9
+ "onexthm": "./bin/onexthm.js"
10
10
  },
11
11
  "exports": {
12
12
  ".": {
@@ -15,6 +15,13 @@
15
15
  "require": "./dist/index.js"
16
16
  }
17
17
  },
18
+ "scripts": {
19
+ "build": "tsup",
20
+ "dev": "tsup --watch",
21
+ "type-check": "tsc --noEmit",
22
+ "lint": "eslint src --ext .ts",
23
+ "prepublishOnly": "pnpm run type-check && pnpm run build"
24
+ },
18
25
  "keywords": [
19
26
  "onex",
20
27
  "cli",
@@ -41,8 +48,9 @@
41
48
  "registry": "https://registry.npmjs.org/"
42
49
  },
43
50
  "dependencies": {
44
- "@tanstack/react-query": "^5.90.16",
45
51
  "@aws-sdk/client-s3": "^3.470.0",
52
+ "@onexapis/core": "workspace:*",
53
+ "@tanstack/react-query": "^5.90.16",
46
54
  "adm-zip": "^0.5.16",
47
55
  "archiver": "^7.0.1",
48
56
  "chalk": "^5.3.0",
@@ -52,6 +60,7 @@
52
60
  "ejs": "^3.1.10",
53
61
  "esbuild": "^0.25.0",
54
62
  "form-data": "^4.0.5",
63
+ "framer-motion": "^12.23.26",
55
64
  "fs-extra": "^11.2.0",
56
65
  "glob": "^10.3.10",
57
66
  "inquirer": "^9.2.12",
@@ -59,8 +68,7 @@
59
68
  "node-fetch": "^3.3.2",
60
69
  "open": "^10.1.0",
61
70
  "ora": "^8.0.1",
62
- "ws": "^8.18.0",
63
- "@onexapis/core": "1.0.2"
71
+ "ws": "^8.18.0"
64
72
  },
65
73
  "devDependencies": {
66
74
  "@types/adm-zip": "^0.5.7",
@@ -80,11 +88,5 @@
80
88
  ],
81
89
  "engines": {
82
90
  "node": ">=18.0.0"
83
- },
84
- "scripts": {
85
- "build": "tsup",
86
- "dev": "tsup --watch",
87
- "type-check": "tsc --noEmit",
88
- "lint": "eslint src --ext .ts"
89
91
  }
90
- }
92
+ }
@@ -1,4 +1,4 @@
1
- # API Configuration (optional — enables real data in preview)
1
+ # API Configuration (enables real data in preview)
2
2
  # Get your Company ID from the OneX dashboard
3
3
  NEXT_PUBLIC_API_URL=https://api-dev.onexeos.com
4
4
  NEXT_PUBLIC_COMPANY_ID=
@@ -0,0 +1,8 @@
1
+ {
2
+ "mcpServers": {
3
+ "onexthm": {
4
+ "command": "npx",
5
+ "args": ["@onexapis/theme-mcp"]
6
+ }
7
+ }
8
+ }