@jsonpages/cli 3.0.28 → 3.0.31

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.
@@ -27,107 +27,120 @@ END_OF_FILE_CONTENT
27
27
  mkdir -p "src"
28
28
  echo "Creating src/App.tsx..."
29
29
  cat << 'END_OF_FILE_CONTENT' > "src/App.tsx"
30
- /**
31
- * Thin Entry Point (Tenant).
32
- * Data from getHydratedData (file-backed or draft); assets from public/assets/images.
33
- */
34
- import { useState, useEffect } from 'react';
35
- import { JsonPagesEngine } from '@jsonpages/core';
36
- import type { LibraryImageEntry } from '@jsonpages/core';
37
- import { ComponentRegistry } from '@/lib/ComponentRegistry';
38
- import { SECTION_SCHEMAS } from '@/lib/schemas';
39
- import { addSectionConfig } from '@/lib/addSectionConfig';
40
- import { getHydratedData } from '@/lib/draftStorage';
41
- import type { JsonPagesConfig } from '@jsonpages/core';
42
- import type { PageConfig, SiteConfig, ThemeConfig, MenuConfig } from '@/types';
43
-
44
- import siteData from '@/data/config/site.json';
45
- import themeData from '@/data/config/theme.json';
46
- import menuData from '@/data/config/menu.json';
47
- import homeData from '@/data/pages/home.json';
48
-
49
- import tenantCss from './index.css?inline';
50
-
51
- const themeConfig = themeData as unknown as ThemeConfig;
52
- const menuConfig = menuData as unknown as MenuConfig;
53
-
54
- const TENANT_ID = 'alpha';
55
- const filePages: Record<string, PageConfig> = {
56
- home: homeData as unknown as PageConfig,
57
- };
58
- const fileSiteConfig = siteData as unknown as SiteConfig;
59
-
60
- const MAX_UPLOAD_SIZE_BYTES = 5 * 1024 * 1024; // 5MB, must match server
61
-
62
- function getInitialData() {
63
- return getHydratedData(TENANT_ID, filePages, fileSiteConfig);
64
- }
65
-
66
- function App() {
67
- const [{ pages, siteConfig }] = useState(getInitialData);
68
- const [assetsManifest, setAssetsManifest] = useState<LibraryImageEntry[]>([]);
69
-
70
- useEffect(() => {
71
- fetch('/api/list-assets')
72
- .then((r) => (r.ok ? r.json() : []))
73
- .then((list: LibraryImageEntry[]) => setAssetsManifest(Array.isArray(list) ? list : []))
74
- .catch(() => setAssetsManifest([]));
75
- }, []);
76
-
77
- const config: JsonPagesConfig = {
78
- tenantId: TENANT_ID,
79
- registry: ComponentRegistry as JsonPagesConfig['registry'],
80
- schemas: SECTION_SCHEMAS as unknown as JsonPagesConfig['schemas'],
81
- pages,
82
- siteConfig,
83
- themeConfig,
84
- menuConfig,
85
- themeCss: { tenant: tenantCss },
86
- addSection: addSectionConfig,
87
- assets: {
88
- assetsBaseUrl: '/assets',
89
- assetsManifest,
90
- async onAssetUpload(file: File): Promise<string> {
91
- if (!file.type.startsWith('image/')) {
92
- throw new Error('Invalid file type. Allowed: JPEG, PNG, WebP, GIF, SVG, AVIF.');
93
- }
94
- if (file.size > MAX_UPLOAD_SIZE_BYTES) {
95
- throw new Error(`File too large. Maximum size is ${MAX_UPLOAD_SIZE_BYTES / 1024 / 1024}MB.`);
96
- }
97
- const base64 = await new Promise<string>((resolve, reject) => {
98
- const reader = new FileReader();
99
- reader.onload = () => {
100
- const dataUrl = reader.result as string;
101
- const b64 = dataUrl.includes(',') ? dataUrl.split(',')[1] : '';
102
- resolve(b64 || '');
103
- };
104
- reader.onerror = () => reject(reader.error);
105
- reader.readAsDataURL(file);
106
- });
107
- const res = await fetch('/api/upload-asset', {
108
- method: 'POST',
109
- headers: { 'Content-Type': 'application/json' },
110
- body: JSON.stringify({
111
- filename: file.name,
112
- mimeType: file.type || undefined,
113
- data: base64,
114
- }),
115
- });
116
- const body = (await res.json().catch(() => ({}))) as { url?: string; error?: string };
117
- if (!res.ok) {
118
- throw new Error(body.error || `Upload failed: ${res.status}`);
119
- }
120
- if (typeof body.url !== 'string') {
121
- throw new Error('Invalid server response: missing url');
122
- }
123
- return body.url;
124
- },
125
- },
126
- };
127
-
128
- return <JsonPagesEngine config={config} />;
129
- }
130
-
30
+ /**
31
+ * Thin Entry Point (Tenant).
32
+ * Data from getHydratedData (file-backed or draft); assets from public/assets/images.
33
+ */
34
+ import { useState, useEffect } from 'react';
35
+ import { JsonPagesEngine } from '@jsonpages/core';
36
+ import type { LibraryImageEntry } from '@jsonpages/core';
37
+ import { ComponentRegistry } from '@/lib/ComponentRegistry';
38
+ import { SECTION_SCHEMAS } from '@/lib/schemas';
39
+ import { addSectionConfig } from '@/lib/addSectionConfig';
40
+ import { getHydratedData } from '@/lib/draftStorage';
41
+ import type { JsonPagesConfig, ProjectState } from '@jsonpages/core';
42
+ import type { PageConfig, SiteConfig, ThemeConfig, MenuConfig } from '@/types';
43
+
44
+ import siteData from '@/data/config/site.json';
45
+ import themeData from '@/data/config/theme.json';
46
+ import menuData from '@/data/config/menu.json';
47
+ import homeData from '@/data/pages/home.json';
48
+
49
+ import tenantCss from './index.css?inline';
50
+
51
+ const themeConfig = themeData as unknown as ThemeConfig;
52
+ const menuConfig = menuData as unknown as MenuConfig;
53
+
54
+ const TENANT_ID = 'alpha';
55
+ const filePages: Record<string, PageConfig> = {
56
+ home: homeData as unknown as PageConfig,
57
+ };
58
+ const fileSiteConfig = siteData as unknown as SiteConfig;
59
+
60
+ const MAX_UPLOAD_SIZE_BYTES = 5 * 1024 * 1024; // 5MB, must match server
61
+
62
+ function getInitialData() {
63
+ return getHydratedData(TENANT_ID, filePages, fileSiteConfig);
64
+ }
65
+
66
+ function App() {
67
+ const [{ pages, siteConfig }] = useState(getInitialData);
68
+ const [assetsManifest, setAssetsManifest] = useState<LibraryImageEntry[]>([]);
69
+
70
+ useEffect(() => {
71
+ fetch('/api/list-assets')
72
+ .then((r) => (r.ok ? r.json() : []))
73
+ .then((list: LibraryImageEntry[]) => setAssetsManifest(Array.isArray(list) ? list : []))
74
+ .catch(() => setAssetsManifest([]));
75
+ }, []);
76
+
77
+ const config: JsonPagesConfig = {
78
+ tenantId: TENANT_ID,
79
+ registry: ComponentRegistry as JsonPagesConfig['registry'],
80
+ schemas: SECTION_SCHEMAS as unknown as JsonPagesConfig['schemas'],
81
+ pages,
82
+ siteConfig,
83
+ themeConfig,
84
+ menuConfig,
85
+ themeCss: { tenant: tenantCss },
86
+ addSection: addSectionConfig,
87
+ persistence: {
88
+ async saveToFile(state: ProjectState, slug: string): Promise<void> {
89
+ const res = await fetch('/api/save-to-file', {
90
+ method: 'POST',
91
+ headers: { 'Content-Type': 'application/json' },
92
+ body: JSON.stringify({ projectState: state, slug }),
93
+ });
94
+ const body = (await res.json().catch(() => ({}))) as { error?: string };
95
+ if (!res.ok) {
96
+ throw new Error(body.error ?? `Save to file failed: ${res.status}`);
97
+ }
98
+ },
99
+ },
100
+ assets: {
101
+ assetsBaseUrl: '/assets',
102
+ assetsManifest,
103
+ async onAssetUpload(file: File): Promise<string> {
104
+ if (!file.type.startsWith('image/')) {
105
+ throw new Error('Invalid file type. Allowed: JPEG, PNG, WebP, GIF, SVG, AVIF.');
106
+ }
107
+ if (file.size > MAX_UPLOAD_SIZE_BYTES) {
108
+ throw new Error(`File too large. Maximum size is ${MAX_UPLOAD_SIZE_BYTES / 1024 / 1024}MB.`);
109
+ }
110
+ const base64 = await new Promise<string>((resolve, reject) => {
111
+ const reader = new FileReader();
112
+ reader.onload = () => {
113
+ const dataUrl = reader.result as string;
114
+ const b64 = dataUrl.includes(',') ? dataUrl.split(',')[1] : '';
115
+ resolve(b64 || '');
116
+ };
117
+ reader.onerror = () => reject(reader.error);
118
+ reader.readAsDataURL(file);
119
+ });
120
+ const res = await fetch('/api/upload-asset', {
121
+ method: 'POST',
122
+ headers: { 'Content-Type': 'application/json' },
123
+ body: JSON.stringify({
124
+ filename: file.name,
125
+ mimeType: file.type || undefined,
126
+ data: base64,
127
+ }),
128
+ });
129
+ const body = (await res.json().catch(() => ({}))) as { url?: string; error?: string };
130
+ if (!res.ok) {
131
+ throw new Error(body.error || `Upload failed: ${res.status}`);
132
+ }
133
+ if (typeof body.url !== 'string') {
134
+ throw new Error('Invalid server response: missing url');
135
+ }
136
+ return body.url;
137
+ },
138
+ },
139
+ };
140
+
141
+ return <JsonPagesEngine config={config} />;
142
+ }
143
+
131
144
  export default App;
