@olonjs/cli 3.0.83 → 3.0.85

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.
@@ -596,7 +596,7 @@ cat << 'END_OF_FILE_CONTENT' > "package.json"
596
596
  "@tiptap/extension-link": "^2.11.5",
597
597
  "@tiptap/react": "^2.11.5",
598
598
  "@tiptap/starter-kit": "^2.11.5",
599
- "@olonjs/core": "^1.0.71",
599
+ "@olonjs/core": "^1.0.73",
600
600
  "clsx": "^2.1.1",
601
601
  "lucide-react": "^0.474.0",
602
602
  "react": "^19.0.0",
@@ -1780,102 +1780,6 @@ function App() {
1780
1780
  export default App;
1781
1781
 
1782
1782
 
1783
- END_OF_FILE_CONTENT
1784
- # SKIP: src/App.tsx:Zone.Identifier is binary and cannot be embedded as text.
1785
- echo "Creating src/App_.tsx..."
1786
- cat << 'END_OF_FILE_CONTENT' > "src/App_.tsx"
1787
- import { useState, useEffect } from 'react';
1788
- import { JsonPagesEngine } from '@jsonpages/core';
1789
- import type { LibraryImageEntry } from '@jsonpages/core';
1790
- import { ComponentRegistry } from '@/lib/ComponentRegistry';
1791
- import { SECTION_SCHEMAS } from '@/lib/schemas';
1792
- import { addSectionConfig } from '@/lib/addSectionConfig';
1793
- import { getHydratedData } from '@/lib/draftStorage';
1794
- import type { JsonPagesConfig, ProjectState } from '@jsonpages/core';
1795
- import type { SiteConfig, ThemeConfig, MenuConfig } from '@/types';
1796
-
1797
- import siteData from '@/data/config/site.json';
1798
- import themeData from '@/data/config/theme.json';
1799
- import menuData from '@/data/config/menu.json';
1800
- import { getFilePages } from '@/lib/getFilePages';
1801
-
1802
- import fontsCss from './fonts.css?inline';
1803
- import tenantCss from './index.css?inline';
1804
-
1805
- const themeConfig = themeData as unknown as ThemeConfig;
1806
- const menuConfig = menuData as unknown as MenuConfig;
1807
- const TENANT_ID = 'alpha';
1808
-
1809
- const filePages = getFilePages();
1810
- const fileSiteConfig = siteData as unknown as SiteConfig;
1811
- const MAX_UPLOAD_SIZE_BYTES = 5 * 1024 * 1024;
1812
-
1813
- function getInitialData() {
1814
- return getHydratedData(TENANT_ID, filePages, fileSiteConfig);
1815
- }
1816
-
1817
- function App() {
1818
- const [{ pages, siteConfig }] = useState(getInitialData);
1819
- const [assetsManifest, setAssetsManifest] = useState<LibraryImageEntry[]>([]);
1820
-
1821
- useEffect(() => {
1822
- fetch('/api/list-assets')
1823
- .then((r) => (r.ok ? r.json() : []))
1824
- .then((list: LibraryImageEntry[]) => setAssetsManifest(Array.isArray(list) ? list : []))
1825
- .catch(() => setAssetsManifest([]));
1826
- }, []);
1827
-
1828
- const config: JsonPagesConfig = {
1829
- tenantId: TENANT_ID,
1830
- registry: ComponentRegistry as JsonPagesConfig['registry'],
1831
- schemas: SECTION_SCHEMAS as unknown as JsonPagesConfig['schemas'],
1832
- pages,
1833
- siteConfig,
1834
- themeConfig,
1835
- menuConfig,
1836
- themeCss: { tenant: fontsCss + '\n' + tenantCss },
1837
- addSection: addSectionConfig,
1838
- persistence: {
1839
- async saveToFile(state: ProjectState, slug: string): Promise<void> {
1840
- const res = await fetch('/api/save-to-file', {
1841
- method: 'POST',
1842
- headers: { 'Content-Type': 'application/json' },
1843
- body: JSON.stringify({ projectState: state, slug }),
1844
- });
1845
- const body = (await res.json().catch(() => ({}))) as { error?: string };
1846
- if (!res.ok) throw new Error(body.error ?? `Save to file failed: ${res.status}`);
1847
- },
1848
- },
1849
- assets: {
1850
- assetsBaseUrl: '/assets',
1851
- assetsManifest,
1852
- async onAssetUpload(file: File): Promise<string> {
1853
- if (!file.type.startsWith('image/')) throw new Error('Invalid file type.');
1854
- if (file.size > MAX_UPLOAD_SIZE_BYTES) throw new Error(`File too large. Max ${MAX_UPLOAD_SIZE_BYTES / 1024 / 1024}MB.`);
1855
- const base64 = await new Promise<string>((resolve, reject) => {
1856
- const reader = new FileReader();
1857
- reader.onload = () => resolve((reader.result as string).split(',')[1] ?? '');
1858
- reader.onerror = () => reject(reader.error);
1859
- reader.readAsDataURL(file);
1860
- });
1861
- const res = await fetch('/api/upload-asset', {
1862
- method: 'POST',
1863
- headers: { 'Content-Type': 'application/json' },
1864
- body: JSON.stringify({ filename: file.name, mimeType: file.type || undefined, data: base64 }),
1865
- });
1866
- const body = (await res.json().catch(() => ({}))) as { url?: string; error?: string };
1867
- if (!res.ok) throw new Error(body.error || `Upload failed: ${res.status}`);
1868
- if (typeof body.url !== 'string') throw new Error('Invalid server response: missing url');
1869
- return body.url;
1870
- },
1871
- },
1872
- };
1873
-
1874
- return <JsonPagesEngine config={config} />;
1875
- }
1876
-
1877
- export default App;
1878
-
1879
1783
  END_OF_FILE_CONTENT
1880
1784
  mkdir -p "src/components"
1881
1785
  echo "Creating src/components/NotFound.tsx..."
@@ -3689,6 +3593,9 @@ export const Hero: React.FC<{ data: HeroData; settings?: HeroSettings }> = ({ da
3689
3593
  '--local-cyan': 'var(--color-secondary, #22d3ee)',
3690
3594
  '--local-border': 'var(--border)',
3691
3595
  '--local-surface': 'var(--card)',
3596
+ '--local-radius-sm': 'var(--theme-radius-sm)',
3597
+ '--local-radius-md': 'var(--theme-radius-md)',
3598
+ '--local-radius-lg': 'var(--theme-radius-lg)',
3692
3599
  } as React.CSSProperties}
3693
3600
  className="jp-hero relative min-h-screen flex items-center overflow-hidden pt-24 pb-0 bg-[var(--local-bg)]"
3694
3601
  >
@@ -3747,7 +3654,7 @@ export const Hero: React.FC<{ data: HeroData; settings?: HeroSettings }> = ({ da
3747
3654
  data-jp-item-id={cta.id ?? `legacy-${idx}`}
3748
3655
  data-jp-item-field="ctas"
3749
3656
  className={cn(
3750
- 'inline-flex items-center gap-2 px-7 py-3 rounded-[7px] font-semibold text-[0.95rem] transition-all duration-200 no-underline',
3657
+ 'inline-flex items-center gap-2 px-7 py-3 rounded-[var(--local-radius-md)] font-semibold text-[0.95rem] transition-all duration-200 no-underline',
3751
3658
  cta.variant === 'primary'
3752
3659
  ? 'bg-[var(--local-primary)] text-white hover:brightness-110 hover:-translate-y-0.5 shadow-[0_0_24px_rgba(59,130,246,0.25)]'
3753
3660
  : 'bg-transparent text-[var(--local-text)] border border-[rgba(255,255,255,0.12)] hover:border-[rgba(255,255,255,0.3)] hover:bg-[rgba(255,255,255,0.04)]'
@@ -3780,13 +3687,13 @@ export const Hero: React.FC<{ data: HeroData; settings?: HeroSettings }> = ({ da
3780
3687
  </div>
3781
3688
 
3782
3689
  {/* RIGHT — ICE mini-mockup */}
3783
- <div className="jp-animate-in jp-d2 rounded-[12px] overflow-hidden border border-[rgba(255,255,255,0.10)] shadow-[0_0_0_1px_rgba(255,255,255,0.04),0_40px_80px_rgba(0,0,0,0.6),0_0_60px_rgba(59,130,246,0.08)]">
3690
+ <div className="jp-animate-in jp-d2 rounded-[var(--local-radius-lg)] overflow-hidden border border-[rgba(255,255,255,0.10)] shadow-[0_0_0_1px_rgba(255,255,255,0.04),0_40px_80px_rgba(0,0,0,0.6),0_0_60px_rgba(59,130,246,0.08)]">
3784
3691
  {/* Browser bar */}
3785
3692
  <div className="bg-[#0f1923] px-3 py-2.5 flex items-center gap-1.5 border-b border-[rgba(255,255,255,0.05)]">
3786
3693
  <span className="w-2.5 h-2.5 rounded-full bg-[#ef4444]" />
3787
3694
  <span className="w-2.5 h-2.5 rounded-full bg-[#f59e0b]" />
3788
3695
  <span className="w-2.5 h-2.5 rounded-full bg-[#22c55e]" />
3789
- <span className="mx-auto font-mono text-[0.60rem] text-[rgba(255,255,255,0.20)] bg-[rgba(255,255,255,0.04)] px-3 py-0.5 rounded">localhost:5173 · Studio</span>
3696
+ <span className="mx-auto font-mono text-[0.60rem] text-[rgba(255,255,255,0.20)] bg-[rgba(255,255,255,0.04)] px-3 py-0.5 rounded-[var(--local-radius-sm)]">localhost:5173 · Studio</span>
3790
3697
  </div>
3791
3698
  {/* Split: canvas + inspector */}
3792
3699
  <div className="grid grid-cols-[1fr_260px] h-[360px] bg-[#060d1b]">
@@ -3810,8 +3717,8 @@ export const Hero: React.FC<{ data: HeroData; settings?: HeroSettings }> = ({ da
3810
3717
  {data.description?.slice(0, 100)}…
3811
3718
  </p>
3812
3719
  <div className="flex gap-1.5">
3813
- <span className="text-[0.58rem] font-semibold bg-[#3b82f6] text-white px-2.5 py-1 rounded">Read the Docs</span>
3814
- <span className="text-[0.58rem] border border-[rgba(255,255,255,0.15)] text-[#94a3b8] px-2.5 py-1 rounded">View on NPM</span>
3720
+ <span className="text-[0.58rem] font-semibold bg-[#3b82f6] text-white px-2.5 py-1 rounded-[var(--local-radius-sm)]">Read the Docs</span>
3721
+ <span className="text-[0.58rem] border border-[rgba(255,255,255,0.15)] text-[#94a3b8] px-2.5 py-1 rounded-[var(--local-radius-sm)]">View on NPM</span>
3815
3722
  </div>
3816
3723
  <div className="flex gap-4 mt-3 pt-3 border-t border-[rgba(255,255,255,0.05)]">
3817
3724
  {(data.metrics ?? []).map((m, i) => (
@@ -3858,15 +3765,15 @@ export const Hero: React.FC<{ data: HeroData; settings?: HeroSettings }> = ({ da
3858
3765
  <div className="flex-1 px-3.5 py-3 flex flex-col gap-2.5 overflow-hidden">
3859
3766
  <div>
3860
3767
  <div className="font-mono text-[0.50rem] uppercase tracking-widest text-[#334155] mb-1">Title</div>
3861
- <div className="bg-[rgba(59,130,246,0.05)] border border-[rgba(59,130,246,0.45)] rounded px-2 py-1.5 font-mono text-[0.58rem] text-[#e2e8f0] truncate">{data.title}</div>
3768
+ <div className="bg-[rgba(59,130,246,0.05)] border border-[rgba(59,130,246,0.45)] rounded-[var(--local-radius-sm)] px-2 py-1.5 font-mono text-[0.58rem] text-[#e2e8f0] truncate">{data.title}</div>
3862
3769
  </div>
3863
3770
  <div>
3864
3771
  <div className="font-mono text-[0.50rem] uppercase tracking-widest text-[#334155] mb-1">Subtitle</div>
3865
- <div className="bg-[rgba(255,255,255,0.03)] border border-[rgba(255,255,255,0.07)] rounded px-2 py-1.5 font-mono text-[0.58rem] text-[#94a3b8] truncate">{data.titleHighlight}</div>
3772
+ <div className="bg-[rgba(255,255,255,0.03)] border border-[rgba(255,255,255,0.07)] rounded-[var(--local-radius-sm)] px-2 py-1.5 font-mono text-[0.58rem] text-[#94a3b8] truncate">{data.titleHighlight}</div>
3866
3773
  </div>
3867
3774
  <div>
3868
3775
  <div className="font-mono text-[0.50rem] uppercase tracking-widest text-[#334155] mb-1">Badge</div>
3869
- <div className="bg-[rgba(255,255,255,0.03)] border border-[rgba(255,255,255,0.07)] rounded px-2 py-1.5 font-mono text-[0.58rem] text-[#94a3b8] truncate">{data.badge}</div>
3776
+ <div className="bg-[rgba(255,255,255,0.03)] border border-[rgba(255,255,255,0.07)] rounded-[var(--local-radius-sm)] px-2 py-1.5 font-mono text-[0.58rem] text-[#94a3b8] truncate">{data.badge}</div>
3870
3777
  </div>
3871
3778
  </div>
3872
3779
  {/* Bottom bar */}
@@ -3874,8 +3781,8 @@ export const Hero: React.FC<{ data: HeroData; settings?: HeroSettings }> = ({ da
3874
3781
  <span className="w-1.5 h-1.5 rounded-full bg-[#22c55e]" />
3875
3782
  <span className="font-mono text-[0.50rem] text-[#475569]">All Changes Saved</span>
3876
3783
  <div className="ml-auto flex gap-1.5">
3877
- <span className="font-mono text-[0.48rem] px-1.5 py-0.5 rounded border border-[rgba(59,130,246,0.3)] bg-[rgba(59,130,246,0.12)] text-[#60a5fa]">⬡ HTML</span>
3878
- <span className="font-mono text-[0.48rem] px-1.5 py-0.5 rounded border border-[rgba(255,255,255,0.08)] bg-[rgba(255,255,255,0.03)] text-[#94a3b8] opacity-50">{ } JSON</span>
3784
+ <span className="font-mono text-[0.48rem] px-1.5 py-0.5 rounded-[var(--local-radius-sm)] border border-[rgba(59,130,246,0.3)] bg-[rgba(59,130,246,0.12)] text-[#60a5fa]">⬡ HTML</span>
3785
+ <span className="font-mono text-[0.48rem] px-1.5 py-0.5 rounded-[var(--local-radius-sm)] border border-[rgba(255,255,255,0.08)] bg-[rgba(255,255,255,0.03)] text-[#94a3b8] opacity-50">{ } JSON</span>
3879
3786
  </div>
3880
3787
  </div>
3881
3788
  </div>
@@ -7404,13 +7311,14 @@ cat << 'END_OF_FILE_CONTENT' > "src/data/config/theme.json"
7404
7311
  "fontFamily": {
7405
7312
  "primary": "'Instrument Sans', system-ui, sans-serif",
7406
7313
  "mono": "'JetBrains Mono', monospace",
7407
- "display": "'Bricolage Grotesque', system-ui, sans-serif"
7314
+ "display": "'Bricolage Grotesque', system-ui, sans-serif",
7315
+ "display-2":"'Instrument Sans'"
7408
7316
  }
7409
7317
  },
7410
7318
  "borderRadius": {
7411
- "sm": "5px",
7412
- "md": "7px",
7413
- "lg": "8px"
7319
+ "sm": "4px",
7320
+ "md": "8px",
7321
+ "lg": "36px"
7414
7322
  }
7415
7323
  }
7416
7324
  }
@@ -8028,72 +7936,6 @@ cat << 'END_OF_FILE_CONTENT' > "src/data/pages/post.json"
8028
7936
  ]
8029
7937
  }
8030
7938
  END_OF_FILE_CONTENT
8031
- mkdir -p "src/data/pages/servizi"
8032
- echo "Creating src/data/pages/servizi/trattamento.json..."
8033
- cat << 'END_OF_FILE_CONTENT' > "src/data/pages/servizi/trattamento.json"
8034
- {
8035
- "id": "servizi-trattamento-page",
8036
- "slug": "servizi/trattamento",
8037
- "meta": {
8038
- "title": "Servizi - Trattamento",
8039
- "description": "Pagina nested di smoke test per verificare routing visitor/admin/preview."
8040
- },
8041
- "sections": [
8042
- {
8043
- "id": "hero-servizi-trattamento",
8044
- "type": "hero",
8045
- "data": {
8046
- "badge": "Smoke Test",
8047
- "title": "Trattamento",
8048
- "titleHighlight": "Pagina Nested",
8049
- "description": "Questa pagina verifica il supporto ai nested slug su filesystem e router.",
8050
- "ctas": [
8051
- {
8052
- "id": "cta-home",
8053
- "label": "Torna Home",
8054
- "href": "/",
8055
- "variant": "primary"
8056
- }
8057
- ]
8058
- },
8059
- "settings": {}
8060
- }
8061
- ]
8062
- }
8063
-
8064
- END_OF_FILE_CONTENT
8065
- echo "Creating src/data/pages/servizi_trattamento.json..."
8066
- cat << 'END_OF_FILE_CONTENT' > "src/data/pages/servizi_trattamento.json"
8067
- {
8068
- "id": "servizi-trattamento-page",
8069
- "slug": "servizi/trattamento",
8070
- "meta": {
8071
- "title": "Servizi - Trattamento",
8072
- "description": "Pagina nested di smoke test per verificare routing visitor/admin/preview."
8073
- },
8074
- "sections": [
8075
- {
8076
- "id": "hero-servizi-trattamento",
8077
- "type": "hero",
8078
- "data": {
8079
- "badge": "Smoke Test",
8080
- "title": "Trattamentos",
8081
- "titleHighlight": "Pagina Nested",
8082
- "description": "Questa pagina verifica il supporto ai nested slug su filesystem e router.",
8083
- "ctas": [
8084
- {
8085
- "id": "cta-home",
8086
- "label": "Torna Home",
8087
- "href": "/",
8088
- "variant": "primary"
8089
- }
8090
- ]
8091
- },
8092
- "settings": {}
8093
- }
8094
- ]
8095
- }
8096
- END_OF_FILE_CONTENT
8097
7939
  mkdir -p "src/emails"
8098
7940
  echo "Creating src/emails/LeadNotificationEmail.tsx..."
8099
7941
  cat << 'END_OF_FILE_CONTENT' > "src/emails/LeadNotificationEmail.tsx"
@@ -8407,7 +8249,7 @@ export default LeadSenderConfirmationEmail;
8407
8249
  END_OF_FILE_CONTENT
8408
8250
  echo "Creating src/fonts.css..."
8409
8251
  cat << 'END_OF_FILE_CONTENT' > "src/fonts.css"
8410
- @import url('https://fonts.googleapis.com/css2?family=Bricolage+Grotesque:opsz,wght@12..96,400;12..96,600;12..96,700;12..96,800&family=JetBrains+Mono:wght@400;500;600;700&family=Instrument+Sans:wght@400;500;600&display=swap');
8252
+ @import url('https://fonts.googleapis.com/css2?family=Instrument+Sans:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500&family=Playfair+Display:wght@700;800;900&display=swap');
8411
8253
 
8412
8254
  END_OF_FILE_CONTENT
8413
8255
  mkdir -p "src/hooks"
@@ -8472,14 +8314,13 @@ cat << 'END_OF_FILE_CONTENT' > "src/index.css"
8472
8314
  --font-primary: var(--theme-font-primary);
8473
8315
  --font-mono: var(--theme-font-mono);
8474
8316
 
8475
- /*
8476
- 🔧 DISPLAY FONT — Forward-compatible workaround
8477
- theme-manager.ts does NOT inject --theme-font-display (Skeleton v2.7 gap).
8478
- The var() falls through to the hardcoded fallback today.
8479
- When Skeleton v2.8 wires display into theme-manager, the var() will resolve
8480
- automatically and the fallback becomes dead code.
8317
+ /*
8318
+ DISPLAY FONT bridge
8319
+ The core now emits --theme-font-display from theme.json, so this keeps
8320
+ the tenant on the stable semantic alias rather than depending on the
8321
+ flattened internal variable path.
8481
8322
  */
8482
- --font-display: var(--theme-font-display, 'Playfair Display', Georgia, serif);
8323
+ --font-display: var(--theme-font-display);
8483
8324
  }
8484
8325
 
8485
8326
  /*
@@ -8497,7 +8338,7 @@ cat << 'END_OF_FILE_CONTENT' > "src/index.css"
8497
8338
  --muted: var(--theme-surface-alt);
8498
8339
  --muted-foreground: var(--theme-text-muted);
8499
8340
  --border: var(--theme-border);
8500
- --radius: 0.45rem;
8341
+ --radius: 3.45rem;
8501
8342
 
8502
8343
  /*
8503
8344
  🔧 ACCENT CHAIN — Forward-compatible workaround
@@ -9394,24 +9235,6 @@ ReactDOM.createRoot(document.getElementById('root')!).render(
9394
9235
 
9395
9236
 
9396
9237
 
9397
- END_OF_FILE_CONTENT
9398
- echo "Creating src/main_.tsx..."
9399
- cat << 'END_OF_FILE_CONTENT' > "src/main_.tsx"
9400
- import '@/types'; // TBP: load type augmentation from capsule-driven types
9401
- import React from 'react';
9402
- import ReactDOM from 'react-dom/client';
9403
- import App from './App';
9404
- // ... resto del file
9405
-
9406
- ReactDOM.createRoot(document.getElementById('root')!).render(
9407
- <React.StrictMode>
9408
- <App />
9409
- </React.StrictMode>
9410
- );
9411
-
9412
-
9413
-
9414
-
9415
9238
  END_OF_FILE_CONTENT
9416
9239
  # SKIP: src/registry-types.ts is binary and cannot be embedded as text.
9417
9240
  mkdir -p "src/server"
@@ -596,7 +596,7 @@ cat << 'END_OF_FILE_CONTENT' > "package.json"
596
596
  "@tiptap/extension-link": "^2.11.5",
597
597
  "@tiptap/react": "^2.11.5",
598
598
  "@tiptap/starter-kit": "^2.11.5",
599
- "@olonjs/core": "^1.0.71",
599
+ "@olonjs/core": "^1.0.73",
600
600
  "clsx": "^2.1.1",
601
601
  "lucide-react": "^0.474.0",
602
602
  "react": "^19.0.0",
@@ -1144,264 +1144,6 @@ function App() {
1144
1144
 
1145
1145
  export default App;
1146
1146
 
1147
- END_OF_FILE_CONTENT
1148
- echo "Creating src/App_.tsx..."
1149
- cat << 'END_OF_FILE_CONTENT' > "src/App_.tsx"
1150
- import { useState, useEffect } from 'react';
1151
- import { JsonPagesEngine } from '@jsonpages/core';
1152
- import type { LibraryImageEntry } from '@jsonpages/core';
1153
- import { ComponentRegistry } from '@/lib/ComponentRegistry';
1154
- import { SECTION_SCHEMAS } from '@/lib/schemas';
1155
- import { addSectionConfig } from '@/lib/addSectionConfig';
1156
- import { getHydratedData } from '@/lib/draftStorage';
1157
- import type { JsonPagesConfig, ProjectState } from '@jsonpages/core';
1158
- import type { SiteConfig, ThemeConfig, MenuConfig } from '@/types';
1159
-
1160
- import siteData from '@/data/config/site.json';
1161
- import themeData from '@/data/config/theme.json';
1162
- import menuData from '@/data/config/menu.json';
1163
- import { getFilePages } from '@/lib/getFilePages';
1164
-
1165
- import fontsCss from './fonts.css?inline';
1166
- import tenantCss from './index.css?inline';
1167
-
1168
- const themeConfig = themeData as unknown as ThemeConfig;
1169
- const menuConfig = menuData as unknown as MenuConfig;
1170
- const TENANT_ID = 'alpha';
1171
-
1172
- const filePages = getFilePages();
1173
- const fileSiteConfig = siteData as unknown as SiteConfig;
1174
- const MAX_UPLOAD_SIZE_BYTES = 5 * 1024 * 1024;
1175
-
1176
- function getInitialData() {
1177
- return getHydratedData(TENANT_ID, filePages, fileSiteConfig);
1178
- }
1179
-
1180
- function App() {
1181
- const [{ pages, siteConfig }] = useState(getInitialData);
1182
- const [assetsManifest, setAssetsManifest] = useState<LibraryImageEntry[]>([]);
1183
-
1184
- useEffect(() => {
1185
- fetch('/api/list-assets')
1186
- .then((r) => (r.ok ? r.json() : []))
1187
- .then((list: LibraryImageEntry[]) => setAssetsManifest(Array.isArray(list) ? list : []))
1188
- .catch(() => setAssetsManifest([]));
1189
- }, []);
1190
-
1191
- const config: JsonPagesConfig = {
1192
- tenantId: TENANT_ID,
1193
- registry: ComponentRegistry as JsonPagesConfig['registry'],
1194
- schemas: SECTION_SCHEMAS as unknown as JsonPagesConfig['schemas'],
1195
- pages,
1196
- siteConfig,
1197
- themeConfig,
1198
- menuConfig,
1199
- themeCss: { tenant: fontsCss + '\n' + tenantCss },
1200
- addSection: addSectionConfig,
1201
- persistence: {
1202
- async saveToFile(state: ProjectState, slug: string): Promise<void> {
1203
- const res = await fetch('/api/save-to-file', {
1204
- method: 'POST',
1205
- headers: { 'Content-Type': 'application/json' },
1206
- body: JSON.stringify({ projectState: state, slug }),
1207
- });
1208
- const body = (await res.json().catch(() => ({}))) as { error?: string };
1209
- if (!res.ok) throw new Error(body.error ?? `Save to file failed: ${res.status}`);
1210
- },
1211
- },
1212
- assets: {
1213
- assetsBaseUrl: '/assets',
1214
- assetsManifest,
1215
- async onAssetUpload(file: File): Promise<string> {
1216
- if (!file.type.startsWith('image/')) throw new Error('Invalid file type.');
1217
- if (file.size > MAX_UPLOAD_SIZE_BYTES) throw new Error(`File too large. Max ${MAX_UPLOAD_SIZE_BYTES / 1024 / 1024}MB.`);
1218
- const base64 = await new Promise<string>((resolve, reject) => {
1219
- const reader = new FileReader();
1220
- reader.onload = () => resolve((reader.result as string).split(',')[1] ?? '');
1221
- reader.onerror = () => reject(reader.error);
1222
- reader.readAsDataURL(file);
1223
- });
1224
- const res = await fetch('/api/upload-asset', {
1225
- method: 'POST',
1226
- headers: { 'Content-Type': 'application/json' },
1227
- body: JSON.stringify({ filename: file.name, mimeType: file.type || undefined, data: base64 }),
1228
- });
1229
- const body = (await res.json().catch(() => ({}))) as { url?: string; error?: string };
1230
- if (!res.ok) throw new Error(body.error || `Upload failed: ${res.status}`);
1231
- if (typeof body.url !== 'string') throw new Error('Invalid server response: missing url');
1232
- return body.url;
1233
- },
1234
- },
1235
- };
1236
-
1237
- return <JsonPagesEngine config={config} />;
1238
- }
1239
-
1240
- export default App;
1241
-
1242
- END_OF_FILE_CONTENT
1243
- echo "Creating src/_App.tsx..."
1244
- cat << 'END_OF_FILE_CONTENT' > "src/_App.tsx"
1245
- /**
1246
- * Thin Entry Point (Tenant).
1247
- * Data from getHydratedData (file-backed or draft); assets from public/assets/images.
1248
- * Supports Hybrid Persistence: Local Filesystem (Dev) or Cloud Bridge (Prod).
1249
- */
1250
- import { useState, useEffect } from 'react';
1251
- import { JsonPagesEngine } from '@jsonpages/core';
1252
- import type { LibraryImageEntry } from '@jsonpages/core';
1253
- import { ComponentRegistry } from '@/lib/ComponentRegistry';
1254
- import { SECTION_SCHEMAS } from '@/lib/schemas';
1255
- import { addSectionConfig } from '@/lib/addSectionConfig';
1256
- import { getHydratedData } from '@/lib/draftStorage';
1257
- import type { JsonPagesConfig, ProjectState } from '@jsonpages/core';
1258
- import type { SiteConfig, ThemeConfig, MenuConfig } from '@/types';
1259
-
1260
- import siteData from '@/data/config/site.json';
1261
- import themeData from '@/data/config/theme.json';
1262
- import menuData from '@/data/config/menu.json';
1263
- import { getFilePages } from '@/lib/getFilePages';
1264
-
1265
- import fontsCss from './fonts.css?inline';
1266
- import tenantCss from './index.css?inline';
1267
-
1268
- // Cloud Configuration (Injected by Vercel/Netlify Env Vars)
1269
- const CLOUD_API_URL = import.meta.env.VITE_JSONPAGES_CLOUD_URL;
1270
- const CLOUD_API_KEY = import.meta.env.VITE_JSONPAGES_API_KEY;
1271
-
1272
- const themeConfig = themeData as unknown as ThemeConfig;
1273
- const menuConfig = menuData as unknown as MenuConfig;
1274
- const TENANT_ID = 'alpha';
1275
-
1276
- const filePages = getFilePages();
1277
- const fileSiteConfig = siteData as unknown as SiteConfig;
1278
- const MAX_UPLOAD_SIZE_BYTES = 5 * 1024 * 1024;
1279
-
1280
- function getInitialData() {
1281
- return getHydratedData(TENANT_ID, filePages, fileSiteConfig);
1282
- }
1283
-
1284
- function App() {
1285
- const [{ pages, siteConfig }] = useState(getInitialData);
1286
- const [assetsManifest, setAssetsManifest] = useState<LibraryImageEntry[]>([]);
1287
-
1288
- useEffect(() => {
1289
- // In Cloud mode, listing assets might be different or disabled for MVP
1290
- // For now, we keep the local fetch which will fail gracefully on Vercel (404)
1291
- fetch('/api/list-assets')
1292
- .then((r) => (r.ok ? r.json() : []))
1293
- .then((list: LibraryImageEntry[]) => setAssetsManifest(Array.isArray(list) ? list : []))
1294
- .catch(() => setAssetsManifest([]));
1295
- }, []);
1296
-
1297
- console.log("🔍 DEBUG ENV:", {
1298
- URL: import.meta.env.VITE_JSONPAGES_CLOUD_URL,
1299
- KEY: import.meta.env.VITE_JSONPAGES_API_KEY ? "PRESENT" : "MISSING"
1300
- });
1301
- const config: JsonPagesConfig = {
1302
- tenantId: TENANT_ID,
1303
- registry: ComponentRegistry as JsonPagesConfig['registry'],
1304
- schemas: SECTION_SCHEMAS as unknown as JsonPagesConfig['schemas'],
1305
- pages,
1306
- siteConfig,
1307
- themeConfig,
1308
- menuConfig,
1309
- themeCss: { tenant: fontsCss + '\n' + tenantCss },
1310
- addSection: addSectionConfig,
1311
- persistence: {
1312
- async saveToFile(state: ProjectState, slug: string): Promise<void> {
1313
-
1314
- // ☁️ SCENARIO A: CLOUD BRIDGE (Production)
1315
- if (CLOUD_API_URL && CLOUD_API_KEY) {
1316
- console.log(`☁️ Saving ${slug} via Cloud Bridge...`);
1317
-
1318
- const res = await fetch(`${CLOUD_API_URL}/save`, {
1319
- method: 'POST',
1320
- headers: {
1321
- 'Content-Type': 'application/json',
1322
- 'Authorization': `Bearer ${CLOUD_API_KEY}`
1323
- },
1324
- body: JSON.stringify({
1325
- // Mapping logical slug to physical path in repo
1326
- path: `src/data/pages/${slug}.json`,
1327
- // We save the page config specifically
1328
- content: state.page,
1329
- message: `Content update for ${slug} via Visual Editor`
1330
- }),
1331
- });
1332
-
1333
- if (!res.ok) {
1334
- const err = await res.json().catch(() => ({}));
1335
- throw new Error(err.error || `Cloud save failed: ${res.status}`);
1336
- }
1337
- return;
1338
- }
1339
-
1340
- // 💻 SCENARIO B: LOCAL FILESYSTEM (Development)
1341
- console.log(`💻 Saving ${slug} to Local Filesystem...`);
1342
- const res = await fetch('/api/save-to-file', {
1343
- method: 'POST',
1344
- headers: { 'Content-Type': 'application/json' },
1345
- body: JSON.stringify({ projectState: state, slug }),
1346
- });
1347
-
1348
- const body = (await res.json().catch(() => ({}))) as { error?: string };
1349
- if (!res.ok) throw new Error(body.error ?? `Save to file failed: ${res.status}`);
1350
- },
1351
- },
1352
- assets: {
1353
- assetsBaseUrl: '/assets',
1354
- assetsManifest,
1355
- async onAssetUpload(file: File): Promise<string> {
1356
- // Note: Asset upload in Cloud Mode requires the R2 Bridge (Next Step in Roadmap)
1357
- // For now, this works in Local Mode.
1358
- if (!file.type.startsWith('image/')) throw new Error('Invalid file type.');
1359
- if (file.size > MAX_UPLOAD_SIZE_BYTES) throw new Error(`File too large. Max ${MAX_UPLOAD_SIZE_BYTES / 1024 / 1024}MB.`);
1360
-
1361
- const base64 = await new Promise<string>((resolve, reject) => {
1362
- const reader = new FileReader();
1363
- reader.onload = () => resolve((reader.result as string).split(',')[1] ?? '');
1364
- reader.onerror = () => reject(reader.error);
1365
- reader.readAsDataURL(file);
1366
- });
1367
-
1368
- const res = await fetch('/api/upload-asset', {
1369
- method: 'POST',
1370
- headers: { 'Content-Type': 'application/json' },
1371
- body: JSON.stringify({ filename: file.name, mimeType: file.type || undefined, data: base64 }),
1372
- });
1373
-
1374
- const body = (await res.json().catch(() => ({}))) as { url?: string; error?: string };
1375
- if (!res.ok) throw new Error(body.error || `Upload failed: ${res.status}`);
1376
- if (typeof body.url !== 'string') throw new Error('Invalid server response: missing url');
1377
- return body.url;
1378
- },
1379
- },
1380
- };
1381
-
1382
- return <JsonPagesEngine config={config} />;
1383
- }
1384
-
1385
- export default App;
1386
-
1387
- END_OF_FILE_CONTENT
1388
- echo "Creating src/_main.tsx..."
1389
- cat << 'END_OF_FILE_CONTENT' > "src/_main.tsx"
1390
- import '@/types'; // TBP: load type augmentation from capsule-driven types
1391
- import React from 'react';
1392
- import ReactDOM from 'react-dom/client';
1393
- import App from './App';
1394
- // ... resto del file
1395
-
1396
- ReactDOM.createRoot(document.getElementById('root')!).render(
1397
- <React.StrictMode>
1398
- <App />
1399
- </React.StrictMode>
1400
- );
1401
-
1402
-
1403
-
1404
-
1405
1147
  END_OF_FILE_CONTENT
1406
1148
  mkdir -p "src/components"
1407
1149
  echo "Creating src/components/NotFound.tsx..."
@@ -10933,24 +10675,6 @@ ReactDOM.createRoot(document.getElementById('root')!).render(
10933
10675
 
10934
10676
 
10935
10677
 
10936
- END_OF_FILE_CONTENT
10937
- echo "Creating src/main_.tsx..."
10938
- cat << 'END_OF_FILE_CONTENT' > "src/main_.tsx"
10939
- import '@/types'; // TBP: load type augmentation from capsule-driven types
10940
- import React from 'react';
10941
- import ReactDOM from 'react-dom/client';
10942
- import App from './App';
10943
- // ... resto del file
10944
-
10945
- ReactDOM.createRoot(document.getElementById('root')!).render(
10946
- <React.StrictMode>
10947
- <App />
10948
- </React.StrictMode>
10949
- );
10950
-
10951
-
10952
-
10953
-
10954
10678
  END_OF_FILE_CONTENT
10955
10679
  # SKIP: src/registry-types.ts is binary and cannot be embedded as text.
10956
10680
  mkdir -p "src/server"
@@ -596,7 +596,7 @@ cat << 'END_OF_FILE_CONTENT' > "package.json"
596
596
  "@tiptap/extension-link": "^2.11.5",
597
597
  "@tiptap/react": "^2.11.5",
598
598
  "@tiptap/starter-kit": "^2.11.5",
599
- "@olonjs/core": "^1.0.71",
599
+ "@olonjs/core": "^1.0.73",
600
600
  "clsx": "^2.1.1",
601
601
  "lucide-react": "^0.474.0",
602
602
  "react": "^19.0.0",
@@ -1780,102 +1780,6 @@ function App() {
1780
1780
  export default App;
1781
1781
 
1782
1782
 
1783
- END_OF_FILE_CONTENT
1784
- # SKIP: src/App.tsx:Zone.Identifier is binary and cannot be embedded as text.
1785
- echo "Creating src/App_.tsx..."
1786
- cat << 'END_OF_FILE_CONTENT' > "src/App_.tsx"
1787
- import { useState, useEffect } from 'react';
1788
- import { JsonPagesEngine } from '@jsonpages/core';
1789
- import type { LibraryImageEntry } from '@jsonpages/core';
1790
- import { ComponentRegistry } from '@/lib/ComponentRegistry';
1791
- import { SECTION_SCHEMAS } from '@/lib/schemas';
1792
- import { addSectionConfig } from '@/lib/addSectionConfig';
1793
- import { getHydratedData } from '@/lib/draftStorage';
1794
- import type { JsonPagesConfig, ProjectState } from '@jsonpages/core';
1795
- import type { SiteConfig, ThemeConfig, MenuConfig } from '@/types';
1796
-
1797
- import siteData from '@/data/config/site.json';
1798
- import themeData from '@/data/config/theme.json';
1799
- import menuData from '@/data/config/menu.json';
1800
- import { getFilePages } from '@/lib/getFilePages';
1801
-
1802
- import fontsCss from './fonts.css?inline';
1803
- import tenantCss from './index.css?inline';
1804
-
1805
- const themeConfig = themeData as unknown as ThemeConfig;
1806
- const menuConfig = menuData as unknown as MenuConfig;
1807
- const TENANT_ID = 'alpha';
1808
-
1809
- const filePages = getFilePages();
1810
- const fileSiteConfig = siteData as unknown as SiteConfig;
1811
- const MAX_UPLOAD_SIZE_BYTES = 5 * 1024 * 1024;
1812
-
1813
- function getInitialData() {
1814
- return getHydratedData(TENANT_ID, filePages, fileSiteConfig);
1815
- }
1816
-
1817
- function App() {
1818
- const [{ pages, siteConfig }] = useState(getInitialData);
1819
- const [assetsManifest, setAssetsManifest] = useState<LibraryImageEntry[]>([]);
1820
-
1821
- useEffect(() => {
1822
- fetch('/api/list-assets')
1823
- .then((r) => (r.ok ? r.json() : []))
1824
- .then((list: LibraryImageEntry[]) => setAssetsManifest(Array.isArray(list) ? list : []))
1825
- .catch(() => setAssetsManifest([]));
1826
- }, []);
1827
-
1828
- const config: JsonPagesConfig = {
1829
- tenantId: TENANT_ID,
1830
- registry: ComponentRegistry as JsonPagesConfig['registry'],
1831
- schemas: SECTION_SCHEMAS as unknown as JsonPagesConfig['schemas'],
1832
- pages,
1833
- siteConfig,
1834
- themeConfig,
1835
- menuConfig,
1836
- themeCss: { tenant: fontsCss + '\n' + tenantCss },
1837
- addSection: addSectionConfig,
1838
- persistence: {
1839
- async saveToFile(state: ProjectState, slug: string): Promise<void> {
1840
- const res = await fetch('/api/save-to-file', {
1841
- method: 'POST',
1842
- headers: { 'Content-Type': 'application/json' },
1843
- body: JSON.stringify({ projectState: state, slug }),
1844
- });
1845
- const body = (await res.json().catch(() => ({}))) as { error?: string };
1846
- if (!res.ok) throw new Error(body.error ?? `Save to file failed: ${res.status}`);
1847
- },
1848
- },
1849
- assets: {
1850
- assetsBaseUrl: '/assets',
1851
- assetsManifest,
1852
- async onAssetUpload(file: File): Promise<string> {
1853
- if (!file.type.startsWith('image/')) throw new Error('Invalid file type.');
1854
- if (file.size > MAX_UPLOAD_SIZE_BYTES) throw new Error(`File too large. Max ${MAX_UPLOAD_SIZE_BYTES / 1024 / 1024}MB.`);
1855
- const base64 = await new Promise<string>((resolve, reject) => {
1856
- const reader = new FileReader();
1857
- reader.onload = () => resolve((reader.result as string).split(',')[1] ?? '');
1858
- reader.onerror = () => reject(reader.error);
1859
- reader.readAsDataURL(file);
1860
- });
1861
- const res = await fetch('/api/upload-asset', {
1862
- method: 'POST',
1863
- headers: { 'Content-Type': 'application/json' },
1864
- body: JSON.stringify({ filename: file.name, mimeType: file.type || undefined, data: base64 }),
1865
- });
1866
- const body = (await res.json().catch(() => ({}))) as { url?: string; error?: string };
1867
- if (!res.ok) throw new Error(body.error || `Upload failed: ${res.status}`);
1868
- if (typeof body.url !== 'string') throw new Error('Invalid server response: missing url');
1869
- return body.url;
1870
- },
1871
- },
1872
- };
1873
-
1874
- return <JsonPagesEngine config={config} />;
1875
- }
1876
-
1877
- export default App;
1878
-
1879
1783
  END_OF_FILE_CONTENT
1880
1784
  mkdir -p "src/components"
1881
1785
  echo "Creating src/components/NotFound.tsx..."
@@ -3689,6 +3593,9 @@ export const Hero: React.FC<{ data: HeroData; settings?: HeroSettings }> = ({ da
3689
3593
  '--local-cyan': 'var(--color-secondary, #22d3ee)',
3690
3594
  '--local-border': 'var(--border)',
3691
3595
  '--local-surface': 'var(--card)',
3596
+ '--local-radius-sm': 'var(--theme-radius-sm)',
3597
+ '--local-radius-md': 'var(--theme-radius-md)',
3598
+ '--local-radius-lg': 'var(--theme-radius-lg)',
3692
3599
  } as React.CSSProperties}
3693
3600
  className="jp-hero relative min-h-screen flex items-center overflow-hidden pt-24 pb-0 bg-[var(--local-bg)]"
3694
3601
  >
@@ -3747,7 +3654,7 @@ export const Hero: React.FC<{ data: HeroData; settings?: HeroSettings }> = ({ da
3747
3654
  data-jp-item-id={cta.id ?? `legacy-${idx}`}
3748
3655
  data-jp-item-field="ctas"
3749
3656
  className={cn(
3750
- 'inline-flex items-center gap-2 px-7 py-3 rounded-[7px] font-semibold text-[0.95rem] transition-all duration-200 no-underline',
3657
+ 'inline-flex items-center gap-2 px-7 py-3 rounded-[var(--local-radius-md)] font-semibold text-[0.95rem] transition-all duration-200 no-underline',
3751
3658
  cta.variant === 'primary'
3752
3659
  ? 'bg-[var(--local-primary)] text-white hover:brightness-110 hover:-translate-y-0.5 shadow-[0_0_24px_rgba(59,130,246,0.25)]'
3753
3660
  : 'bg-transparent text-[var(--local-text)] border border-[rgba(255,255,255,0.12)] hover:border-[rgba(255,255,255,0.3)] hover:bg-[rgba(255,255,255,0.04)]'
@@ -3780,13 +3687,13 @@ export const Hero: React.FC<{ data: HeroData; settings?: HeroSettings }> = ({ da
3780
3687
  </div>
3781
3688
 
3782
3689
  {/* RIGHT — ICE mini-mockup */}
3783
- <div className="jp-animate-in jp-d2 rounded-[12px] overflow-hidden border border-[rgba(255,255,255,0.10)] shadow-[0_0_0_1px_rgba(255,255,255,0.04),0_40px_80px_rgba(0,0,0,0.6),0_0_60px_rgba(59,130,246,0.08)]">
3690
+ <div className="jp-animate-in jp-d2 rounded-[var(--local-radius-lg)] overflow-hidden border border-[rgba(255,255,255,0.10)] shadow-[0_0_0_1px_rgba(255,255,255,0.04),0_40px_80px_rgba(0,0,0,0.6),0_0_60px_rgba(59,130,246,0.08)]">
3784
3691
  {/* Browser bar */}
3785
3692
  <div className="bg-[#0f1923] px-3 py-2.5 flex items-center gap-1.5 border-b border-[rgba(255,255,255,0.05)]">
3786
3693
  <span className="w-2.5 h-2.5 rounded-full bg-[#ef4444]" />
3787
3694
  <span className="w-2.5 h-2.5 rounded-full bg-[#f59e0b]" />
3788
3695
  <span className="w-2.5 h-2.5 rounded-full bg-[#22c55e]" />
3789
- <span className="mx-auto font-mono text-[0.60rem] text-[rgba(255,255,255,0.20)] bg-[rgba(255,255,255,0.04)] px-3 py-0.5 rounded">localhost:5173 · Studio</span>
3696
+ <span className="mx-auto font-mono text-[0.60rem] text-[rgba(255,255,255,0.20)] bg-[rgba(255,255,255,0.04)] px-3 py-0.5 rounded-[var(--local-radius-sm)]">localhost:5173 · Studio</span>
3790
3697
  </div>
3791
3698
  {/* Split: canvas + inspector */}
3792
3699
  <div className="grid grid-cols-[1fr_260px] h-[360px] bg-[#060d1b]">
@@ -3810,8 +3717,8 @@ export const Hero: React.FC<{ data: HeroData; settings?: HeroSettings }> = ({ da
3810
3717
  {data.description?.slice(0, 100)}…
3811
3718
  </p>
3812
3719
  <div className="flex gap-1.5">
3813
- <span className="text-[0.58rem] font-semibold bg-[#3b82f6] text-white px-2.5 py-1 rounded">Read the Docs</span>
3814
- <span className="text-[0.58rem] border border-[rgba(255,255,255,0.15)] text-[#94a3b8] px-2.5 py-1 rounded">View on NPM</span>
3720
+ <span className="text-[0.58rem] font-semibold bg-[#3b82f6] text-white px-2.5 py-1 rounded-[var(--local-radius-sm)]">Read the Docs</span>
3721
+ <span className="text-[0.58rem] border border-[rgba(255,255,255,0.15)] text-[#94a3b8] px-2.5 py-1 rounded-[var(--local-radius-sm)]">View on NPM</span>
3815
3722
  </div>
3816
3723
  <div className="flex gap-4 mt-3 pt-3 border-t border-[rgba(255,255,255,0.05)]">
3817
3724
  {(data.metrics ?? []).map((m, i) => (
@@ -3858,15 +3765,15 @@ export const Hero: React.FC<{ data: HeroData; settings?: HeroSettings }> = ({ da
3858
3765
  <div className="flex-1 px-3.5 py-3 flex flex-col gap-2.5 overflow-hidden">
3859
3766
  <div>
3860
3767
  <div className="font-mono text-[0.50rem] uppercase tracking-widest text-[#334155] mb-1">Title</div>
3861
- <div className="bg-[rgba(59,130,246,0.05)] border border-[rgba(59,130,246,0.45)] rounded px-2 py-1.5 font-mono text-[0.58rem] text-[#e2e8f0] truncate">{data.title}</div>
3768
+ <div className="bg-[rgba(59,130,246,0.05)] border border-[rgba(59,130,246,0.45)] rounded-[var(--local-radius-sm)] px-2 py-1.5 font-mono text-[0.58rem] text-[#e2e8f0] truncate">{data.title}</div>
3862
3769
  </div>
3863
3770
  <div>
3864
3771
  <div className="font-mono text-[0.50rem] uppercase tracking-widest text-[#334155] mb-1">Subtitle</div>
3865
- <div className="bg-[rgba(255,255,255,0.03)] border border-[rgba(255,255,255,0.07)] rounded px-2 py-1.5 font-mono text-[0.58rem] text-[#94a3b8] truncate">{data.titleHighlight}</div>
3772
+ <div className="bg-[rgba(255,255,255,0.03)] border border-[rgba(255,255,255,0.07)] rounded-[var(--local-radius-sm)] px-2 py-1.5 font-mono text-[0.58rem] text-[#94a3b8] truncate">{data.titleHighlight}</div>
3866
3773
  </div>
3867
3774
  <div>
3868
3775
  <div className="font-mono text-[0.50rem] uppercase tracking-widest text-[#334155] mb-1">Badge</div>
3869
- <div className="bg-[rgba(255,255,255,0.03)] border border-[rgba(255,255,255,0.07)] rounded px-2 py-1.5 font-mono text-[0.58rem] text-[#94a3b8] truncate">{data.badge}</div>
3776
+ <div className="bg-[rgba(255,255,255,0.03)] border border-[rgba(255,255,255,0.07)] rounded-[var(--local-radius-sm)] px-2 py-1.5 font-mono text-[0.58rem] text-[#94a3b8] truncate">{data.badge}</div>
3870
3777
  </div>
3871
3778
  </div>
3872
3779
  {/* Bottom bar */}
@@ -3874,8 +3781,8 @@ export const Hero: React.FC<{ data: HeroData; settings?: HeroSettings }> = ({ da
3874
3781
  <span className="w-1.5 h-1.5 rounded-full bg-[#22c55e]" />
3875
3782
  <span className="font-mono text-[0.50rem] text-[#475569]">All Changes Saved</span>
3876
3783
  <div className="ml-auto flex gap-1.5">
3877
- <span className="font-mono text-[0.48rem] px-1.5 py-0.5 rounded border border-[rgba(59,130,246,0.3)] bg-[rgba(59,130,246,0.12)] text-[#60a5fa]">⬡ HTML</span>
3878
- <span className="font-mono text-[0.48rem] px-1.5 py-0.5 rounded border border-[rgba(255,255,255,0.08)] bg-[rgba(255,255,255,0.03)] text-[#94a3b8] opacity-50">{ } JSON</span>
3784
+ <span className="font-mono text-[0.48rem] px-1.5 py-0.5 rounded-[var(--local-radius-sm)] border border-[rgba(59,130,246,0.3)] bg-[rgba(59,130,246,0.12)] text-[#60a5fa]">⬡ HTML</span>
3785
+ <span className="font-mono text-[0.48rem] px-1.5 py-0.5 rounded-[var(--local-radius-sm)] border border-[rgba(255,255,255,0.08)] bg-[rgba(255,255,255,0.03)] text-[#94a3b8] opacity-50">{ } JSON</span>
3879
3786
  </div>
3880
3787
  </div>
3881
3788
  </div>
@@ -7404,13 +7311,14 @@ cat << 'END_OF_FILE_CONTENT' > "src/data/config/theme.json"
7404
7311
  "fontFamily": {
7405
7312
  "primary": "'Instrument Sans', system-ui, sans-serif",
7406
7313
  "mono": "'JetBrains Mono', monospace",
7407
- "display": "'Bricolage Grotesque', system-ui, sans-serif"
7314
+ "display": "'Bricolage Grotesque', system-ui, sans-serif",
7315
+ "display-2":"'Instrument Sans'"
7408
7316
  }
7409
7317
  },
7410
7318
  "borderRadius": {
7411
- "sm": "5px",
7412
- "md": "7px",
7413
- "lg": "8px"
7319
+ "sm": "4px",
7320
+ "md": "8px",
7321
+ "lg": "36px"
7414
7322
  }
7415
7323
  }
7416
7324
  }
@@ -8028,72 +7936,6 @@ cat << 'END_OF_FILE_CONTENT' > "src/data/pages/post.json"
8028
7936
  ]
8029
7937
  }
8030
7938
  END_OF_FILE_CONTENT
8031
- mkdir -p "src/data/pages/servizi"
8032
- echo "Creating src/data/pages/servizi/trattamento.json..."
8033
- cat << 'END_OF_FILE_CONTENT' > "src/data/pages/servizi/trattamento.json"
8034
- {
8035
- "id": "servizi-trattamento-page",
8036
- "slug": "servizi/trattamento",
8037
- "meta": {
8038
- "title": "Servizi - Trattamento",
8039
- "description": "Pagina nested di smoke test per verificare routing visitor/admin/preview."
8040
- },
8041
- "sections": [
8042
- {
8043
- "id": "hero-servizi-trattamento",
8044
- "type": "hero",
8045
- "data": {
8046
- "badge": "Smoke Test",
8047
- "title": "Trattamento",
8048
- "titleHighlight": "Pagina Nested",
8049
- "description": "Questa pagina verifica il supporto ai nested slug su filesystem e router.",
8050
- "ctas": [
8051
- {
8052
- "id": "cta-home",
8053
- "label": "Torna Home",
8054
- "href": "/",
8055
- "variant": "primary"
8056
- }
8057
- ]
8058
- },
8059
- "settings": {}
8060
- }
8061
- ]
8062
- }
8063
-
8064
- END_OF_FILE_CONTENT
8065
- echo "Creating src/data/pages/servizi_trattamento.json..."
8066
- cat << 'END_OF_FILE_CONTENT' > "src/data/pages/servizi_trattamento.json"
8067
- {
8068
- "id": "servizi-trattamento-page",
8069
- "slug": "servizi/trattamento",
8070
- "meta": {
8071
- "title": "Servizi - Trattamento",
8072
- "description": "Pagina nested di smoke test per verificare routing visitor/admin/preview."
8073
- },
8074
- "sections": [
8075
- {
8076
- "id": "hero-servizi-trattamento",
8077
- "type": "hero",
8078
- "data": {
8079
- "badge": "Smoke Test",
8080
- "title": "Trattamentos",
8081
- "titleHighlight": "Pagina Nested",
8082
- "description": "Questa pagina verifica il supporto ai nested slug su filesystem e router.",
8083
- "ctas": [
8084
- {
8085
- "id": "cta-home",
8086
- "label": "Torna Home",
8087
- "href": "/",
8088
- "variant": "primary"
8089
- }
8090
- ]
8091
- },
8092
- "settings": {}
8093
- }
8094
- ]
8095
- }
8096
- END_OF_FILE_CONTENT
8097
7939
  mkdir -p "src/emails"
8098
7940
  echo "Creating src/emails/LeadNotificationEmail.tsx..."
8099
7941
  cat << 'END_OF_FILE_CONTENT' > "src/emails/LeadNotificationEmail.tsx"
@@ -8407,7 +8249,7 @@ export default LeadSenderConfirmationEmail;
8407
8249
  END_OF_FILE_CONTENT
8408
8250
  echo "Creating src/fonts.css..."
8409
8251
  cat << 'END_OF_FILE_CONTENT' > "src/fonts.css"
8410
- @import url('https://fonts.googleapis.com/css2?family=Bricolage+Grotesque:opsz,wght@12..96,400;12..96,600;12..96,700;12..96,800&family=JetBrains+Mono:wght@400;500;600;700&family=Instrument+Sans:wght@400;500;600&display=swap');
8252
+ @import url('https://fonts.googleapis.com/css2?family=Instrument+Sans:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500&family=Playfair+Display:wght@700;800;900&display=swap');
8411
8253
 
8412
8254
  END_OF_FILE_CONTENT
8413
8255
  mkdir -p "src/hooks"
@@ -8472,14 +8314,13 @@ cat << 'END_OF_FILE_CONTENT' > "src/index.css"
8472
8314
  --font-primary: var(--theme-font-primary);
8473
8315
  --font-mono: var(--theme-font-mono);
8474
8316
 
8475
- /*
8476
- 🔧 DISPLAY FONT — Forward-compatible workaround
8477
- theme-manager.ts does NOT inject --theme-font-display (Skeleton v2.7 gap).
8478
- The var() falls through to the hardcoded fallback today.
8479
- When Skeleton v2.8 wires display into theme-manager, the var() will resolve
8480
- automatically and the fallback becomes dead code.
8317
+ /*
8318
+ DISPLAY FONT bridge
8319
+ The core now emits --theme-font-display from theme.json, so this keeps
8320
+ the tenant on the stable semantic alias rather than depending on the
8321
+ flattened internal variable path.
8481
8322
  */
8482
- --font-display: var(--theme-font-display, 'Playfair Display', Georgia, serif);
8323
+ --font-display: var(--theme-font-display);
8483
8324
  }
8484
8325
 
8485
8326
  /*
@@ -8497,7 +8338,7 @@ cat << 'END_OF_FILE_CONTENT' > "src/index.css"
8497
8338
  --muted: var(--theme-surface-alt);
8498
8339
  --muted-foreground: var(--theme-text-muted);
8499
8340
  --border: var(--theme-border);
8500
- --radius: 0.45rem;
8341
+ --radius: 3.45rem;
8501
8342
 
8502
8343
  /*
8503
8344
  🔧 ACCENT CHAIN — Forward-compatible workaround
@@ -9394,24 +9235,6 @@ ReactDOM.createRoot(document.getElementById('root')!).render(
9394
9235
 
9395
9236
 
9396
9237
 
9397
- END_OF_FILE_CONTENT
9398
- echo "Creating src/main_.tsx..."
9399
- cat << 'END_OF_FILE_CONTENT' > "src/main_.tsx"
9400
- import '@/types'; // TBP: load type augmentation from capsule-driven types
9401
- import React from 'react';
9402
- import ReactDOM from 'react-dom/client';
9403
- import App from './App';
9404
- // ... resto del file
9405
-
9406
- ReactDOM.createRoot(document.getElementById('root')!).render(
9407
- <React.StrictMode>
9408
- <App />
9409
- </React.StrictMode>
9410
- );
9411
-
9412
-
9413
-
9414
-
9415
9238
  END_OF_FILE_CONTENT
9416
9239
  # SKIP: src/registry-types.ts is binary and cannot be embedded as text.
9417
9240
  mkdir -p "src/server"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@olonjs/cli",
3
- "version": "3.0.83",
3
+ "version": "3.0.85",
4
4
  "description": "The Sovereign CLI Engine for OlonJS.",
5
5
  "type": "module",
6
6
  "bin": {