@olonjs/cli 3.0.110 → 3.0.112

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.
@@ -1644,7 +1644,7 @@ cat << 'END_OF_FILE_CONTENT' > "package.json"
1644
1644
  "@tiptap/extension-link": "^2.11.5",
1645
1645
  "@tiptap/react": "^2.11.5",
1646
1646
  "@tiptap/starter-kit": "^2.11.5",
1647
- "@olonjs/core": "^1.0.97",
1647
+ "@olonjs/core": "^1.0.99",
1648
1648
  "class-variance-authority": "^0.7.1",
1649
1649
  "clsx": "^2.1.1",
1650
1650
  "lucide-react": "^0.474.0",
@@ -5968,7 +5968,7 @@ mkdir -p "src/components/header"
5968
5968
  echo "Creating src/components/header/View.tsx..."
5969
5969
  cat << 'END_OF_FILE_CONTENT' > "src/components/header/View.tsx"
5970
5970
  import { useState, useRef, useEffect } from 'react';
5971
- import { Menu, X, ChevronDown } from 'lucide-react';
5971
+ import { Menu, X, ChevronDown, Zap } from 'lucide-react';
5972
5972
  import { OlonMark } from '@/components/OlonWordmark';
5973
5973
  import { Button } from '@/components/ui/button';
5974
5974
  import { ThemeToggle } from '@/components/ThemeToggle';
@@ -6050,7 +6050,7 @@ export function Header({ data, settings, menu }: HeaderViewProps) {
6050
6050
  >
6051
6051
  <div className="max-w-6xl mx-auto px-6 h-18 flex items-center gap-8">
6052
6052
 
6053
- {/* Logo */}
6053
+ {/* Logo da homepage */}
6054
6054
  <a href="/" className="flex items-center gap-2 shrink-0" aria-label="OlonJS home">
6055
6055
  <OlonMark size={26} className="mb-0.5" />
6056
6056
  <div className="flex items-center gap-1"><span
@@ -6160,7 +6160,7 @@ export function Header({ data, settings, menu }: HeaderViewProps) {
6160
6160
  );
6161
6161
  })}
6162
6162
  </nav>
6163
-
6163
+
6164
6164
  {/* Actions */}
6165
6165
  <div className="hidden md:flex items-center gap-1 ml-auto shrink-0">
6166
6166
  <ThemeToggle />