132
145
  END_OF_FILE_CONTENT
133
146
  mkdir -p "src/components"
@@ -2316,85 +2329,85 @@ END_OF_FILE_CONTENT
2316
2329
  # SKIP: src/data/config/menu.json:Zone.Identifier è un file binario e non può essere convertito in testo.
2317
2330
  echo "Creating src/data/config/site.json..."
2318
2331
  cat << 'END_OF_FILE_CONTENT' > "src/data/config/site.json"
2319
- {
2320
- "identity": {
2321
- "title": "JsonPages",
2322
- "logoUrl": "/logo.svg"
2323
- },
2324
- "pages": [
2325
- {
2326
- "slug": "home",
2327
- "label": "Home"
2328
- },
2329
- {
2330
- "slug": "architecture",
2331
- "label": "Architecture"
2332
- },
2333
- {
2334
- "slug": "usage",
2335
- "label": "Usage"
2336
- }
2337
- ],
2338
- "header": {
2339
- "id": "global-header",
2340
- "type": "header",
2341
- "data": {
2342
- "logoText": "Json",
2343
- "logoHighlight": "Pages",
2344
- "logoIconText": "{ }",
2345
- "links": [
2346
- {
2347
- "label": "Architecture",
2348
- "href": "#architecture"
2349
- },
2350
- {
2351
- "label": "Products",
2352
- "href": "#products"
2353
- },
2354
- {
2355
- "label": "PA Ready",
2356
- "href": "#pa-ready"
2357
- },
2358
- {
2359
- "label": "Philosophy",
2360
- "href": "#philosophy"
2361
- }
2362
- ]
2363
- },
2364
- "settings": {
2365
- "sticky": true
2366
- }
2367
- },
2368
- "footer": {
2369
- "id": "global-footer",
2370
- "type": "footer",
2371
- "data": {
2372
- "brandText": "Json",
2373
- "brandHighlight": "Pages",
2374
- "copyright": "© 2026 JsonPages Ecosystem. Architected by Guido Filippo Serio.",
2375
- "links": [
2376
- {
2377
- "label": "Documentation",
2378
- "href": "#"
2379
- },
2380
- {
2381
- "label": "API Reference",
2382
- "href": "#"
2383
- },
2384
- {
2385
- "label": "Changelog",
2386
- "href": "#"
2387
- },
2388
- {
2389
- "label": "Privacy",
2390
- "href": "#"
2391
- }
2392
- ]
2393
- },
2394
- "settings": {
2395
- "showLogo": true
2396
- }
2397
- }
2332
+ {
2333
+ "identity": {
2334
+ "title": "JsonPages",
2335
+ "logoUrl": "/logo.svg"
2336
+ },
2337
+ "pages": [
2338
+ {
2339
+ "slug": "home",
2340
+ "label": "Home"
2341
+ },
2342
+ {
2343
+ "slug": "architecture",
2344
+ "label": "Architecture"
2345
+ },
2346
+ {
2347
+ "slug": "usage",
2348
+ "label": "Usage"
2349
+ }
2350
+ ],
2351
+ "header": {
2352
+ "id": "global-header",
2353
+ "type": "header",
2354
+ "data": {
2355
+ "logoText": "Json",
2356
+ "logoHighlight": "Pages",
2357
+ "logoIconText": "{ }",
2358
+ "links": [
2359
+ {
2360
+ "label": "Architecture",
2361
+ "href": "#architecture"
2362
+ },
2363
+ {
2364
+ "label": "Products",
2365
+ "href": "#products"
2366
+ },
2367
+ {
2368
+ "label": "PA Ready",
2369
+ "href": "#pa-ready"
2370
+ },
2371
+ {
2372
+ "label": "Philosophy",
2373
+ "href": "#philosophy"
2374
+ }
2375
+ ]
2376
+ },
2377
+ "settings": {
2378
+ "sticky": true
2379
+ }
2380
+ },
2381
+ "footer": {
2382
+ "id": "global-footer",
2383
+ "type": "footer",
2384
+ "data": {
2385
+ "brandText": "Json",
2386
+ "brandHighlight": "Pages",
2387
+ "copyright": "© 2026 JsonPages Ecosystem. Architected by Guido Filippo Serio.",
2388
+ "links": [
2389
+ {
2390
+ "label": "Documentation",
2391
+ "href": "#"
2392
+ },
2393
+ {
2394
+ "label": "API Reference",
2395
+ "href": "#"
2396
+ },
2397
+ {
2398
+ "label": "Changelog",
2399
+ "href": "#"
2400
+ },
2401
+ {
2402
+ "label": "Privacy",
2403
+ "href": "#"
2404
+ }
2405
+ ]
2406
+ },
2407
+ "settings": {
2408
+ "showLogo": true
2409
+ }
2410
+ }
2398
2411
  }
2399
2412
  END_OF_FILE_CONTENT
2400
2413
  # SKIP: src/data/config/site.json:Zone.Identifier è un file binario e non può essere convertito in testo.
@@ -2433,422 +2446,421 @@ END_OF_FILE_CONTENT
2433
2446
  mkdir -p "src/data/pages"
2434
2447
  echo "Creating src/data/pages/home.json..."
2435
2448
  cat << 'END_OF_FILE_CONTENT' > "src/data/pages/home.json"
2436
- {
2437
- "id": "home-page",
2438
- "slug": "home",
2439
- "meta": {
2440
- "title": "JsonPages — Global Authoring. Global Governance.",
2441
- "description": "A high-fidelity Site Factory that solves the scalability bottleneck of traditional CMS systems."
2442
- },
2443
- "sections": [
2444
- {
2445
- "id": "hero-main",
2446
- "type": "hero",
2447
- "data": {
2448
- "badge": "Composable UI Engine · v1.0",
2449
- "title": "Local Authoring.",
2450
- "titleHighlight": "Global Governance.",
2451
- "description": "JsonPages is a high-fidelity Site Factory that solves the scalability bottleneck of traditional CMS systems. Distribute, govern, and render UI components across hundreds of independent sites — without technical drift.",
2452
- "ctas": [
2453
- {
2454
- "label": "Explore Products →",
2455
- "href": "#products",
2456
- "variant": "primary"
2457
- },
2458
- {
2459
- "label": "Read the Spec",
2460
- "href": "#architecture",
2461
- "variant": "secondary"
2462
- }
2463
- ],
2464
- "metrics": [
2465
- {
2466
- "val": "100+",
2467
- "label": "Pages per tenant"
2468
- },
2469
- {
2470
- "val": "0ms",
2471
- "label": "Runtime overhead"
2472
- },
2473
- {
2474
- "val": "∞",
2475
- "label": "Scalable blocks"
2476
- }
2477
- ]
2478
- },
2479
- "settings": {}
2480
- },
2481
-
2482
- {
2483
- "id": "problem-statement",
2484
- "type": "problem-statement",
2485
- "data": {
2486
- "siloGroups": [
2487
- {
2488
- "blocks": [
2489
- {
2490
- "label": "Agency A Stack",
2491
- "variant": "red"
2492
- },
2493
- {
2494
- "label": "Custom CMS",
2495
- "variant": "red"
2496
- },
2497
- {
2498
- "label": "PHP Backend",
2499
- "variant": "red"
2500
- }
2501
- ],
2502
- "label": "Silo #1"
2503
- },
2504
- {
2505
- "blocks": [
2506
- {
2507
- "label": "Agency B Tools",
2508
- "variant": "amber"
2509
- },
2510
- {
2511
- "label": "WordPress",
2512
- "variant": "amber"
2513
- },
2514
- {
2515
- "label": "LAMP Stack",
2516
- "variant": "amber"
2517
- }
2518
- ],
2519
- "label": "Silo #2"
2520
- },
2521
- {
2522
- "blocks": [
2523
- {
2524
- "label": "Agency C Kit",
2525
- "variant": "green"
2526
- },
2527
- {
2528
- "label": "React App",
2529
- "variant": "green"
2530
- },
2531
- {
2532
- "label": "Node.js",
2533
- "variant": "green"
2534
- }
2535
- ],
2536
- "label": "Silo #3"
2537
- }
2538
- ],
2539
- "title": "Traditional CMS systems create closed silos.",
2540
- "paragraphs": [
2541
- {
2542
- "text": "When global brands delegate web projects to multiple agencies, each agency picks its own stack, its own conventions, its own deployment pipeline. The result is a portfolio of ungovernable sites with inconsistent quality and unpredictable maintenance costs.",
2543
- "isBold": false
2544
- },
2545
- {
2546
- "text": "JsonPages eliminates this by providing a centralized protocol to distribute, govern, and render UI components — ensuring every site, regardless of who builds it, conforms to a single typed ontology.",
2547
- "isBold": true
2548
- }
2549
- ],
2550
- "anchorId": "aa"
2551
- },
2552
- "settings": {}
2553
- },
2554
- {
2555
- "id": "architecture-pillars",
2556
- "type": "pillars-grid",
2557
- "data": {
2558
- "anchorId": "architecture",
2559
- "label": "Core Architecture",
2560
- "title": "Three Pillars of Composability",
2561
- "description": "A contract-first architecture designed to scale without core refactoring.",
2562
- "pillars": [
2563
- {
2564
- "icon": "box",
2565
- "iconVariant": "split",
2566
- "title": "The Machine-Readable Blueprint. 📐",
2567
- "description": "We enforce a strict file-system ontology that physically separates Global Governance from Local Content. By utilizing \"Dumb Components\" (CIP), we create a deterministic environment where the UI is a predictable function of data. This protocol allows AI agents to understand your site structure instantly, eliminating the \"guessing game\" of traditional web development.",
2568
- "tag": "The Foundation",
2569
- "tagVariant": "core"
2570
- },
2571
- {
2572
- "icon": "sparkles",
2573
- "iconVariant": "registry",
2574
- "title": "The Semantic Brain. 🧠",
2575
- "description": "We reject hardcoded Union Types. The Kernel defines an open registry where independent modules inject their DNA via Zod schemas. This provides the AI with a precise, type-safe vocabulary to build brand-compliant interfaces. Because the registry is the Single Source of Truth, the AI knows exactly which components exist and how to configure them without ever touching the core code.",
2576
- "tag": "The Vocabulary",
2577
- "tagVariant": "pattern"
2578
- },
2579
- {
2580
- "icon": "zap",
2581
- "iconVariant": "federation",
2582
- "title": "The 5-Minute Tenant. 🤖",
2583
- "description": "AI doesn't guess; it follows the Protocol. Because our ecosystem is governed by 5 deterministic standards, agents like Claude can project a complex, multi-page, brand-compliant tenant in under 5 minutes. The result is 100% Type-Safe JSON that perfectly matches your schemas, delivering a production-ready site with zero technical drift and an instant polymorphic CMS.",
2584
- "tag": "The Multiplier",
2585
- "tagVariant": "enterprise"
2586
- }
2587
- ]
2588
- },
2589
- "settings": {}
2590
- },
2591
- {
2592
- "id": "layered-architecture",
2593
- "type": "arch-layers",
2594
- "data": {
2595
- "anchorId": "layers",
2596
- "label": "Engineering Layers",
2597
- "title": "Strict Boundaries. Clean Contracts.",
2598
- "description": "Three architectural layers maintain the separation between engine and implementation.",
2599
- "layers": [
2600
- {
2601
- "number": "0",
2602
- "layerLevel": "l0",
2603
- "title": "The Immutable Kernel",
2604
- "description": "Defines the base structure of a Section — ID, Type, Payload, Settings — and exports the computed union. Zero knowledge of UI implementation or business logic."
2605
- },
2606
- {
2607
- "number": "1",
2608
- "layerLevel": "l1",
2609
- "title": "The Standard Library",
2610
- "description": "Fundamental components (Hero, Text, Grid) used across 90% of projects. Augments the Kernel's registry while remaining part of the internal ecosystem."
2611
- },
2612
- {
2613
- "number": "2",
2614
- "layerLevel": "l2",
2615
- "title": "User-Land Extensions",
2616
- "description": "The enabling layer. Third-party developers define specialized blocks — StockTickers, CRM Forms — in their own repositories. These extensions plug into the Kernel at compile-time."
2617
- }
2618
- ],
2619
- "codeFilename": "libs/shared-data/src/lib/core.ts",
2620
- "codeLines": [
2621
- {
2622
- "content": "// 🛡️ KERNEL — Single Source of Truth",
2623
- "tokenType": "comment"
2624
- },
2625
- {
2626
- "content": "",
2627
- "tokenType": "plain"
2628
- },
2629
- {
2630
- "content": "export interface SectionDataRegistry {",
2631
- "tokenType": "keyword"
2632
- },
2633
- {
2634
- "content": " // Open Registry: modules inject here",
2635
- "tokenType": "comment"
2636
- },
2637
- {
2638
- "content": "}",
2639
- "tokenType": "plain"
2640
- },
2641
- {
2642
- "content": "",
2643
- "tokenType": "plain"
2644
- },
2645
- {
2646
- "content": "export interface BaseSection<K extends keyof SectionDataRegistry> {",
2647
- "tokenType": "keyword"
2648
- },
2649
- {
2650
- "content": " id: string;",
2651
- "tokenType": "type"
2652
- },
2653
- {
2654
- "content": " type: K;",
2655
- "tokenType": "type"
2656
- },
2657
- {
2658
- "content": " data: SectionDataRegistry[K];",
2659
- "tokenType": "type"
2660
- },
2661
- {
2662
- "content": " settings?: SectionSettings;",
2663
- "tokenType": "type"
2664
- },
2665
- {
2666
- "content": "}",
2667
- "tokenType": "plain"
2668
- },
2669
- {
2670
- "content": "",
2671
- "tokenType": "plain"
2672
- },
2673
- {
2674
- "content": "// Computed Union — built automatically from registry",
2675
- "tokenType": "comment"
2676
- },
2677
- {
2678
- "content": "export type Section =",
2679
- "tokenType": "keyword"
2680
- },
2681
- {
2682
- "content": " { [K in keyof SectionDataRegistry]: BaseSection<K> }",
2683
- "tokenType": "type"
2684
- },
2685
- {
2686
- "content": " [keyof SectionDataRegistry];",
2687
- "tokenType": "operator"
2688
- }
2689
- ]
2690
- },
2691
- "settings": {}
2692
- },
2693
- {
2694
- "id": "product-triad",
2695
- "type": "product-triad",
2696
- "data": {
2697
- "anchorId": "products",
2698
- "label": "Product Triad",
2699
- "title": "One Protocol. Three Products.",
2700
- "description": "From solo developers to global enterprises — the same underlying ontology powers every tier.",
2701
- "products": [
2702
- {
2703
- "tier": "Starter",
2704
- "name": "JsonPage",
2705
- "price": "NPM",
2706
- "priceSuffix": "Local Install",
2707
- "delivery": "Static Export · Zero Runtime",
2708
- "features": [
2709
- {
2710
- "text": "Local Authoring SDK"
2711
- },
2712
- {
2713
- "text": "Zero-Runtime HTML Bake"
2714
- },
2715
- {
2716
- "text": "Pure HTML/CSS Export"
2717
- },
2718
- {
2719
- "text": "CDN-Ready Delivery"
2720
- },
2721
- {
2722
- "text": "Full Code Ownership"
2723
- }
2724
- ],
2725
- "featured": false
2726
- },
2727
- {
2728
- "tier": "Professional",
2729
- "name": "JsonPages",
2730
- "price": "SaaS",
2731
- "priceSuffix": "/month",
2732
- "delivery": "Managed Cloud · Multi-Tenant",
2733
- "features": [
2734
- {
2735
- "text": "Multi-Tenant Hosting"
2736
- },
2737
- {
2738
- "text": "Premium Block Library"
2739
- },
2740
- {
2741
- "text": "SchemaUI Components"
2742
- },
2743
- {
2744
- "text": "In-Context Editing"
2745
- },
2746
- {
2747
- "text": "AI-Native Protocol"
2748
- },
2749
- {
2750
- "text": "Tailwind v4 & BI Engine"
2751
- }
2752
- ],
2753
- "featured": false
2754
- },
2755
- {
2756
- "tier": "Enterprise",
2757
- "name": "The Platform",
2758
- "price": "Custom",
2759
- "delivery": "Private Cloud · Enterprise Governance",
2760
- "features": [
2761
- {
2762
- "text": "Private Federated Registry"
2763
- },
2764
- {
2765
- "text": "Module Federation"
2766
- },
2767
- {
2768
- "text": "Runtime Component Injection"
2769
- },
2770
- {
2771
- "text": "Custom Brand Guidelines"
2772
- },
2773
- {
2774
- "text": "Dedicated SLA & Support"
2775
- },
2776
- {
2777
- "text": "SSO & RBAC"
2778
- }
2779
- ],
2780
- "featured": false
2781
- }
2782
- ]
2783
- },
2784
- "settings": {}
2785
- },
2786
- {
2787
- "id": "pa-ready",
2788
- "type": "pa-section",
2789
- "data": {
2790
- "anchorId": "pa-ready",
2791
- "label": "Public Sector",
2792
- "title": "PA Ready — One Platform, Two Markets",
2793
- "subtitle": "The Only Builder with Native Bootstrap Italia.",
2794
- "paragraphs": [
2795
- {
2796
- "text": "By switching the rendering engine to Bootstrap Italia, System Integrators use the same high-efficiency workflow to deliver WCAG-compliant sites for Municipalities and Government bodies — without rewriting backend logic."
2797
- },
2798
- {
2799
- "text": "The same JSON ontology drives both the Tailwind-powered private sector and the Bootstrap Italia public sector pipeline. This is not flexibility — this is Business Continuity."
2800
- }
2801
- ],
2802
- "badges": [
2803
- {
2804
- "label": "PNRR Ready"
2805
- },
2806
- {
2807
- "label": "WCAG 2.1 AA"
2808
- },
2809
- {
2810
- "label": "AgID Compliant"
2811
- }
2812
- ],
2813
- "engines": [
2814
- {
2815
- "label": "Tailwind v4",
2816
- "variant": "tailwind"
2817
- },
2818
- {
2819
- "label": "Bootstrap Italia",
2820
- "variant": "bootstrap"
2821
- }
2822
- ],
2823
- "codeSnippet": "{\n \"type\": \"hero\",\n \"data\": { \"title\": \"...\" },\n \"settings\": { \"engine\": \"auto\" }\n}"
2824
- },
2825
- "settings": {}
2826
- },
2827
- {
2828
- "id": "philosophy",
2829
- "type": "philosophy",
2830
- "data": {
2831
- "anchorId": "philosophy",
2832
- "label": "Our Philosophy",
2833
- "title": "Governance Over Captivity",
2834
- "quote": "We sell the infrastructure to manage UI — not the right to access the code.",
2835
- "quoteHighlightWord": "manage",
2836
- "description": "Unlike closed-garden platforms, JsonPages provides an Eject Button by design. The Bake Engine exports your site as pure HTML/CSS with zero backend requirements. If you leave the SaaS, the shared ontology and SDK ensure you remain fully operational and independent. We earn your loyalty through value — not lock-in."
2837
- },
2838
- "settings": {}
2839
- },
2840
- {
2841
- "id": "cta-final",
2842
- "type": "cta-banner",
2843
- "data": {
2844
- "anchorId": "start",
2845
- "label": "Local Authoring Global Governance",
2846
- "title": "Ready to Govern at Scale?",
2847
- "description": "Whether you're building a single site or managing a portfolio of hundreds — JsonPages scales with your ambition."
2848
- },
2849
- "settings": {}
2850
- }
2851
- ]
2449
+ {
2450
+ "id": "home-page",
2451
+ "slug": "home",
2452
+ "meta": {
2453
+ "title": "JsonPages — Global Authoring. Global Governance.",
2454
+ "description": "A high-fidelity Site Factory that solves the scalability bottleneck of traditional CMS systems."
2455
+ },
2456
+ "sections": [
2457
+ {
2458
+ "id": "hero-main",
2459
+ "type": "hero",
2460
+ "data": {
2461
+ "badge": "V1.3",
2462
+ "title": "Local Authoring.",
2463
+ "titleHighlight": "Global Governance.",
2464
+ "description": "JsonPages is a high-fidelity Site Factory that solves the scalability bottleneck of traditional CMS systems. Distribute, govern, and render UI components across hundreds of independent sites — without technical drift.",
2465
+ "ctas": [
2466
+ {
2467
+ "label": "Explore Products →",
2468
+ "href": "#products",
2469
+ "variant": "primary"
2470
+ },
2471
+ {
2472
+ "label": "Read the Spec",
2473
+ "href": "#architecture",
2474
+ "variant": "secondary"
2475
+ }
2476
+ ],
2477
+ "metrics": [
2478
+ {
2479
+ "val": "100+",
2480
+ "label": "Pages per tenant"
2481
+ },
2482
+ {
2483
+ "val": "0ms",
2484
+ "label": "Runtime overhead"
2485
+ },
2486
+ {
2487
+ "val": "∞",
2488
+ "label": "Scalable blocks"
2489
+ }
2490
+ ]
2491
+ },
2492
+ "settings": {}
2493
+ },
2494
+ {
2495
+ "id": "problem-statement",
2496
+ "type": "problem-statement",
2497
+ "data": {
2498
+ "siloGroups": [
2499
+ {
2500
+ "blocks": [
2501
+ {
2502
+ "label": "Agency A Stack",
2503
+ "variant": "red"
2504
+ },
2505
+ {
2506
+ "label": "Custom CMS",
2507
+ "variant": "red"
2508
+ },
2509
+ {
2510
+ "label": "PHP Backend",
2511
+ "variant": "red"
2512
+ }
2513
+ ],
2514
+ "label": "Silo #1"
2515
+ },
2516
+ {
2517
+ "blocks": [
2518
+ {
2519
+ "label": "Agency B Tools",
2520
+ "variant": "amber"
2521
+ },
2522
+ {
2523
+ "label": "WordPress",
2524
+ "variant": "amber"
2525
+ },
2526
+ {
2527
+ "label": "LAMP Stack",
2528
+ "variant": "amber"
2529
+ }
2530
+ ],
2531
+ "label": "Silo #2"
2532
+ },
2533
+ {
2534
+ "blocks": [
2535
+ {
2536
+ "label": "Agency C Kit",
2537
+ "variant": "green"
2538
+ },
2539
+ {
2540
+ "label": "React App",
2541
+ "variant": "green"
2542
+ },
2543
+ {
2544
+ "label": "Node.js",
2545
+ "variant": "green"
2546
+ }
2547
+ ],
2548
+ "label": "Silo #3"
2549
+ }
2550
+ ],
2551
+ "title": "Traditional CMS systems create closed silos.",
2552
+ "paragraphs": [
2553
+ {
2554
+ "text": "When global brands delegate web projects to multiple agencies, each agency picks its own stack, its own conventions, its own deployment pipeline. The result is a portfolio of ungovernable sites with inconsistent quality and unpredictable maintenance costs.",
2555
+ "isBold": false
2556
+ },
2557
+ {
2558
+ "text": "JsonPages eliminates this by providing a centralized protocol to distribute, govern, and render UI components — ensuring every site, regardless of who builds it, conforms to a single typed ontology.",
2559
+ "isBold": true
2560
+ }
2561
+ ],
2562
+ "anchorId": "aa"
2563
+ },
2564
+ "settings": {}
2565
+ },
2566
+ {
2567
+ "id": "architecture-pillars",
2568
+ "type": "pillars-grid",
2569
+ "data": {
2570
+ "anchorId": "architecture",
2571
+ "label": "Core Architecture",
2572
+ "title": "Three Pillars of Composability",
2573
+ "description": "A contract-first architecture designed to scale without core refactoring.",
2574
+ "pillars": [
2575
+ {
2576
+ "icon": "box",
2577
+ "iconVariant": "split",
2578
+ "title": "The Machine-Readable Blueprint. 📐",
2579
+ "description": "We enforce a strict file-system ontology that physically separates Global Governance from Local Content. By utilizing \"Dumb Components\" (CIP), we create a deterministic environment where the UI is a predictable function of data. This protocol allows AI agents to understand your site structure instantly, eliminating the \"guessing game\" of traditional web development.",
2580
+ "tag": "The Foundation",
2581
+ "tagVariant": "core"
2582
+ },
2583
+ {
2584
+ "icon": "sparkles",
2585
+ "iconVariant": "registry",
2586
+ "title": "The Semantic Brain. 🧠",
2587
+ "description": "We reject hardcoded Union Types. The Kernel defines an open registry where independent modules inject their DNA via Zod schemas. This provides the AI with a precise, type-safe vocabulary to build brand-compliant interfaces. Because the registry is the Single Source of Truth, the AI knows exactly which components exist and how to configure them without ever touching the core code.",
2588
+ "tag": "The Vocabulary",
2589
+ "tagVariant": "pattern"
2590
+ },
2591
+ {
2592
+ "icon": "zap",
2593
+ "iconVariant": "federation",
2594
+ "title": "The 5-Minute Tenant. 🤖",
2595
+ "description": "AI doesn't guess; it follows the Protocol. Because our ecosystem is governed by 5 deterministic standards, agents like Claude can project a complex, multi-page, brand-compliant tenant in under 5 minutes. The result is 100% Type-Safe JSON that perfectly matches your schemas, delivering a production-ready site with zero technical drift and an instant polymorphic CMS.",
2596
+ "tag": "The Multiplier",
2597
+ "tagVariant": "enterprise"
2598
+ }
2599
+ ]
2600
+ },
2601
+ "settings": {}
2602
+ },
2603
+ {
2604
+ "id": "layered-architecture",
2605
+ "type": "arch-layers",
2606
+ "data": {
2607
+ "anchorId": "layers",
2608
+ "label": "Engineering Layers",
2609
+ "title": "Strict Boundaries. Clean Contracts.",
2610
+ "description": "Three architectural layers maintain the separation between engine and implementation.",
2611
+ "layers": [
2612
+ {
2613
+ "number": "0",
2614
+ "layerLevel": "l0",
2615
+ "title": "The Immutable Kernel",
2616
+ "description": "Defines the base structure of a Section — ID, Type, Payload, Settings — and exports the computed union. Zero knowledge of UI implementation or business logic."
2617
+ },
2618
+ {
2619
+ "number": "1",
2620
+ "layerLevel": "l1",
2621
+ "title": "The Standard Library",
2622
+ "description": "Fundamental components (Hero, Text, Grid) used across 90% of projects. Augments the Kernel's registry while remaining part of the internal ecosystem."
2623
+ },
2624
+ {
2625
+ "number": "2",
2626
+ "layerLevel": "l2",
2627
+ "title": "User-Land Extensions",
2628
+ "description": "The enabling layer. Third-party developers define specialized blocks — StockTickers, CRM Forms — in their own repositories. These extensions plug into the Kernel at compile-time."
2629
+ }
2630
+ ],
2631
+ "codeFilename": "libs/shared-data/src/lib/core.ts",
2632
+ "codeLines": [
2633
+ {
2634
+ "content": "// 🛡️ KERNEL — Single Source of Truth",
2635
+ "tokenType": "comment"
2636
+ },
2637
+ {
2638
+ "content": "",
2639
+ "tokenType": "plain"
2640
+ },
2641
+ {
2642
+ "content": "export interface SectionDataRegistry {",
2643
+ "tokenType": "keyword"
2644
+ },
2645
+ {
2646
+ "content": " // Open Registry: modules inject here",
2647
+ "tokenType": "comment"
2648
+ },
2649
+ {
2650
+ "content": "}",
2651
+ "tokenType": "plain"
2652
+ },
2653
+ {
2654
+ "content": "",
2655
+ "tokenType": "plain"
2656
+ },
2657
+ {
2658
+ "content": "export interface BaseSection<K extends keyof SectionDataRegistry> {",
2659
+ "tokenType": "keyword"
2660
+ },
2661
+ {
2662
+ "content": " id: string;",
2663
+ "tokenType": "type"
2664
+ },
2665
+ {
2666
+ "content": " type: K;",
2667
+ "tokenType": "type"
2668
+ },
2669
+ {
2670
+ "content": " data: SectionDataRegistry[K];",
2671
+ "tokenType": "type"
2672
+ },
2673
+ {
2674
+ "content": " settings?: SectionSettings;",
2675
+ "tokenType": "type"
2676
+ },
2677
+ {
2678
+ "content": "}",
2679
+ "tokenType": "plain"
2680
+ },
2681
+ {
2682
+ "content": "",
2683
+ "tokenType": "plain"
2684
+ },
2685
+ {
2686
+ "content": "// Computed Union — built automatically from registry",
2687
+ "tokenType": "comment"
2688
+ },
2689
+ {
2690
+ "content": "export type Section =",
2691
+ "tokenType": "keyword"
2692
+ },
2693
+ {
2694
+ "content": " { [K in keyof SectionDataRegistry]: BaseSection<K> }",
2695
+ "tokenType": "type"
2696
+ },
2697
+ {
2698
+ "content": " [keyof SectionDataRegistry];",
2699
+ "tokenType": "operator"
2700
+ }
2701
+ ]
2702
+ },
2703
+ "settings": {}
2704
+ },
2705
+ {
2706
+ "id": "product-triad",
2707
+ "type": "product-triad",
2708
+ "data": {
2709
+ "anchorId": "products",
2710
+ "label": "Product Triad",
2711
+ "title": "One Protocol. Three Products.",
2712
+ "description": "From solo developers to global enterprises — the same underlying ontology powers every tier.",
2713
+ "products": [
2714
+ {
2715
+ "tier": "Starter",
2716
+ "name": "JsonPage",
2717
+ "price": "NPM",
2718
+ "priceSuffix": "Local Install",
2719
+ "delivery": "Static Export · Zero Runtime",
2720
+ "features": [
2721
+ {
2722
+ "text": "Local Authoring SDK"
2723
+ },
2724
+ {
2725
+ "text": "Zero-Runtime HTML Bake"
2726
+ },
2727
+ {
2728
+ "text": "Pure HTML/CSS Export"
2729
+ },
2730
+ {
2731
+ "text": "CDN-Ready Delivery"
2732
+ },
2733
+ {
2734
+ "text": "Full Code Ownership"
2735
+ }
2736
+ ],
2737
+ "featured": false
2738
+ },
2739
+ {
2740
+ "tier": "Professional",
2741
+ "name": "JsonPages",
2742
+ "price": "SaaS",
2743
+ "priceSuffix": "/month",
2744
+ "delivery": "Managed Cloud · Multi-Tenant",
2745
+ "features": [
2746
+ {
2747
+ "text": "Multi-Tenant Hosting"
2748
+ },
2749
+ {
2750
+ "text": "Premium Block Library"
2751
+ },
2752
+ {
2753
+ "text": "SchemaUI Components"
2754
+ },
2755
+ {
2756
+ "text": "In-Context Editing"
2757
+ },
2758
+ {
2759
+ "text": "AI-Native Protocol"
2760
+ },
2761
+ {
2762
+ "text": "Tailwind v4 & BI Engine"
2763
+ }
2764
+ ],
2765
+ "featured": false
2766
+ },
2767
+ {
2768
+ "tier": "Enterprise",
2769
+ "name": "The Platform",
2770
+ "price": "Custom",
2771
+ "delivery": "Private Cloud · Enterprise Governance",
2772
+ "features": [
2773
+ {
2774
+ "text": "Private Federated Registry"
2775
+ },
2776
+ {
2777
+ "text": "Module Federation"
2778
+ },
2779
+ {
2780
+ "text": "Runtime Component Injection"
2781
+ },
2782
+ {
2783
+ "text": "Custom Brand Guidelines"
2784
+ },
2785
+ {
2786
+ "text": "Dedicated SLA & Support"
2787
+ },
2788
+ {
2789
+ "text": "SSO & RBAC"
2790
+ }
2791
+ ],
2792
+ "featured": false
2793
+ }
2794
+ ]
2795
+ },
2796
+ "settings": {}
2797
+ },
2798
+ {
2799
+ "id": "pa-ready",
2800
+ "type": "pa-section",
2801
+ "data": {
2802
+ "anchorId": "pa-ready",
2803
+ "label": "Public Sector",
2804
+ "title": "PA Ready — One Platform, Two Markets",
2805
+ "subtitle": "The Only Builder with Native Bootstrap Italia.",
2806
+ "paragraphs": [
2807
+ {
2808
+ "text": "By switching the rendering engine to Bootstrap Italia, System Integrators use the same high-efficiency workflow to deliver WCAG-compliant sites for Municipalities and Government bodies — without rewriting backend logic."
2809
+ },
2810
+ {
2811
+ "text": "The same JSON ontology drives both the Tailwind-powered private sector and the Bootstrap Italia public sector pipeline. This is not flexibility — this is Business Continuity."
2812
+ }
2813
+ ],
2814
+ "badges": [
2815
+ {
2816
+ "label": "PNRR Ready"
2817
+ },
2818
+ {
2819
+ "label": "WCAG 2.1 AA"
2820
+ },
2821
+ {
2822
+ "label": "AgID Compliant"
2823
+ }
2824
+ ],
2825
+ "engines": [
2826
+ {
2827
+ "label": "Tailwind v4",
2828
+ "variant": "tailwind"
2829
+ },
2830
+ {
2831
+ "label": "Bootstrap Italia",
2832
+ "variant": "bootstrap"
2833
+ }
2834
+ ],
2835
+ "codeSnippet": "{\n \"type\": \"hero\",\n \"data\": { \"title\": \"...\" },\n \"settings\": { \"engine\": \"auto\" }\n}"
2836
+ },
2837
+ "settings": {}
2838
+ },
2839
+ {
2840
+ "id": "philosophy",
2841
+ "type": "philosophy",
2842
+ "data": {
2843
+ "anchorId": "philosophy",
2844
+ "label": "Our Philosophy",
2845
+ "title": "Governance Over Captivity",
2846
+ "quote": "We sell the infrastructure to manage UI — not the right to access the code.",
2847
+ "quoteHighlightWord": "manage",
2848
+ "description": "Unlike closed-garden platforms, JsonPages provides an Eject Button by design. The Bake Engine exports your site as pure HTML/CSS with zero backend requirements. If you leave the SaaS, the shared ontology and SDK ensure you remain fully operational and independent. We earn your loyalty through value — not lock-in."
2849
+ },
2850
+ "settings": {}
2851
+ },
2852
+ {
2853
+ "id": "cta-final",
2854
+ "type": "cta-banner",
2855
+ "data": {
2856
+ "anchorId": "start",
2857
+ "label": "Local Authoring Global Governance",
2858
+ "title": "Ready to Govern at Scale?",
2859
+ "description": "Whether you're building a single site or managing a portfolio of hundreds — JsonPages scales with your ambition."
2860
+ },
2861
+ "settings": {}
2862
+ }
2863
+ ]
2852
2864
  }
2853
2865
  END_OF_FILE_CONTENT
2854
2866
  # SKIP: src/data/pages/home.json:Zone.Identifier è un file binario e non può essere convertito in testo.
@@ -2881,180 +2893,180 @@ export const useDocumentMeta = (meta: PageMeta): void => {
2881
2893
  END_OF_FILE_CONTENT
2882
2894
  echo "Creating src/index.css..."
2883
2895
  cat << 'END_OF_FILE_CONTENT' > "src/index.css"
2884
- @import "tailwindcss";
2885
-
2886
- @source "./**/*.tsx";
2887
-
2888
- @theme {
2889
- /*
2890
- 🎯 MASTER MAPPING (V2.7 Landing)
2891
- */
2892
- --color-background: var(--background);
2893
- --color-foreground: var(--foreground);
2894
-
2895
- --color-card: var(--card);
2896
- --color-card-foreground: var(--card-foreground);
2897
-
2898
- --color-primary: var(--primary);
2899
- --color-primary-foreground: var(--primary-foreground);
2900
-
2901
- --color-secondary: var(--secondary);
2902
- --color-secondary-foreground: var(--secondary-foreground);
2903
-
2904
- --color-muted: var(--muted);
2905
- --color-muted-foreground: var(--muted-foreground);
2906
-
2907
- --color-accent: var(--accent);
2908
- --color-border: var(--border);
2909
-
2910
- --radius-lg: var(--radius);
2911
- --radius-md: calc(var(--radius) - 2px);
2912
- --radius-sm: calc(var(--radius) - 4px);
2913
-
2914
- --font-primary: var(--theme-font-primary);
2915
- --font-mono: var(--theme-font-mono);
2916
-
2917
- /*
2918
- 🔧 DISPLAY FONT — Forward-compatible workaround
2919
- theme-manager.ts does NOT inject --theme-font-display (Skeleton v2.7 gap).
2920
- The var() falls through to the hardcoded fallback today.
2921
- When Skeleton v2.8 wires display into theme-manager, the var() will resolve
2922
- automatically and the fallback becomes dead code.
2923
- */
2924
- --font-display: var(--theme-font-display, 'Playfair Display', Georgia, serif);
2925
- }
2926
-
2927
- /*
2928
- 🌍 TENANT BRAND TOKENS (JSP 1.5)
2929
- */
2930
- :root {
2931
- --background: var(--theme-background);
2932
- --foreground: var(--theme-text);
2933
- --card: var(--theme-surface);
2934
- --card-foreground: var(--theme-text);
2935
- --primary: var(--theme-primary);
2936
- --primary-foreground: oklch(0.98 0 0);
2937
- --secondary: var(--theme-secondary);
2938
- --secondary-foreground: var(--theme-text);
2939
- --muted: var(--theme-surface-alt);
2940
- --muted-foreground: var(--theme-text-muted);
2941
- --border: var(--theme-border);
2942
- --radius: 0.45rem;
2943
-
2944
- /*
2945
- 🔧 ACCENT CHAIN — Forward-compatible workaround
2946
- theme-manager.ts already injects --theme-accent on :root,
2947
- but the original index.css never bridged it into the semantic layer.
2948
- This closes the gap: --theme-accent → --accent → --color-accent.
2949
- Falls back to --theme-primary if accent is undefined.
2950
- */
2951
- --accent: var(--theme-accent, var(--theme-primary));
2952
- }
2953
-
2954
- @layer base {
2955
- * { border-color: var(--border); }
2956
- body {
2957
- background-color: var(--background);
2958
- color: var(--foreground);
2959
- font-family: var(--font-primary);
2960
- line-height: 1.7;
2961
- overflow-x: hidden;
2962
- @apply antialiased;
2963
- }
2964
- }
2965
-
2966
- /* ==========================================================================
2967
- FONT DISPLAY UTILITY
2968
- Maps .font-display class to the display font family (Playfair Display)
2969
- ========================================================================== */
2970
- .font-display {
2971
- font-family: var(--font-display, var(--font-primary));
2972
- }
2973
-
2974
- /* ==========================================================================
2975
- LANDING ANIMATIONS
2976
- ========================================================================== */
2977
- @keyframes jp-fadeUp {
2978
- from { opacity: 0; transform: translateY(30px); }
2979
- to { opacity: 1; transform: translateY(0); }
2980
- }
2981
-
2982
- @keyframes jp-pulseDot {
2983
- 0%, 100% { opacity: 1; }
2984
- 50% { opacity: 0.3; }
2985
- }
2986
-
2987
- .jp-animate-in {
2988
- opacity: 0;
2989
- animation: jp-fadeUp 0.7s ease forwards;
2990
- }
2991
- .jp-d1 { animation-delay: 0.1s; }
2992
- .jp-d2 { animation-delay: 0.2s; }
2993
- .jp-d3 { animation-delay: 0.3s; }
2994
- .jp-d4 { animation-delay: 0.4s; }
2995
- .jp-d5 { animation-delay: 0.5s; }
2996
-
2997
- .jp-pulse-dot {
2998
- animation: jp-pulseDot 2s ease infinite;
2999
- }
3000
-
3001
- /* ==========================================================================
3002
- SMOOTH SCROLL
3003
- ========================================================================== */
3004
- html {
3005
- scroll-behavior: smooth;
3006
- }
3007
-
3008
- /* ==========================================================================
3009
- ICE ADMIN — Section highlight in preview iframe
3010
- The preview iframe only receives tenant CSS; core's overlay classes
3011
- (z-[50], absolute, etc.) are not in this build. Define them here so
3012
- the section highlight is always visible in /admin.
3013
- ========================================================================== */
3014
- [data-jp-section-overlay] {
3015
- position: absolute;
3016
- inset: 0;
3017
- z-index: 9999;
3018
- pointer-events: none;
3019
- transition: border-color 0.2s, background-color 0.2s;
3020
- border: 2px solid transparent;
3021
- }
3022
-
3023
- [data-section-id]:hover [data-jp-section-overlay] {
3024
- border-color: rgba(96, 165, 250, 0.5);
3025
- border-style: dashed;
3026
- }
3027
-
3028
- [data-section-id][data-jp-selected] [data-jp-section-overlay] {
3029
- border-color: rgb(37, 99, 235);
3030
- border-style: solid;
3031
- background-color: rgba(59, 130, 246, 0.05);
3032
- }
3033
-
3034
- [data-jp-section-overlay] > div {
3035
- position: absolute;
3036
- top: 0;
3037
- right: 0;
3038
- padding: 0.25rem 0.5rem;
3039
- font-size: 9px;
3040
- font-weight: 800;
3041
- text-transform: uppercase;
3042
- letter-spacing: 0.1em;
3043
- background: rgb(37, 99, 235);
3044
- color: white;
3045
- transition: opacity 0.2s;
3046
- }
3047
-
3048
- [data-section-id]:hover [data-jp-section-overlay] > div,
3049
- [data-section-id][data-jp-selected] [data-jp-section-overlay] > div {
3050
- opacity: 1;
3051
- }
3052
-
3053
- [data-section-id] [data-jp-section-overlay] > div {
3054
- opacity: 0;
3055
- }
3056
-
3057
-
2896
+ @import "tailwindcss";
2897
+
2898
+ @source "./**/*.tsx";
2899
+
2900
+ @theme {
2901
+ /*
2902
+ 🎯 MASTER MAPPING (V2.7 Landing)
2903
+ */
2904
+ --color-background: var(--background);
2905
+ --color-foreground: var(--foreground);
2906
+
2907
+ --color-card: var(--card);
2908
+ --color-card-foreground: var(--card-foreground);
2909
+
2910
+ --color-primary: var(--primary);
2911
+ --color-primary-foreground: var(--primary-foreground);
2912
+
2913
+ --color-secondary: var(--secondary);
2914
+ --color-secondary-foreground: var(--secondary-foreground);
2915
+
2916
+ --color-muted: var(--muted);
2917
+ --color-muted-foreground: var(--muted-foreground);
2918
+
2919
+ --color-accent: var(--accent);
2920
+ --color-border: var(--border);
2921
+
2922
+ --radius-lg: var(--radius);
2923
+ --radius-md: calc(var(--radius) - 2px);
2924
+ --radius-sm: calc(var(--radius) - 4px);
2925
+
2926
+ --font-primary: var(--theme-font-primary);
2927
+ --font-mono: var(--theme-font-mono);
2928
+
2929
+ /*
2930
+ 🔧 DISPLAY FONT — Forward-compatible workaround
2931
+ theme-manager.ts does NOT inject --theme-font-display (Skeleton v2.7 gap).
2932
+ The var() falls through to the hardcoded fallback today.
2933
+ When Skeleton v2.8 wires display into theme-manager, the var() will resolve
2934
+ automatically and the fallback becomes dead code.
2935
+ */
2936
+ --font-display: var(--theme-font-display, 'Playfair Display', Georgia, serif);
2937
+ }
2938
+
2939
+ /*
2940
+ 🌍 TENANT BRAND TOKENS (JSP 1.5)
2941
+ */
2942
+ :root {
2943
+ --background: var(--theme-background);
2944
+ --foreground: var(--theme-text);
2945
+ --card: var(--theme-surface);
2946
+ --card-foreground: var(--theme-text);
2947
+ --primary: var(--theme-primary);
2948
+ --primary-foreground: oklch(0.98 0 0);
2949
+ --secondary: var(--theme-secondary);
2950
+ --secondary-foreground: var(--theme-text);
2951
+ --muted: var(--theme-surface-alt);
2952
+ --muted-foreground: var(--theme-text-muted);
2953
+ --border: var(--theme-border);
2954
+ --radius: 0.45rem;
2955
+
2956
+ /*
2957
+ 🔧 ACCENT CHAIN — Forward-compatible workaround
2958
+ theme-manager.ts already injects --theme-accent on :root,
2959
+ but the original index.css never bridged it into the semantic layer.
2960
+ This closes the gap: --theme-accent → --accent → --color-accent.
2961
+ Falls back to --theme-primary if accent is undefined.
2962
+ */
2963
+ --accent: var(--theme-accent, var(--theme-primary));
2964
+ }
2965
+
2966
+ @layer base {
2967
+ * { border-color: var(--border); }
2968
+ body {
2969
+ background-color: var(--background);
2970
+ color: var(--foreground);
2971
+ font-family: var(--font-primary);
2972
+ line-height: 1.7;
2973
+ overflow-x: hidden;
2974
+ @apply antialiased;
2975
+ }
2976
+ }
2977
+
2978
+ /* ==========================================================================
2979
+ FONT DISPLAY UTILITY
2980
+ Maps .font-display class to the display font family (Playfair Display)
2981
+ ========================================================================== */
2982
+ .font-display {
2983
+ font-family: var(--font-display, var(--font-primary));
2984
+ }
2985
+
2986
+ /* ==========================================================================
2987
+ LANDING ANIMATIONS
2988
+ ========================================================================== */
2989
+ @keyframes jp-fadeUp {
2990
+ from { opacity: 0; transform: translateY(30px); }
2991
+ to { opacity: 1; transform: translateY(0); }
2992
+ }
2993
+
2994
+ @keyframes jp-pulseDot {
2995
+ 0%, 100% { opacity: 1; }
2996
+ 50% { opacity: 0.3; }
2997
+ }
2998
+
2999
+ .jp-animate-in {
3000
+ opacity: 0;
3001
+ animation: jp-fadeUp 0.7s ease forwards;
3002
+ }
3003
+ .jp-d1 { animation-delay: 0.1s; }
3004
+ .jp-d2 { animation-delay: 0.2s; }
3005
+ .jp-d3 { animation-delay: 0.3s; }
3006
+ .jp-d4 { animation-delay: 0.4s; }
3007
+ .jp-d5 { animation-delay: 0.5s; }
3008
+
3009
+ .jp-pulse-dot {
3010
+ animation: jp-pulseDot 2s ease infinite;
3011
+ }
3012
+
3013
+ /* ==========================================================================
3014
+ SMOOTH SCROLL
3015
+ ========================================================================== */
3016
+ html {
3017
+ scroll-behavior: smooth;
3018
+ }
3019
+
3020
+ /* ==========================================================================
3021
+ ICE ADMIN — Section highlight in preview iframe
3022
+ The preview iframe only receives tenant CSS; core's overlay classes
3023
+ (z-[50], absolute, etc.) are not in this build. Define them here so
3024
+ the section highlight is always visible in /admin.
3025
+ ========================================================================== */
3026
+ [data-jp-section-overlay] {
3027
+ position: absolute;
3028
+ inset: 0;
3029
+ z-index: 9999;
3030
+ pointer-events: none;
3031
+ transition: border-color 0.2s, background-color 0.2s;
3032
+ border: 2px solid transparent;
3033
+ }
3034
+
3035
+ [data-section-id]:hover [data-jp-section-overlay] {
3036
+ border-color: rgba(96, 165, 250, 0.5);
3037
+ border-style: dashed;
3038
+ }
3039
+
3040
+ [data-section-id][data-jp-selected] [data-jp-section-overlay] {
3041
+ border-color: rgb(37, 99, 235);
3042
+ border-style: solid;
3043
+ background-color: rgba(59, 130, 246, 0.05);
3044
+ }
3045
+
3046
+ [data-jp-section-overlay] > div {
3047
+ position: absolute;
3048
+ top: 0;
3049
+ right: 0;
3050
+ padding: 0.25rem 0.5rem;
3051
+ font-size: 9px;
3052
+ font-weight: 800;
3053
+ text-transform: uppercase;
3054
+ letter-spacing: 0.1em;
3055
+ background: rgb(37, 99, 235);
3056
+ color: white;
3057
+ transition: opacity 0.2s;
3058
+ }
3059
+
3060
+ [data-section-id]:hover [data-jp-section-overlay] > div,
3061
+ [data-section-id][data-jp-selected] [data-jp-section-overlay] > div {
3062
+ opacity: 1;
3063
+ }
3064
+
3065
+ [data-section-id] [data-jp-section-overlay] > div {
3066
+ opacity: 0;
3067
+ }
3068
+
3069
+
3058
3070
 
3059
3071
  END_OF_FILE_CONTENT
3060
3072
  mkdir -p "src/lib"
@@ -3273,66 +3285,28 @@ END_OF_FILE_CONTENT
3273
3285
  echo "Creating src/lib/draftStorage.ts..."
3274
3286
  cat << 'END_OF_FILE_CONTENT' > "src/lib/draftStorage.ts"
3275
3287
  /**
3276
- * Tenant draft storage — same key and shape as @jsonpages/core autosave.
3277
- * Load from localStorage when present, otherwise use file-backed data.
3288
+ * Tenant initial data — file-backed only (no localStorage).
3278
3289
  */
3279
3290
 
3280
3291
  import type { PageConfig, SiteConfig } from '@/types';
3281
3292
 
3282
- const DRAFT_KEY_PREFIX = 'jsonpages-draft';
3283
-
3284
- export function getDraftStorageKey(tenantId: string, slug: string): string {
3285
- return `${DRAFT_KEY_PREFIX}-${tenantId}-${slug}`;
3286
- }
3287
-
3288
- export interface StoredDraft {
3289
- page?: PageConfig;
3290
- site?: SiteConfig;
3291
- }
3292
-
3293
- export function readDraftFromStorage(
3294
- tenantId: string,
3295
- slug: string
3296
- ): StoredDraft | null {
3297
- if (typeof window === 'undefined') return null;
3298
- try {
3299
- const key = getDraftStorageKey(tenantId, slug);
3300
- const raw = localStorage.getItem(key);
3301
- if (!raw) return null;
3302
- return JSON.parse(raw) as StoredDraft;
3303
- } catch {
3304
- return null;
3305
- }
3306
- }
3307
-
3308
3293
  export interface HydratedData {
3309
3294
  pages: Record<string, PageConfig>;
3310
3295
  siteConfig: SiteConfig;
3311
3296
  }
3312
3297
 
3313
3298
  /**
3314
- * Build pages and siteConfig from localStorage when a draft exists for a slug,
3315
- * otherwise use file-backed data. Uses the same storage key as Core autosave.
3299
+ * Return pages and siteConfig from file-backed data only.
3316
3300
  */
3317
3301
  export function getHydratedData(
3318
- tenantId: string,
3302
+ _tenantId: string,
3319
3303
  filePages: Record<string, PageConfig>,
3320
3304
  fileSiteConfig: SiteConfig
3321
3305
  ): HydratedData {
3322
- let siteConfig = fileSiteConfig;
3323
- const pages: Record<string, PageConfig> = {};
3324
-
3325
- for (const slug of Object.keys(filePages)) {
3326
- const stored = readDraftFromStorage(tenantId, slug);
3327
- if (stored?.page) {
3328
- pages[slug] = stored.page;
3329
- if (stored?.site) siteConfig = stored.site;
3330
- } else {
3331
- pages[slug] = filePages[slug];
3332
- }
3333
- }
3334
-
3335
- return { pages, siteConfig };
3306
+ return {
3307
+ pages: { ...filePages },
3308
+ siteConfig: fileSiteConfig,
3309
+ };
3336
3310
  }
3337
3311
 
3338
3312
  END_OF_FILE_CONTENT
@@ -3411,6 +3385,7 @@ ReactDOM.createRoot(document.getElementById('root')!).render(
3411
3385
 
3412
3386
  END_OF_FILE_CONTENT
3413
3387
  # SKIP: src/registry-types.ts è un file binario e non può essere convertito in testo.
3388
+ mkdir -p "src/server"
3414
3389
  echo "Creating src/types.ts..."
3415
3390
  cat << 'END_OF_FILE_CONTENT' > "src/types.ts"
3416
3391
  import type { MenuItem } from '@jsonpages/core';