@@ -6189,6 +6189,15 @@ export function Header({ data, settings, menu }: HeaderViewProps) {
6189
6189
  </button>
6190
6190
  </div>
6191
6191
 
6192
+ {/* Banner Sotto il Menu */}
6193
+ <div className="border-t border-border/60 py-1 px-4 text-center text-[10px] uppercase font-semibold tracking-wider text-muted-foreground flex justify-center items-center gap-1.5 bg-background/50">
6194
+ <Zap className="w-2.5 h-2.5 text-primary-light" />
6195
+ <span>Built with</span>
6196
+ <a href="https://github.com/olonjs/npm-jpcore" target="_blank" rel="noopener noreferrer" className="text-foreground hover:text-primary-light transition-colors font-bold">
6197
+ OlonJS
6198
+ </a>
6199
+ </div>
6200
+
6192
6201
  {/* Mobile drawer */}
6193
6202
  <div className={cn(
6194
6203
  'md:hidden border-t border-border bg-card overflow-hidden transition-all duration-200',
@@ -6292,49 +6301,205 @@ export function Header({ data, settings, menu }: HeaderViewProps) {
6292
6301
  );
6293
6302
  }
6294
6303
 
6304
+ END_OF_FILE_CONTENT
6305
+ # SKIP: src/components/header/View.tsx:Zone.Identifier is binary and cannot be embedded as text.
6306
+ echo "Creating src/components/header/View_.tsx..."
6307
+ cat << 'END_OF_FILE_CONTENT' > "src/components/header/View_.tsx"
6308
+ import React, { useState } from 'react';
6309
+ import { cn } from '@/lib/utils';
6310
+ import { OlonMark } from '@/components/ui/OlonMark';
6311
+ import { Badge } from '@/components/ui/badge';
6312
+ import { Button } from '@/components/ui/button';
6313
+ import { Sparkles } from 'lucide-react';
6314
+ import type { MenuItem } from '@olonjs/core';
6315
+ import type { HeaderData, HeaderSettings } from './types';
6316
+
6317
+ export const Header: React.FC<{
6318
+ data: HeaderData;
6319
+ settings?: HeaderSettings;
6320
+ menu: MenuItem[];
6321
+ }> = ({ data, menu }) => {
6322
+ const [mobileMenuOpen, setMobileMenuOpen] = useState(false);
6323
+
6324
+ return (
6325
+ <>
6326
+ <div style={{ height: '80px' }} aria-hidden />
6327
+ <header
6328
+ className={cn(
6329
+ 'fixed top-0 left-0 right-0 w-full z-50 transition-all duration-300',
6330
+ 'flex flex-col',
6331
+ 'bg-background/88 backdrop-blur-[16px] border-b border-border/60'
6332
+ )}
6333
+ >
6334
+ <div className="max-w-[1040px] w-full h-14 mx-auto px-8 flex items-center gap-3">
6335
+
6336
+
6337
+ {/* Logo */}
6338
+ <a href="/" className="flex items-center gap-2 shrink-0" aria-label="OlonJS home">
6339
+ <OlonMark size={26} className="mb-0.5" />
6340
+ <div className="flex items-center gap-1"><span
6341
+ className="text-2xl text-foreground leading-none"
6342
+ style={{
6343
+ fontFamily: 'var(--wordmark-font)',
6344
+ letterSpacing: 'var(--wordmark-tracking)',
6345
+ fontWeight: 'var(--wordmark-weight)',
6346
+ fontVariationSettings: '"wdth" var(--wordmark-width)',
6347
+ }}
6348
+ >
6349
+ {data.logoText}
6350
+ </span>
6351
+ <span className="text-primary-light font-mono">{data.badge}</span>
6352
+ </div>
6353
+ </a>
6354
+
6355
+ {data.badge && (
6356
+ <>
6357
+ <span className="w-px h-4 bg-border" aria-hidden />
6358
+ <Badge variant="outline" className="rounded-full" data-jp-field="badge">
6359
+ {data.badge}
6360
+ </Badge>
6361
+ </>
6362
+ )}
6363
+
6364
+ <div className="flex-1" />
6365
+
6366
+ <nav className="hidden md:flex items-center gap-0.5" aria-label="Site">
6367
+ {menu.map((item, idx) => (
6368
+ <Button
6369
+ key={(item as { id?: string }).id ?? idx}
6370
+ asChild
6371
+ variant={item.isCta ? 'default' : 'ghost'}
6372
+ size="sm"
6373
+ className={cn(
6374
+ 'text-[13px]',
6375
+ !item.isCta && 'text-muted-foreground hover:text-foreground'
6376
+ )}
6377
+ >
6378
+ <a
6379
+ href={item.href}
6380
+ data-jp-item-id={(item as { id?: string }).id ?? `legacy-${idx}`}
6381
+ data-jp-item-field="links"
6382
+ target={item.external ? '_blank' : undefined}
6383
+ rel={item.external ? 'noopener noreferrer' : undefined}
6384
+ >
6385
+ {item.label}
6386
+ </a>
6387
+ </Button>
6388
+ ))}
6389
+ </nav>
6390
+
6391
+ <button
6392
+ type="button"
6393
+ className="md:hidden p-2 rounded-lg border border-border text-muted-foreground hover:bg-muted hover:text-foreground transition-colors"
6394
+ onClick={() => setMobileMenuOpen(!mobileMenuOpen)}
6395
+ aria-label={mobileMenuOpen ? 'Close menu' : 'Open menu'}
6396
+ >
6397
+ <svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
6398
+ {mobileMenuOpen ? (
6399
+ <><line x1="18" y1="6" x2="6" y2="18" /><line x1="6" y1="6" x2="18" y2="18" /></>
6400
+ ) : (
6401
+ <><line x1="3" y1="12" x2="21" y2="12" /><line x1="3" y1="6" x2="21" y2="6" /><line x1="3" y1="18" x2="21" y2="18" /></>
6402
+ )}
6403
+ </svg>
6404
+ </button>
6405
+ </div>
6406
+
6407
+ {/* Banner Sotto il Menu */}
6408
+ <div className="border-t border-border/60 py-1 px-4 text-center text-[10px] uppercase font-semibold tracking-wider text-muted-foreground flex justify-center items-center gap-1.5 bg-background/50">
6409
+ <Sparkles className="w-2.5 h-2.5 text-primary-light" />
6410
+ <span>Built with</span>
6411
+ <a href="https://github.com/olonjs/npm-jpcore" target="_blank" rel="noopener noreferrer" className="text-foreground hover:text-primary-light transition-colors font-bold">
6412
+ OlonJS
6413
+ </a>
6414
+ </div>
6415
+
6416
+ {mobileMenuOpen && (
6417
+ <nav
6418
+ className="absolute top-[82px] left-0 right-0 md:hidden border-b border-border bg-background/95 backdrop-blur-[16px]"
6419
+ aria-label="Mobile menu"
6420
+ >
6421
+ <div className="max-w-[1040px] mx-auto px-8 py-4 flex flex-col gap-1">
6422
+ {menu.map((item, idx) => (
6423
+ <a
6424
+ key={(item as { id?: string }).id ?? idx}
6425
+ href={item.href}
6426
+ className="text-sm font-medium text-muted-foreground hover:text-foreground transition-colors py-2.5 no-underline"
6427
+ onClick={() => setMobileMenuOpen(false)}
6428
+ data-jp-item-id={(item as { id?: string }).id ?? `legacy-${idx}`}
6429
+ data-jp-item-field="links"
6430
+ >
6431
+ {item.label}
6432
+ </a>
6433
+ ))}
6434
+ </div>
6435
+ </nav>
6436
+ )}
6437
+ </header>
6438
+ </>
6439
+ );
6440
+ };
6441
+
6295
6442
  END_OF_FILE_CONTENT
6296
6443
  echo "Creating src/components/header/index.ts..."
6297
6444
  cat << 'END_OF_FILE_CONTENT' > "src/components/header/index.ts"
6298
- export * from './View';
6299
- export * from './schema';
6300
- export * from './types';
6301
-
6445
+ export * from './View';
6446
+ export * from './schema';
6447
+ export * from './types';
6302
6448
  END_OF_FILE_CONTENT
6449
+ # SKIP: src/components/header/index.ts:Zone.Identifier is binary and cannot be embedded as text.
6303
6450
  echo "Creating src/components/header/schema.ts..."
6304
6451
  cat << 'END_OF_FILE_CONTENT' > "src/components/header/schema.ts"
6305
- import { z } from 'zod';
6306
-
6307
- export const HeaderSchema = z.object({
6308
- logoText: z.string().describe('ui:text'),
6309
- badge: z.string().optional().describe('ui:text'),
6310
- links: z.array(z.object({
6311
- label: z.string().describe('ui:text'),
6312
- href: z.string().describe('ui:text'),
6313
- variant: z.string().optional().describe('ui:text'),
6314
- children: z.array(z.object({
6315
- label: z.string().describe('ui:text'),
6316
- href: z.string().describe('ui:text'),
6317
- })).optional().describe('ui:list'),
6318
- })).describe('ui:list'),
6319
- ctaLabel: z.string().optional().describe('ui:text'),
6320
- ctaHref: z.string().optional().describe('ui:text'),
6321
- signinHref: z.string().optional().describe('ui:text'),
6322
- });
6323
-
6324
- export const HeaderSettingsSchema = z.object({
6325
- sticky: z.boolean().default(true).describe('ui:checkbox'),
6326
- });
6452
+ import { z } from 'zod';
6453
+
6454
+ /**
6455
+ * 📝 HEADER SCHEMA (Contract)
6456
+ * Definisce la struttura dati che l'Admin userà per generare la form.
6457
+ */
6458
+ export const HeaderSchema = z.object({
6459
+ logoText: z.string().describe('ui:text'),
6460
+ logoHighlight: z.string().optional().describe('ui:text'),
6461
+ logoIconText: z.string().optional().describe('ui:text'),
6462
+ badge: z.string().optional().describe('ui:text'),
6463
+ signinHref: z.string().optional().describe('ui:text'),
6464
+ ctaHref: z.string().optional().describe('ui:text'),
6465
+ ctaLabel: z.string().optional().describe('ui:text'),
6466
+ links: z.array(z.object({
6467
+ label: z.string().describe('ui:text'),
6468
+ href: z.string().describe('ui:text'),
6469
+ isCta: z.boolean().default(false).describe('ui:checkbox'),
6470
+ external: z.boolean().default(false).optional().describe('ui:checkbox'),
6471
+ })).describe('ui:list'),
6472
+ });
6327
6473
 
6474
+ /**
6475
+ * ⚙️ HEADER SETTINGS
6476
+ * Definisce i parametri tecnici (non di contenuto).
6477
+ */
6478
+ export const HeaderSettingsSchema = z.object({
6479
+ sticky: z.boolean().default(true).describe('ui:checkbox'),
6480
+ });
6328
6481
  END_OF_FILE_CONTENT
6482
+ # SKIP: src/components/header/schema.ts:Zone.Identifier is binary and cannot be embedded as text.
6329
6483
  echo "Creating src/components/header/types.ts..."
6330
6484
  cat << 'END_OF_FILE_CONTENT' > "src/components/header/types.ts"
6331
- import { z } from 'zod';
6332
- import { HeaderSchema, HeaderSettingsSchema } from './schema';
6333
-
6334
- export type HeaderData = z.infer<typeof HeaderSchema>;
6335
- export type HeaderSettings = z.infer<typeof HeaderSettingsSchema>;
6485
+ import { z } from 'zod';
6486
+ import { HeaderSchema, HeaderSettingsSchema } from './schema';
6336
6487
 
6488
+ /**
6489
+ * 🧩 HEADER DATA
6490
+ * Tipo inferito dallo schema Zod del contenuto.
6491
+ * Utilizzato dalla View per renderizzare logo e links.
6492
+ */
6493
+ export type HeaderData = z.infer<typeof HeaderSchema>;
6494
+
6495
+ /**
6496
+ * ⚙️ HEADER SETTINGS
6497
+ * Tipo inferito dallo schema Zod dei settings.
6498
+ * Gestisce comportamenti tecnici come lo 'sticky'.
6499
+ */
6500
+ export type HeaderSettings = z.infer<typeof HeaderSettingsSchema>;
6337
6501
  END_OF_FILE_CONTENT
6502
+ # SKIP: src/components/header/types.ts:Zone.Identifier is binary and cannot be embedded as text.
6338
6503
  mkdir -p "src/components/hero"
6339
6504
  # SKIP: src/components/hero/RadialBackground - Copy.tsx:Zone.Identifier is binary and cannot be embedded as text.
6340
6505
  echo "Creating src/components/hero/RadialBackground.tsx..."
@@ -7012,7 +7177,7 @@ export function OlonArchitectureView({ data }: Props) {
7012
7177
  <div className="max-w-6xl mx-auto px-8">
7013
7178
  <p className="text-xs font-semibold tracking-[0.12em] uppercase text-[var(--local-muted)] mb-3"
7014
7179
  data-jp-field="label">{data.label}</p>
7015
- <h2 className="text-4xl font-bold tracking-[-0.03em] text-white mb-3"
7180
+ <h2 className="text-4xl font-bold tracking-[-0.03em] text-foreground mb-3"
7016
7181
  data-jp-field="headline">{data.headline}</h2>
7017
7182
  <p className="text-base text-[var(--local-muted)] leading-relaxed max-w-2xl mb-3"
7018
7183
  data-jp-field="body">{data.body}</p>
@@ -7035,11 +7200,11 @@ export function OlonArchitectureView({ data }: Props) {
7035
7200
  <div>{ICONS[p.icon]}</div>
7036
7201
  <p className="text-[11px] font-semibold tracking-[0.1em] uppercase text-[var(--local-p400)] font-mono"
7037
7202
  data-jp-field="version">{p.acronym} · {p.version}</p>
7038
- <p className="font-bold text-white text-base" data-jp-field="name">{p.name}</p>
7203
+ <p className="font-bold text-foreground text-base" data-jp-field="name">{p.name}</p>
7039
7204
  <p className="text-sm text-[var(--local-muted)] leading-relaxed flex-1"
7040
7205
  data-jp-field="desc">{p.desc}</p>
7041
7206
  <a href={p.specHref} target="_blank" rel="noopener noreferrer"
7042
- className="text-xs text-[var(--local-p400)] hover:text-white transition-colors mt-auto"
7207
+ className="text-xs text-[var(--local-p400)] hover:text-foreground transition-colors mt-auto"
7043
7208
  data-jp-field="specHref">Read spec ↗</a>
7044
7209
  </div>
7045
7210
  ))}
@@ -7113,7 +7278,7 @@ export function OlonExampleView({ data }: Props) {
7113
7278
  <div className="max-w-6xl mx-auto px-8">
7114
7279
  <p className="text-xs font-semibold tracking-[0.12em] uppercase text-[var(--local-muted)] mb-3"
7115
7280
  data-jp-field="label">{data.label}</p>
7116
- <h2 className="text-4xl font-bold tracking-[-0.03em] text-white mb-3"
7281
+ <h2 className="text-4xl font-bold tracking-[-0.03em] text-foreground mb-3"
7117
7282
  data-jp-field="headline">{data.headline}</h2>
7118
7283
  <p className="text-base text-[var(--local-muted)] leading-relaxed max-w-2xl mb-12"
7119
7284
  data-jp-field="body">{data.body}</p>
@@ -7124,14 +7289,14 @@ export function OlonExampleView({ data }: Props) {
7124
7289
  className="bg-[var(--local-card)] border border-[var(--local-border)] rounded-2xl overflow-hidden">
7125
7290
  {/* Step header */}
7126
7291
  <div className="px-6 py-4 border-b border-[var(--local-border)] flex items-center gap-3">
7127
- <span className="w-6 h-6 rounded-full bg-[var(--local-p400)] text-white text-xs font-bold flex items-center justify-center flex-shrink-0">
7292
+ <span className="w-6 h-6 rounded-full bg-[var(--local-p400)] text-foreground text-xs font-bold flex items-center justify-center flex-shrink-0">
7128
7293
  {step.number}
7129
7294
  </span>
7130
- <span className="font-semibold text-white text-sm">{step.title}</span>
7295
+ <span className="font-semibold text-foreground text-sm">{step.title}</span>
7131
7296
  <span className="ml-auto text-xs text-[var(--local-muted)]">{step.meta}</span>
7132
7297
  </div>
7133
7298
  {/* Code block */}
7134
- <pre className="p-6 font-mono text-xs leading-relaxed bg-[#080E14] text-[var(--local-fg)] overflow-x-auto whitespace-pre-wrap min-h-[200px]">
7299
+ <pre className="p-6 font-mono text-xs leading-relaxed bg-[#080E14] text-gray-300 overflow-x-auto whitespace-pre-wrap min-h-[200px]">
7135
7300
  {step.code}
7136
7301
  </pre>
7137
7302
  </div>
@@ -7231,7 +7396,7 @@ export function OlonGetStartedView({ data }: Props) {
7231
7396
  <div className="max-w-6xl mx-auto px-8">
7232
7397
  <p className="text-xs font-semibold tracking-[0.12em] uppercase text-[var(--local-muted)] mb-3"
7233
7398
  data-jp-field="label">{data.label}</p>
7234
- <h2 className="text-4xl font-bold tracking-[-0.03em] text-white mb-3"
7399
+ <h2 className="text-4xl font-bold tracking-[-0.03em] text-foreground mb-3"
7235
7400
  data-jp-field="headline">{data.headline}</h2>
7236
7401
  <p className="text-base text-[var(--local-muted)] leading-relaxed max-w-2xl mb-12"
7237
7402
  data-jp-field="body">{data.body}</p>
@@ -7246,7 +7411,7 @@ export function OlonGetStartedView({ data }: Props) {
7246
7411
  data-jp-field="badge">
7247
7412
  {card.badge}
7248
7413
  </span>
7249
- <p className="font-bold text-white text-base" data-jp-field="title">{card.title}</p>
7414
+ <p className="font-bold text-foreground text-base" data-jp-field="title">{card.title}</p>
7250
7415
  <p className="text-sm text-[var(--local-muted)] leading-relaxed flex-1"
7251
7416
  data-jp-field="body">{card.body}</p>
7252
7417
  {card.code && (
@@ -7449,12 +7614,15 @@ echo "Creating src/components/olon-hero/View.tsx..."
7449
7614
  cat << 'END_OF_FILE_CONTENT' > "src/components/olon-hero/View.tsx"
7450
7615
  import type { OlonHeroData } from './types';
7451
7616
  import { Button } from '@/components/ui/button';
7452
- import { DawnBackground } from './DawnBackground';
7617
+ import { Github, Terminal } from 'lucide-react';
7618
+
7453
7619
 
7454
7620
  interface Props {
7455
7621
  data: OlonHeroData;
7456
7622
  }
7457
7623
 
7624
+ const heroPlugImage = '/assets/images/plug-graded-square.jpg';
7625
+
7458
7626
  export function OlonHeroView({ data }: Props) {
7459
7627
  return (
7460
7628
  <section
@@ -7470,22 +7638,32 @@ export function OlonHeroView({ data }: Props) {
7470
7638
  data-jp-section-type="olon-hero"
7471
7639
  >
7472
7640
  {/* Dawn background — absolute, behind content */}
7473
- <DawnBackground />
7641
+
7474
7642
 
7475
7643
  {/* Content — relative, above background */}
7476
7644
  <div className="relative z-10 max-w-6xl mx-auto px-8 grid grid-cols-1 md:grid-cols-2 gap-16 items-center">
7477
7645
  {/* Left: copy */}
7478
7646
  <div className="flex flex-col gap-6">
7479
- <p
7480
- className="text-xs font-semibold tracking-[0.12em] uppercase text-[var(--local-muted)]"
7481
- data-jp-field="eyebrow"
7482
- >
7483
- {data.eyebrow}
7484
- </p>
7647
+ <div className="flex flex-wrap items-center gap-3">
7648
+ <p
7649
+ className="text-xs font-semibold tracking-[0.12em] uppercase text-[var(--local-muted)]"
7650
+ data-jp-field="eyebrow"
7651
+ >
7652
+ {data.eyebrow}
7653
+ </p>
7654
+ <div className="flex items-center gap-2">
7655
+ <a href="https://www.npmjs.com/package/@olonjs/core">
7656
+ <img src="https://img.shields.io/npm/v/@olonjs/core?color=blue&style=flat-square" alt="npm version"/>
7657
+ </a>
7658
+ <a href="https://github.com/olonjs/npm-jpcore/blob/main/LICENSE">
7659
+ <img src="https://img.shields.io/badge/license-MIT-green?style=flat-square" alt="license"/>
7660
+ </a>
7661
+ </div>
7662
+ </div>
7485
7663
 
7486
7664
  <div>
7487
7665
  <h1
7488
- className="text-5xl md:text-6xl font-bold tracking-[-0.03em] leading-[1.05] text-white"
7666
+ className="text-6xl md:text-7xl font-bold tracking-[-0.03em] leading-[1.05] text-foreground"
7489
7667
  data-jp-field="headline"
7490
7668
  >
7491
7669
  {data.headline}
@@ -7507,78 +7685,41 @@ export function OlonHeroView({ data }: Props) {
7507
7685
 
7508
7686
  <div className="flex flex-wrap gap-3 items-center">
7509
7687
  <Button asChild size="lg" className="font-semibold">
7510
- <a href={data.cta.primary.href}>{data.cta.primary.label} →</a>
7688
+ <a href={data.cta.primary.href}>
7689
+ {data.cta.primary.label} →
7690
+ </a>
7511
7691
  </Button>
7512
- <Button asChild variant="outline" size="lg" className="font-semibold">
7513
- <a href={data.cta.secondary.href}>{data.cta.secondary.label}</a>
7692
+ <Button asChild variant="outline" size="lg" className="font-semibold gap-2">
7693
+ <a href={data.cta.secondary.href}>
7694
+ <Github className="w-4 h-4" />
7695
+ {data.cta.secondary.label}
7696
+ </a>
7514
7697
  </Button>
7515
7698
  <a
7516
7699
  href={data.cta.ghost.href}
7517
7700
  className="text-sm text-[var(--local-muted)] hover:text-[var(--local-fg)] transition-colors flex items-center gap-1.5"
7518
7701
  >
7519
7702
  {data.cta.ghost.label}
7703
+ <Terminal className="w-4 h-4" />
7520
7704
  </a>
7521
7705
  </div>
7522
7706
  </div>
7523
7707
 
7524
- {/* Right: SVG illustration */}
7708
+ {/* Right: branded product photo */}
7525
7709
  <div className="hidden md:flex items-center justify-center">
7526
- <svg
7527
- viewBox="0 0 400 400"
7528
- fill="none"
7529
- xmlns="http://www.w3.org/2000/svg"
7530
- className="w-full max-w-md"
7531
- >
7532
- <defs>
7533
- <linearGradient id="hero-main" x1="0" y1="0" x2="1" y2="1">
7534
- <stop offset="0%" stopColor="#84ABFF"/>
7535
- <stop offset="60%" stopColor="#1763FF"/>
7536
- <stop offset="100%" stopColor="#0F52E0"/>
7537
- </linearGradient>
7538
- <linearGradient id="hero-accent" x1="0" y1="0" x2="0" y2="1">
7539
- <stop offset="0%" stopColor="#EEF3FF"/>
7540
- <stop offset="100%" stopColor="#84ABFF"/>
7541
- </linearGradient>
7542
- <linearGradient id="hero-glow" x1="0" y1="0" x2="0" y2="1">
7543
- <stop offset="0%" stopColor="#1763FF" stopOpacity="0.3"/>
7544
- <stop offset="100%" stopColor="#1763FF" stopOpacity="0"/>
7545
- </linearGradient>
7546
- <filter id="glow">
7547
- <feGaussianBlur stdDeviation="8" result="blur"/>
7548
- <feComposite in="SourceGraphic" in2="blur" operator="over"/>
7549
- </filter>
7550
- </defs>
7551
- <circle cx="200" cy="200" r="160" fill="url(#hero-glow)" opacity="0.4"/>
7552
- <rect x="90" y="90" width="220" height="220" rx="28" fill="none" stroke="url(#hero-main)" strokeWidth="14"/>
7553
- {/* Left pins */}
7554
- <line x1="16" y1="148" x2="90" y2="148" stroke="url(#hero-main)" strokeWidth="10" strokeLinecap="round"/>
7555
- <line x1="16" y1="200" x2="90" y2="200" stroke="url(#hero-main)" strokeWidth="10" strokeLinecap="round"/>
7556
- <line x1="16" y1="252" x2="90" y2="252" stroke="url(#hero-main)" strokeWidth="10" strokeLinecap="round"/>
7557
- {/* Right pins */}
7558
- <line x1="310" y1="148" x2="384" y2="148" stroke="url(#hero-main)" strokeWidth="10" strokeLinecap="round"/>
7559
- <line x1="310" y1="200" x2="384" y2="200" stroke="url(#hero-main)" strokeWidth="10" strokeLinecap="round"/>
7560
- <line x1="310" y1="252" x2="384" y2="252" stroke="url(#hero-main)" strokeWidth="10" strokeLinecap="round"/>
7561
- {/* Top pins */}
7562
- <line x1="148" y1="16" x2="148" y2="90" stroke="url(#hero-main)" strokeWidth="10" strokeLinecap="round"/>
7563
- <line x1="200" y1="16" x2="200" y2="90" stroke="url(#hero-main)" strokeWidth="10" strokeLinecap="round"/>
7564
- <line x1="252" y1="16" x2="252" y2="90" stroke="url(#hero-main)" strokeWidth="10" strokeLinecap="round"/>
7565
- {/* Bottom pins */}
7566
- <line x1="148" y1="310" x2="148" y2="384" stroke="url(#hero-main)" strokeWidth="10" strokeLinecap="round"/>
7567
- <line x1="200" y1="310" x2="200" y2="384" stroke="url(#hero-main)" strokeWidth="10" strokeLinecap="round"/>
7568
- <line x1="252" y1="310" x2="252" y2="384" stroke="url(#hero-main)" strokeWidth="10" strokeLinecap="round"/>
7569
- {/* Corner nodes */}
7570
- <circle cx="148" cy="148" r="13" fill="url(#hero-main)"/>
7571
- <circle cx="252" cy="148" r="13" fill="url(#hero-main)"/>
7572
- <circle cx="148" cy="252" r="13" fill="url(#hero-main)"/>
7573
- <circle cx="252" cy="252" r="13" fill="url(#hero-main)"/>
7574
- {/* Connection lines */}
7575
- <line x1="148" y1="148" x2="200" y2="200" stroke="#84ABFF" strokeWidth="2.5" opacity="0.35"/>
7576
- <line x1="252" y1="148" x2="200" y2="200" stroke="#84ABFF" strokeWidth="2.5" opacity="0.35"/>
7577
- <line x1="148" y1="252" x2="200" y2="200" stroke="#84ABFF" strokeWidth="2.5" opacity="0.35"/>
7578
- <line x1="252" y1="252" x2="200" y2="200" stroke="#84ABFF" strokeWidth="2.5" opacity="0.35"/>
7579
- {/* Center node */}
7580
- <circle cx="200" cy="200" r="18" fill="url(#hero-accent)" filter="url(#glow)"/>
7581
- </svg>
7710
+ <div className="relative w-full max-w-lg">
7711
+ <div className="absolute inset-[-8%] bg-[radial-gradient(circle_at_50%_50%,rgba(52,109,255,0.22),rgba(12,17,22,0)_70%)] blur-2xl" />
7712
+ <div className="relative aspect-[1/1.03] overflow-hidden rounded-none border border-white/14 bg-[#0d1219] shadow-[0_22px_56px_rgba(4,8,20,0.42)]">
7713
+ <img
7714
+ src={heroPlugImage}
7715
+ alt="Olon interface port engraved into a dark stone surface"
7716
+ className="absolute inset-0 h-full w-full object-cover"
7717
+ style={{ objectPosition: '50% 50%' }}
7718
+ />
7719
+ <div className="absolute inset-0 bg-[linear-gradient(180deg,rgba(7,11,21,0.02)_0%,rgba(7,11,21,0.14)_24%,rgba(7,11,21,0.44)_100%)]" />
7720
+ <div className="absolute inset-0 bg-[radial-gradient(circle_at_62%_44%,rgba(122,163,255,0.18),rgba(29,78,216,0.08)_24%,rgba(12,17,22,0)_54%)] mix-blend-screen" />
7721
+ </div>
7722
+ </div>
7582
7723
  </div>
7583
7724
  </div>
7584
7725
  </section>
@@ -7618,7 +7759,7 @@ export function OlonHeroView({ data }: Props) {
7618
7759
  </p>
7619
7760
 
7620
7761
  <div>
7621
- <h1 className="text-5xl md:text-6xl font-bold tracking-[-0.03em] leading-[1.05] text-white"
7762
+ <h1 className="text-5xl md:text-6xl font-bold tracking-[-0.03em] leading-[1.05] text-foreground"
7622
7763
  data-jp-field="headline">
7623
7764
  {data.headline}
7624
7765
  </h1>
@@ -7795,7 +7936,7 @@ export function OlonWhyView({ data }: Props) {
7795
7936
  <div className="max-w-6xl mx-auto px-8">
7796
7937
  <p className="text-xs font-semibold tracking-[0.12em] uppercase text-[var(--local-muted)] mb-3"
7797
7938
  data-jp-field="label">{data.label}</p>
7798
- <h2 className="text-4xl font-bold tracking-[-0.03em] text-white leading-tight"
7939
+ <h2 className="text-4xl font-bold tracking-[-0.03em] text-foreground leading-tight"
7799
7940
  data-jp-field="headline">{data.headline}</h2>
7800
7941
  <p className="text-3xl font-semibold tracking-[-0.03em] text-[var(--local-p300)] leading-tight mb-4"
7801
7942
  data-jp-field="subline">{data.subline}</p>
@@ -7809,7 +7950,7 @@ export function OlonWhyView({ data }: Props) {
7809
7950
  className="bg-[var(--local-card)] p-8 flex flex-col gap-4 border-r last:border-r-0 border-[var(--local-border)]"
7810
7951
  data-jp-item-id={pillar.id}>
7811
7952
  <div>{ICONS[pillar.icon]}</div>
7812
- <div className="font-bold text-white" data-jp-field="title">{pillar.title}</div>
7953
+ <div className="font-bold text-foreground" data-jp-field="title">{pillar.title}</div>
7813
7954
  <div className="text-sm text-[var(--local-muted)] leading-relaxed" data-jp-field="body">{pillar.body}</div>
7814
7955
  </div>
7815
7956
  ))}
@@ -10118,7 +10259,7 @@ const StudioTiptapEditor: React.FC<{ data: TiptapData }> = ({ data }) => {
10118
10259
  type="button"
10119
10260
  onMouseDown={(e) => e.preventDefault()}
10120
10261
  onClick={applyLink}
10121
- className="shrink-0 rounded-[var(--local-radius-sm)] px-2 py-0.5 text-xs bg-[var(--local-primary)] hover:brightness-110 text-white transition-colors"
10262
+ className="shrink-0 rounded-[var(--local-radius-sm)] px-2 py-0.5 text-xs bg-[var(--local-primary)] hover:brightness-110 text-foreground transition-colors"
10122
10263
  >
10123
10264
  Set
10124
10265
  </button>
@@ -10313,88 +10454,88 @@ END_OF_FILE_CONTENT
10313
10454
  mkdir -p "src/components/ui"
10314
10455
  echo "Creating src/components/ui/OlonMark.tsx..."
10315
10456
  cat << 'END_OF_FILE_CONTENT' > "src/components/ui/OlonMark.tsx"
10316
- import { cn } from '@/lib/utils'
10317
-
10318
- interface OlonMarkProps {
10319
- size?: number
10320
- /** mono: uses currentColor — for single-colour print/emboss contexts */
10321
- variant?: 'default' | 'mono'
10322
- className?: string
10323
- }
10324
-
10325
- export function OlonMark({ size = 32, variant = 'default', className }: OlonMarkProps) {
10326
- const gid = `olon-ring-${size}`
10327
-
10328
- if (variant === 'mono') {
10329
- return (
10330
- <svg
10331
- viewBox="0 0 100 100"
10332
- fill="none"
10333
- xmlns="http://www.w3.org/2000/svg"
10334
- width={size}
10335
- height={size}
10336
- aria-label="Olon mark"
10337
- className={cn('flex-shrink-0', className)}
10338
- >
10339
- <circle cx="50" cy="50" r="38" stroke="currentColor" strokeWidth="20"/>
10340
- <circle cx="50" cy="50" r="15" fill="currentColor"/>
10341
- </svg>
10342
- )
10343
- }
10344
-
10345
- return (
10346
- <svg
10347
- viewBox="0 0 100 100"
10348
- fill="none"
10349
- xmlns="http://www.w3.org/2000/svg"
10350
- width={size}
10351
- height={size}
10352
- aria-label="Olon mark"
10353
- className={cn('flex-shrink-0', className)}
10354
- >
10355
- <defs>
10356
- <linearGradient id={gid} x1="0" y1="0" x2="0" y2="1">
10357
- <stop offset="0%" stopColor="var(--olon-ring-top)"/>
10358
- <stop offset="100%" stopColor="var(--olon-ring-bottom)"/>
10359
- </linearGradient>
10360
- </defs>
10361
- <circle cx="50" cy="50" r="38" stroke={`url(#${gid})`} strokeWidth="20"/>
10362
- <circle cx="50" cy="50" r="15" fill="var(--olon-nucleus)"/>
10363
- </svg>
10364
- )
10365
- }
10366
-
10367
- interface OlonLogoProps {
10368
- markSize?: number
10369
- fontSize?: number
10370
- variant?: 'default' | 'mono'
10371
- className?: string
10372
- }
10373
-
10374
- export function OlonLogo({
10375
- markSize = 32,
10376
- fontSize = 24,
10377
- variant = 'default',
10378
- className,
10379
- }: OlonLogoProps) {
10380
- return (
10381
- <div className={cn('flex items-center gap-3', className)}>
10382
- <OlonMark size={markSize} variant={variant}/>
10383
- <span
10384
- style={{
10385
- fontFamily: "'Instrument Sans', Helvetica, Arial, sans-serif",
10386
- fontWeight: 700,
10387
- fontSize,
10388
- letterSpacing: '-0.02em',
10389
- color: 'hsl(var(--foreground))',
10390
- lineHeight: 1,
10391
- }}
10392
- >
10393
- Olon
10394
- </span>
10395
- </div>
10396
- )
10397
- }
10457
+ import { cn } from '@/lib/utils'
10458
+
10459
+ interface OlonMarkProps {
10460
+ size?: number
10461
+ /** mono: uses currentColor — for single-colour print/emboss contexts */
10462
+ variant?: 'default' | 'mono'
10463
+ className?: string
10464
+ }
10465
+
10466
+ export function OlonMark({ size = 32, variant = 'default', className }: OlonMarkProps) {
10467
+ const gid = `olon-ring-${size}`
10468
+
10469
+ if (variant === 'mono') {
10470
+ return (
10471
+ <svg
10472
+ viewBox="0 0 100 100"
10473
+ fill="none"
10474
+ xmlns="http://www.w3.org/2000/svg"
10475
+ width={size}
10476
+ height={size}
10477
+ aria-label="Olon mark"
10478
+ className={cn('flex-shrink-0', className)}
10479
+ >
10480
+ <circle cx="50" cy="50" r="38" stroke="currentColor" strokeWidth="20"/>
10481
+ <circle cx="50" cy="50" r="15" fill="currentColor"/>
10482
+ </svg>
10483
+ )
10484
+ }
10485
+
10486
+ return (
10487
+ <svg
10488
+ viewBox="0 0 100 100"
10489
+ fill="none"
10490
+ xmlns="http://www.w3.org/2000/svg"
10491
+ width={size}
10492
+ height={size}
10493
+ aria-label="Olon mark"
10494
+ className={cn('flex-shrink-0', className)}
10495
+ >
10496
+ <defs>
10497
+ <linearGradient id={gid} x1="0" y1="0" x2="0" y2="1">
10498
+ <stop offset="0%" stopColor="var(--olon-ring-top)"/>
10499
+ <stop offset="100%" stopColor="var(--olon-ring-bottom)"/>
10500
+ </linearGradient>
10501
+ </defs>
10502
+ <circle cx="50" cy="50" r="38" stroke={`url(#${gid})`} strokeWidth="20"/>
10503
+ <circle cx="50" cy="50" r="15" fill="var(--olon-nucleus)"/>
10504
+ </svg>
10505
+ )
10506
+ }
10507
+
10508
+ interface OlonLogoProps {
10509
+ markSize?: number
10510
+ fontSize?: number
10511
+ variant?: 'default' | 'mono'
10512
+ className?: string
10513
+ }
10514
+
10515
+ export function OlonLogo({
10516
+ markSize = 32,
10517
+ fontSize = 24,
10518
+ variant = 'default',
10519
+ className,
10520
+ }: OlonLogoProps) {
10521
+ return (
10522
+ <div className={cn('flex items-center gap-3', className)}>
10523
+ <OlonMark size={markSize} variant={variant}/>
10524
+ <span
10525
+ style={{
10526
+ fontFamily: "'Instrument Sans', Helvetica, Arial, sans-serif",
10527
+ fontWeight: 700,
10528
+ fontSize,
10529
+ letterSpacing: '-0.02em',
10530
+ color: 'hsl(var(--foreground))',
10531
+ lineHeight: 1,
10532
+ }}
10533
+ >
10534
+ Olon
10535
+ </span>
10536
+ </div>
10537
+ )
10538
+ }
10398
10539
 
10399
10540
  END_OF_FILE_CONTENT
10400
10541
  echo "Creating src/components/ui/badge.tsx..."
@@ -11354,7 +11495,8 @@ cat << 'END_OF_FILE_CONTENT' > "src/data/config/theme.json"
11354
11495
  },
11355
11496
  "wordmark": {
11356
11497
  "fontFamily": "\"Instrument Sans\", Helvetica, Arial, sans-serif",
11357
- "weight": "700"
11498
+ "weight": "700",
11499
+ "tracking": "-0.05em"
11358
11500
  },
11359
11501
  "scale": {
11360
11502
  "xs": "0.75rem",
@@ -11466,14 +11608,14 @@ cat << 'END_OF_FILE_CONTENT' > "src/data/pages/home.json"
11466
11608
  "type": "olon-hero",
11467
11609
  "data": {
11468
11610
  "id": "hero-main",
11469
- "eyebrow": "CONTRACT LAYER · V1.5 · OPEN CORE",
11611
+ "eyebrow": "CONTRACT LAYER · V1.5 · OPEN SOURCE",
11470
11612
  "headline": "Contract Layer",
11471
11613
  "subline": "for the agentic web.",
11472
11614
  "body": "AI agents are becoming operational actors in commerce, marketing, and support. They need more than content — they need a contract. OlonJS is the deterministic machine contract for websites: every site typed, structured, and addressable by design. No custom glue. No fragile integrations. Just a contract any agent can read and operate.",
11473
11615
  "cta": {
11474
11616
  "primary": {
11475
11617
  "label": "Get started",
11476
- "href": "#Getstarted"
11618
+ "href": "#getstarted"
11477
11619
  },
11478
11620
  "secondary": {
11479
11621
  "label": "GitHub",
@@ -11666,178 +11808,203 @@ cat << 'END_OF_FILE_CONTENT' > "src/data/pages/home_.json"
11666
11808
  "id": "home-page",
11667
11809
  "slug": "home",
11668
11810
  "meta": {
11669
- "title": "OlonJS — The Contract Layer for the Agentic Web",
11670
- "description": "OlonJS standardizes machine-readable web content across tenants. Predictable page endpoints for agents, typed schema contracts, repeatable governance."
11811
+ "title": "OlonJS — Contract Layer for the Agentic Web",
11812
+ "description": "OlonJS is a TypeScript framework for building JSON-driven websites with a deterministic data contract. Every section is typed, validated, and structurally addressable."
11671
11813
  },
11672
11814
  "sections": [
11673
11815
  {
11674
11816
  "id": "hero-main",
11675
- "type": "hero",
11817
+ "type": "olon-hero",
11676
11818
  "data": {
11677
- "eyebrow": "Contract layer · v1.4 · Open Core",
11678
- "title": "Contract Layer",
11679
- "titleHighlight": "for the agentic web.",
11680
- "description": "OlonJS is a TypeScript framework for building JSON-driven websites with a deterministic data contract. Every section is typed, validated, and structurally addressable. Designed for reproducible tenant generation and machine-readable output.",
11681
- "ctas": [
11682
- {
11683
- "id": "cta-started",
11684
- "label": "Get started",
11685
- "href": "#contact",
11686
- "variant": "accent"
11819
+ "eyebrow": "AI GENERATED 🤖",
11820
+ "headline": "Contract Layer",
11821
+ "subline": "for the agentic web.",
11822
+ "body": "AI agents are becoming operational actors in commerce, marketing, and support. They need more than content — they need a contract. OlonJS is the deterministic machine contract for websites: every site typed, structured, and addressable by design. No custom glue. No fragile integrations. Just a contract any agent can read and operate.",
11823
+ "cta": {
11824
+ "primary": {
11825
+ "label": "Inizia Ora",
11826
+ "href": "/docs"
11687
11827
  },
11688
- {
11689
- "id": "cta-github",
11828
+ "secondary": {
11690
11829
  "label": "GitHub",
11691
- "href": "#",
11692
- "variant": "secondary"
11830
+ "href": "https://github.com"
11831
+ },
11832
+ "ghost": {
11833
+ "label": "Contatti",
11834
+ "href": "/contact"
11693
11835
  }
11694
- ],
11695
- "docsLabel": "Explore platform",
11696
- "docsHref": "/platform/overview",
11697
- "heroImage": {
11698
- "url": "data:image/svg+xml;base64,PHN2ZyB2ZXJzaW9uPSIxLjIiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgdmlld0JveD0iMCAwIDIwMCAyMDAiIHdpZHRoPSI1MTIiIGhlaWdodD0iNTEyIj4KICA8ZGVmcz4KICAgIDxsaW5lYXJHcmFkaWVudCBpZD0iZy1tYWluIiB4Mj0iMSIgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiIGdyYWRpZW50VHJhbnNmb3JtPSJtYXRyaXgoMTEwLjc5NSwxMTAuNzk1LC0xMTAuNzk1LDExMC43OTUsNDQuNjAzLDQ0LjYwMykiPgogICAgICA8c3RvcCBvZmZzZXQ9IjAiIHN0b3AtY29sb3I9IiM1QjhFRkYiLz4KICAgICAgPHN0b3Agb2Zmc2V0PSIwLjUiIHN0b3AtY29sb3I9IiMwRjUyRTAiLz4KICAgICAgPHN0b3Agb2Zmc2V0PSIxIiBzdG9wLWNvbG9yPSIjMDYzMDkwIi8+CiAgICA8L2xpbmVhckdyYWRpZW50PgogICAgPGxpbmVhckdyYWRpZW50IGlkPSJnLWFjY2VudCIgeDE9IjAiIHkxPSIwIiB4Mj0iMCIgeTI9IjEiPgogICAgICA8c3RvcCBvZmZzZXQ9IjAiIHN0b3AtY29sb3I9IiNBREM4RkYiLz4KICAgICAgPHN0b3Agb2Zmc2V0PSIxIiBzdG9wLWNvbG9yPSIjNUI4RUZGIi8+CiAgICA8L2xpbmVhckdyYWRpZW50PgogIDwvZGVmcz4KICA8IS0tIENoaXAgYm9yZGVyIC0tPgogIDxwYXRoIGZpbGw9Im5vbmUiIHN0cm9rZT0idXJsKCNnLW1haW4pIiBzdHJva2Utd2lkdGg9IjgiCiAgICBkPSJtNTkuMzggNDQuNmg4MS4yNGM4LjE2IDAgMTQuNzggNi42MiAxNC43OCAxNC43OHY4MS4yNGMwIDguMTYtNi42MiAxNC43OC0xNC43OCAxNC43OGgtODEuMjRjLTguMTYgMC0xNC43OC02LjYyLTE0Ljc4LTE0Ljc4di04MS4yNGMwLTguMTYgNi42Mi0xNC43OCAxNC43OC0xNC43OHoiLz4KICA8IS0tIExlZnQgcGlucyAtLT4KICA8cGF0aCBmaWxsPSJub25lIiBzdHJva2U9InVybCgjZy1tYWluKSIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2Utd2lkdGg9IjYiIGQ9Im03LjY3IDcyLjNoMzYuOTMiLz4KICA8cGF0aCBmaWxsPSJub25lIiBzdHJva2U9InVybCgjZy1tYWluKSIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2Utd2lkdGg9IjYiIGQ9Im03LjY3IDEwMGgzNi45MyIvPgogIDxwYXRoIGZpbGw9Im5vbmUiIHN0cm9rZT0idXJsKCNnLW1haW4pIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS13aWR0aD0iNiIgZD0ibTcuNjcgMTI3LjdoMzYuOTMiLz4KICA8IS0tIFJpZ2h0IHBpbnMgLS0+CiAgPHBhdGggZmlsbD0ibm9uZSIgc3Ryb2tlPSJ1cmwoI2ctbWFpbikiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLXdpZHRoPSI2IiBkPSJtMTU1LjQgNzIuM2gzNi45MyIvPgogIDxwYXRoIGZpbGw9Im5vbmUiIHN0cm9rZT0idXJsKCNnLW1haW4pIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS13aWR0aD0iNiIgZD0ibTE1NS40IDEwMGgzNi45MyIvPgogIDxwYXRoIGZpbGw9Im5vbmUiIHN0cm9rZT0idXJsKCNnLW1haW4pIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS13aWR0aD0iNiIgZD0ibTE1NS40IDEyNy43aDM2LjkzIi8+CiAgPCEtLSBUb3AgcGlucyAtLT4KICA8cGF0aCBmaWxsPSJub25lIiBzdHJva2U9InVybCgjZy1tYWluKSIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2Utd2lkdGg9IjYiIGQ9Im03Mi4zIDcuNjd2MzYuOTMiLz4KICA8cGF0aCBmaWxsPSJub25lIiBzdHJva2U9InVybCgjZy1tYWluKSIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2Utd2lkdGg9IjYiIGQ9Im0xMDAgNy42N3YzNi45MyIvPgogIDxwYXRoIGZpbGw9Im5vbmUiIHN0cm9rZT0idXJsKCNnLW1haW4pIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS13aWR0aD0iNiIgZD0ibTEyNy43IDcuNjd2MzYuOTMiLz4KICA8IS0tIEJvdHRvbSBwaW5zIC0tPgogIDxwYXRoIGZpbGw9Im5vbmUiIHN0cm9rZT0idXJsKCNnLW1haW4pIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS13aWR0aD0iNiIgZD0ibTcyLjMgMTU1LjR2MzYuOTMiLz4KICA8cGF0aCBmaWxsPSJub25lIiBzdHJva2U9InVybCgjZy1tYWluKSIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2Utd2lkdGg9IjYiIGQ9Im0xMDAgMTU1LjR2MzYuOTMiLz4KICA8cGF0aCBmaWxsPSJub25lIiBzdHJva2U9InVybCgjZy1tYWluKSIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2Utd2lkdGg9IjYiIGQ9Im0xMjcuNyAxNTUuNHYzNi45MyIvPgogIDwhLS0gQ29ybmVyIG5vZGVzIC0tPgogIDxjaXJjbGUgZmlsbD0idXJsKCNnLW1haW4pIiBjeD0iNzIuMyIgY3k9IjcyLjMiIHI9IjcuMzkiLz4KICA8Y2lyY2xlIGZpbGw9InVybCgjZy1tYWluKSIgY3g9IjEyNy43IiBjeT0iNzIuMyIgcj0iNy4zOSIvPgogIDxjaXJjbGUgZmlsbD0idXJsKCNnLW1haW4pIiBjeD0iNzIuMyIgY3k9IjEyNy43IiByPSI3LjM5Ii8+CiAgPGNpcmNsZSBmaWxsPSJ1cmwoI2ctbWFpbikiIGN4PSIxMjcuNyIgY3k9IjEyNy43IiByPSI3LjM5Ii8+CiAgPCEtLSBDZW50ZXIgbm9kZSDigJQgYWNjZW50IC0tPgogIDxjaXJjbGUgZmlsbD0idXJsKCNnLWFjY2VudCkiIGN4PSIxMDAiIGN5PSIxMDAiIHI9IjkuMjMiLz4KICA8IS0tIENvbm5lY3Rpb24gbGluZXMgLS0+CiAgPHBhdGggb3BhY2l0eT0iMC4zIiBmaWxsPSJub25lIiBzdHJva2U9IiM1QjhFRkYiIHN0cm9rZS13aWR0aD0iMiIgZD0ibTcyLjMgNzIuM2wyNy43IDI3LjciLz4KICA8cGF0aCBvcGFjaXR5PSIwLjMiIGZpbGw9Im5vbmUiIHN0cm9rZT0iIzVCOEVGRiIgc3Ryb2tlLXdpZHRoPSIyIiBkPSJtMTI3LjcgNzIuM2wtMjcuNyAyNy43Ii8+CiAgPHBhdGggb3BhY2l0eT0iMC4zIiBmaWxsPSJub25lIiBzdHJva2U9IiM1QjhFRkYiIHN0cm9rZS13aWR0aD0iMiIgZD0ibTcyLjMgMTI3LjdsMjcuNy0yNy43Ii8+CiAgPHBhdGggb3BhY2l0eT0iMC4zIiBmaWxsPSJub25lIiBzdHJva2U9IiM1QjhFRkYiIHN0cm9rZS13aWR0aD0iMiIgZD0ibTEyNy43IDEyNy43bC0yNy43LTI3LjciLz4KPC9zdmc+Cg==",
11699
- "alt": "icon-ai-specs.svg"
11700
11836
  }
11701
- },
11702
- "settings": {
11703
- "showCode": false
11704
11837
  }
11705
11838
  },
11706
11839
  {
11707
- "id": "features-section",
11708
- "type": "feature-grid",
11840
+ "id": "why-olon",
11841
+ "type": "olon-why",
11709
11842
  "data": {
11843
+ "id": "why-olon",
11710
11844
  "label": "Why OlonJS",
11711
- "sectionTitle": "A Meaningful Web",
11712
- "sectionTitleItalic": "Whole in itself, part of something greater.",
11713
- "sectionLead": "Most web frameworks separate concerns across layers — data, UI, validation, metadata — with no shared contract between them. OlonJS inverts this: the JSON data structure is the contract. Every layer — rendering, editing, validation, machine access — is a deterministic projection of the same typed source. The result is a site that is structurally coherent by construction, not by convention.",
11714
- "cards": [
11845
+ "headline": "A Meaningful Web",
11846
+ "subline": "Whole in itself, part of something greater.",
11847
+ "body": "Most web frameworks separate concerns across layers — data, UI, validation, metadata — with no shared contract between them. OlonJS inverts this: the JSON data structure is the contract. Every layer — rendering, editing, validation, machine access — is a deterministic projection of the same typed source. The result is a site that is structurally coherent by construction, not by convention. Every site built with OlonJS is a holon: complete in itself, intelligible to the network around it. The meaningful web doesn't happen all at once. It grows one site at a time.",
11848
+ "pillars": [
11715
11849
  {
11716
- "id": "card-endpoints",
11717
- "icon": {
11718
- "url": "/icons/features/icon-json-files.svg",
11719
- "alt": "JSON files icon"
11720
- },
11721
- "title": "Modular Type Registry",
11722
- "description": "Core exports an empty SectionDataRegistry. Tenants extend it via module augmentation — no Core changes required. Full TypeScript inference across all section types at compile-time."
11850
+ "id": "pillar-contract",
11851
+ "icon": "contract",
11852
+ "title": "The data is the contract",
11853
+ "body": "One typed JSON source. Every layer — UI, editor, agent, SEO — is a projection. No translation, no drift."
11723
11854
  },
11724
11855
  {
11725
- "id": "card-schema",
11726
- "icon": {
11727
- "url": "/icons/features/icon-zod-schemas.svg",
11728
- "alt": "Zod schemas icon"
11729
- },
11730
- "title": "Tenant Block Protocol",
11731
- "description": "Each section type lives in a self-contained capsule: View.tsx, schema.ts, types.ts, index.ts. The capsule is the unit of extension — renderable, validatable, and ingestible by the engine without additional configuration."
11856
+ "id": "pillar-holon",
11857
+ "icon": "holon",
11858
+ "title": "Every site is a holon",
11859
+ "body": "Autonomous, complete, structurally intelligible. Each site is whole in itself and part of a network that understands it."
11732
11860
  },
11733
11861
  {
11734
- "id": "card-ai",
11735
- "icon": {
11736
- "url": "/icons/features/icon-ai-specs.svg",
11737
- "alt": "AI specs icon"
11738
- },
11739
- "title": "Deterministic CLI",
11740
- "description": "@olonjs/cli scaffolds new tenants from a canonical script. Infrastructure, source structure, and dependencies are projected deterministically. Output is reproducible across environments"
11741
- },
11862
+ "id": "pillar-generated",
11863
+ "icon": "generated",
11864
+ "title": "Built to be generated",
11865
+ "body": "Every constraint is also an instruction. The spec is precise enough for AI agents to scaffold a fully compliant tenant from scratch."
11866
+ }
11867
+ ],
11868
+ "anchorId": "Why"
11869
+ }
11870
+ },
11871
+ {
11872
+ "id": "architecture-protocols",
11873
+ "type": "olon-architecture",
11874
+ "data": {
11875
+ "id": "architecture-protocols",
11876
+ "label": "Architecture",
11877
+ "headline": "Six governing protocols.",
11878
+ "body": "OlonJS is specified as a versioned set of architectural protocols. Each protocol is independently versioned and mandatory for compliant tenants.",
11879
+ "specHref": "https://github.com/olonjs/core/blob/main/specs/olonjsSpecs_V_1_5.md",
11880
+ "protocols": [
11742
11881
  {
11743
- "id": "card-multitenant",
11744
- "icon": {
11745
- "url": "/icons/features/icon-own-data.svg",
11746
- "alt": "Own data icon"
11747
- },
11748
- "title": "ICE Data Contract",
11749
- "description": "Mandatory data-jp-* DOM attributes bind rendered sections to their data contract. The binding is structural, not UI-dependent — any consumer that can traverse the DOM can identify and address any content node."
11882
+ "id": "proto-mtrp",
11883
+ "icon": "mtrp",
11884
+ "acronym": "MTRP",
11885
+ "version": "v1.2",
11886
+ "name": "Modular Type Registry",
11887
+ "desc": "Core exports an empty SectionDataRegistry. Tenants extend it via module augmentation. Full TypeScript inference across all section types at compile-time, zero Core changes.",
11888
+ "specHref": "https://github.com/olonjs/core/blob/main/specs/olonjsSpecs_V_1_5.md#1--modular-type-registry-pattern-mtrp-v12"
11750
11889
  },
11751
11890
  {
11752
- "id": "card-governance",
11753
- "icon": {
11754
- "url": "/icons/features/icon-governance.svg",
11755
- "alt": "Governance icon"
11756
- },
11757
- "title": "Base Schema Fragments",
11758
- "description": "BaseSectionData and BaseArrayItem are the required base types for all section data schemas and array items. They enforce anchor IDs and stable React keys across all capsules."
11891
+ "id": "proto-tbp",
11892
+ "icon": "tbp",
11893
+ "acronym": "TBP",
11894
+ "version": "v1.0",
11895
+ "name": "Tenant Block Protocol",
11896
+ "desc": "Each section type is a self-contained capsule: View.tsx, schema.ts, types.ts, index.ts. Renderable, validatable, and ingestible by the engine without additional configuration.",
11897
+ "specHref": "https://github.com/olonjs/core/blob/main/specs/olonjsSpecs_V_1_5.md#3--tenant-block-protocol-tbp-v10"
11759
11898
  },
11760
11899
  {
11761
- "id": "card-deploy",
11762
- "icon": {
11763
- "url": "/icons/features/icon-clean-commits.svg",
11764
- "alt": "Clean commits icon"
11765
- },
11766
- "title": "Path-Based Selection",
11767
- "description": "Content selection uses strict root-to-leaf path semantics. Each segment identifies a node unambiguously. The flat itemField/itemId protocol is removed in v1.3+."
11768
- }
11769
- ],
11770
- "proofStatement": "Working end-to-end with production routing parity.",
11771
- "proofSub": "Early customer usage across real tenant deployments · Clear hardening path to enterprise-grade governance.",
11772
- "tiers": [
11900
+ "id": "proto-jsp",
11901
+ "icon": "jsp",
11902
+ "acronym": "JSP",
11903
+ "version": "v1.8",
11904
+ "name": "JsonPages Site Protocol",
11905
+ "desc": "Deterministic file system ontology and CLI projection engine. config/ separates global governance from per-page content. Reproducible across every environment.",
11906
+ "specHref": "https://github.com/olonjs/core/blob/main/specs/olonjsSpecs_V_1_5.md#2--jsonpages-site-protocol-jsp-v18"
11907
+ },
11773
11908
  {
11774
- "id": "tier-oss",
11775
- "label": "OSS"
11909
+ "id": "proto-idac",
11910
+ "icon": "idac",
11911
+ "acronym": "IDAC",
11912
+ "version": "v1.0",
11913
+ "name": "ICE Data Contract",
11914
+ "desc": "Mandatory data-jp-* DOM attributes bind every section to its data. Any consumer that can traverse the DOM can identify and operate any content node — human or agent.",
11915
+ "specHref": "https://github.com/olonjs/core/blob/main/specs/olonjsSpecs_V_1_5.md"
11776
11916
  },
11777
11917
  {
11778
- "id": "tier-cloud",
11779
- "label": "Cloud"
11918
+ "id": "proto-bsds",
11919
+ "icon": "bsds",
11920
+ "acronym": "BSDS",
11921
+ "version": "v1.0",
11922
+ "name": "Base Schema Fragments",
11923
+ "desc": "BaseSectionData and BaseArrayItem enforce anchor IDs and stable React keys across all capsules. The foundation that doesn't move so your content never drifts.",
11924
+ "specHref": "https://github.com/olonjs/core/blob/main/specs/olonjsSpecs_V_1_5.md"
11780
11925
  },
11781
11926
  {
11782
- "id": "tier-enterprise",
11783
- "label": "Enterprise"
11927
+ "id": "proto-pss",
11928
+ "icon": "pss",
11929
+ "acronym": "PSS",
11930
+ "version": "v1.4",
11931
+ "name": "Path-Based Selection",
11932
+ "desc": "Every node has an address. Content selection uses strict root-to-leaf path semantics — unambiguous, stable, operable by any consumer that knows the contract.",
11933
+ "specHref": "https://github.com/olonjs/core/blob/main/specs/olonjsSpecs_V_1_5.md"
11784
11934
  }
11785
- ]
11786
- },
11787
- "settings": {
11788
- "columns": 3
11935
+ ],
11936
+ "anchorId": "Architecture"
11789
11937
  }
11790
11938
  },
11791
11939
  {
11792
- "id": "contact-section",
11793
- "type": "contact",
11940
+ "id": "example-steps",
11941
+ "type": "olon-example",
11794
11942
  "data": {
11795
- "label": "Contact",
11796
- "title": "Ready to define",
11797
- "titleHighlight": "your contract?",
11798
- "description": "Whether you're running a single tenant or deploying enterprise-grade governance across dozens of propertieslet's talk.",
11799
- "tiers": [
11800
- {
11801
- "id": "tier-oss",
11802
- "label": "OSS",
11803
- "desc": "Open source core — free forever",
11804
- "sub": "Adoption, trust, ecosystem growth"
11805
- },
11943
+ "id": "example-steps",
11944
+ "label": "Quick Example",
11945
+ "headline": "Two steps. One contract.",
11946
+ "body": "Scaffold a fully compliant tenant in under three minutes. Then read any page via the OlonJS protocol from a browser, a script, or an AI agent.",
11947
+ "note": "Every OlonJS tenant exposes a machine-readable manifest at",
11948
+ "noteHref": "http://localhost:5173/mcp-manifest.json",
11949
+ "steps": [
11806
11950
  {
11807
- "id": "tier-cloud",
11808
- "label": "Cloud",
11809
- "desc": "Vercel-native self-serve workflow",
11810
- "sub": "Fast for modern dev teams"
11951
+ "number": 1,
11952
+ "title": "Scaffold a tenant",
11953
+ "meta": "~3 min",
11954
+ "code": "# Install the CLI\nnpm install -g @olonjs/cli\n\n# Scaffold a new tenant\nnpx @olonjs/cli new tenant\n\n✓ Projecting infrastructure...\n✓ Projecting source (src_tenant_alpha.sh)\n✓ Resolving dependencies\n✓ Tenant scaffolded\n\nsrc/\n components/hero/\n View.tsx\n schema.ts\n types.ts\n index.ts\n data/config/\n site.json\n theme.json\n menu.json\n lib/\n schemas.ts\n base-schemas.ts"
11811
11955
  },
11812
11956
  {
11813
- "id": "tier-enterprise",
11814
- "label": "Enterprise",
11815
- "desc": "Private cloud + NX monorepo",
11816
- "sub": "Security, compliance, controlled deployment"
11957
+ "number": 2,
11958
+ "title": "Read via OlonJS protocol",
11959
+ "meta": "Any consumer",
11960
+ "code": "// Read any page via the contract\n// Works from browser, script, or AI agent\n\nconst page = await\n navigator.modelContextProtocol\n .readResource(\n 'olon://pages/home'\n );\n\n// Returns the full typed contract\n// { slug, meta, sections: Section[] }\n// No DOM scraping. No layout knowledge.\n// Just the contract.\n\n// {\n// \"slug\": \"home\",\n// \"sections\": [\n// { \"type\": \"hero\", \"data\": {...} },\n// { \"type\": \"features\", \"data\": {...} }\n// ]\n// }"
11817
11961
  }
11818
11962
  ],
11819
- "formTitle": "Get in touch",
11820
- "successTitle": "Message received",
11821
- "successBody": "We'll respond within one business day.",
11822
- "disclaimer": "No spam. Unsubscribe at any time."
11823
- },
11824
- "settings": {
11825
- "showTiers": true
11963
+ "anchorId": "Example"
11826
11964
  }
11827
11965
  },
11828
11966
  {
11829
- "id": "login-section",
11830
- "type": "login",
11967
+ "id": "getstarted-cards",
11968
+ "type": "olon-getstarted",
11831
11969
  "data": {
11832
- "title": "Start your journey",
11833
- "subtitle": "Enter your credentials to continue",
11834
- "forgotHref": "#",
11835
- "signupHref": "#contact",
11836
- "termsHref": "#",
11837
- "privacyHref": "#"
11838
- },
11839
- "settings": {
11840
- "showOauth": true
11970
+ "id": "getstarted-cards",
11971
+ "label": "Get Started",
11972
+ "headline": "Three paths in.",
11973
+ "body": "Start with the Core package, scaffold a full tenant with the CLI, or deploy a working example in one click.",
11974
+ "cards": [
11975
+ {
11976
+ "id": "card-core",
11977
+ "badge": "Open Core",
11978
+ "badgeStyle": "oss",
11979
+ "title": "Install Core",
11980
+ "body": "The Core package is free and open — forever. The contract, the protocols, the CLI. No lock-in on the foundation.",
11981
+ "code": "npm install @olonjs/core",
11982
+ "linkLabel": "View on GitHub",
11983
+ "linkHref": "https://github.com/olonjs/core"
11984
+ },
11985
+ {
11986
+ "id": "card-cli",
11987
+ "badge": "CLI",
11988
+ "badgeStyle": "cli",
11989
+ "title": "Scaffold a tenant",
11990
+ "body": "The CLI scaffolds a fully compliant tenant from a canonical script. Same result on every machine, every run.",
11991
+ "code": "npx @olonjs/cli new tenant",
11992
+ "linkLabel": "View on npm",
11993
+ "linkHref": "https://www.npmjs.com/package/@olonjs/cli"
11994
+ },
11995
+ {
11996
+ "id": "card-deploy",
11997
+ "badge": "Deploy",
11998
+ "badgeStyle": "deploy",
11999
+ "title": "Deploy a template",
12000
+ "body": "Clone a working OlonJS tenant and deploy it with one click. Explore the full capsule structure in a real project.",
12001
+ "deployLabel": "Deploy template →",
12002
+ "deployHref": "https://github.com/olonjs/core",
12003
+ "linkLabel": "View on npm",
12004
+ "linkHref": "https://www.npmjs.com/package/@olonjs/core"
12005
+ }
12006
+ ],
12007
+ "anchorId": "Getstarted"
11841
12008
  }
11842
12009
  }
11843
12010
  ]
@@ -14219,22 +14386,23 @@ export default defineConfig({
14219
14386
  }
14220
14387
 
14221
14388
  const schemaMatch = pathname.match(/^\/schemas\/(.+)\.schema\.json$/i);
14222
- if (!schemaMatch || req.method !== 'GET') return false;
14389
+ if (schemaMatch && req.method === 'GET') {
14390
+ const slug = normalizeManifestSlug(schemaMatch[1]);
14391
+ const pageConfig = buildState.pages[slug];
14392
+ if (!pageConfig) {
14393
+ sendJson(res, 404, { error: 'Schema contract not found' });
14394
+ return true;
14395
+ }
14223
14396
 
14224
- const slug = normalizeManifestSlug(schemaMatch[1]);
14225
- const pageConfig = buildState.pages[slug];
14226
- if (!pageConfig) {
14227
- sendJson(res, 404, { error: 'Schema contract not found' });
14397
+ sendJson(res, 200, buildPageContract({
14398
+ slug,
14399
+ pageConfig,
14400
+ schemas: buildState.schemas,
14401
+ siteConfig: buildState.siteConfig,
14402
+ }));
14228
14403
  return true;
14229
14404
  }
14230
-
14231
- sendJson(res, 200, buildPageContract({
14232
- slug,
14233
- pageConfig,
14234
- schemas: buildState.schemas,
14235
- siteConfig: buildState.siteConfig,
14236
- }));
14237
- return true;
14405
+ return false;
14238
14406
  };
14239
14407
 
14240
14408
  if (
@@ -14247,7 +14415,11 @@ export default defineConfig({
14247
14415
  ) {
14248
14416
  void handleManifestRequest()
14249
14417
  .then((handled) => {
14250
- if (!handled) next();
14418
+ if (!handled) {
14419
+ // Se handleManifestRequest fallisce a trovare qualcosa ma l'URL era giusto,
14420
+ // forziamo un 404 JSON per evitare che Vite serva l'index.html
14421
+ sendJson(res, 404, { error: 'Manifest or schema not found' });
14422
+ }
14251
14423
  })
14252
14424
  .catch((error) => {
14253
14425
  sendJson(res, 500, { error: error?.message || 'Manifest generation failed' });