@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.
|
|
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
|
-
|
|
6308
|
-
|
|
6309
|
-
|
|
6310
|
-
|
|
6311
|
-
|
|
6312
|
-
|
|
6313
|
-
|
|
6314
|
-
|
|
6315
|
-
|
|
6316
|
-
|
|
6317
|
-
|
|
6318
|
-
|
|
6319
|
-
|
|
6320
|
-
|
|
6321
|
-
|
|
6322
|
-
|
|
6323
|
-
|
|
6324
|
-
|
|
6325
|
-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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 {
|
|
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
|
-
|
|
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
|
-
<
|
|
7480
|
-
|
|
7481
|
-
|
|
7482
|
-
|
|
7483
|
-
|
|
7484
|
-
|
|
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-
|
|
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}>
|
|
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}>
|
|
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:
|
|
7708
|
+
{/* Right: branded product photo */}
|
|
7525
7709
|
<div className="hidden md:flex items-center justify-center">
|
|
7526
|
-
<
|
|
7527
|
-
|
|
7528
|
-
|
|
7529
|
-
|
|
7530
|
-
|
|
7531
|
-
|
|
7532
|
-
|
|
7533
|
-
|
|
7534
|
-
|
|
7535
|
-
|
|
7536
|
-
|
|
7537
|
-
|
|
7538
|
-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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
|
|
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": "#
|
|
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 —
|
|
11670
|
-
"description": "OlonJS
|
|
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": "
|
|
11678
|
-
"
|
|
11679
|
-
"
|
|
11680
|
-
"
|
|
11681
|
-
"
|
|
11682
|
-
{
|
|
11683
|
-
"
|
|
11684
|
-
"
|
|
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
|
-
|
|
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": "
|
|
11708
|
-
"type": "
|
|
11840
|
+
"id": "why-olon",
|
|
11841
|
+
"type": "olon-why",
|
|
11709
11842
|
"data": {
|
|
11843
|
+
"id": "why-olon",
|
|
11710
11844
|
"label": "Why OlonJS",
|
|
11711
|
-
"
|
|
11712
|
-
"
|
|
11713
|
-
"
|
|
11714
|
-
"
|
|
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": "
|
|
11717
|
-
"icon":
|
|
11718
|
-
|
|
11719
|
-
|
|
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": "
|
|
11726
|
-
"icon":
|
|
11727
|
-
|
|
11728
|
-
|
|
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": "
|
|
11735
|
-
"icon":
|
|
11736
|
-
|
|
11737
|
-
|
|
11738
|
-
|
|
11739
|
-
|
|
11740
|
-
|
|
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": "
|
|
11744
|
-
"icon":
|
|
11745
|
-
|
|
11746
|
-
|
|
11747
|
-
|
|
11748
|
-
"
|
|
11749
|
-
"
|
|
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": "
|
|
11753
|
-
"icon":
|
|
11754
|
-
|
|
11755
|
-
|
|
11756
|
-
|
|
11757
|
-
"
|
|
11758
|
-
"
|
|
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": "
|
|
11762
|
-
"icon":
|
|
11763
|
-
|
|
11764
|
-
|
|
11765
|
-
|
|
11766
|
-
"
|
|
11767
|
-
"
|
|
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": "
|
|
11775
|
-
"
|
|
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": "
|
|
11779
|
-
"
|
|
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": "
|
|
11783
|
-
"
|
|
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": "
|
|
11793
|
-
"type": "
|
|
11940
|
+
"id": "example-steps",
|
|
11941
|
+
"type": "olon-example",
|
|
11794
11942
|
"data": {
|
|
11795
|
-
"
|
|
11796
|
-
"
|
|
11797
|
-
"
|
|
11798
|
-
"
|
|
11799
|
-
"
|
|
11800
|
-
|
|
11801
|
-
|
|
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
|
-
"
|
|
11808
|
-
"
|
|
11809
|
-
"
|
|
11810
|
-
"
|
|
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
|
-
"
|
|
11814
|
-
"
|
|
11815
|
-
"
|
|
11816
|
-
"
|
|
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
|
-
"
|
|
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": "
|
|
11830
|
-
"type": "
|
|
11967
|
+
"id": "getstarted-cards",
|
|
11968
|
+
"type": "olon-getstarted",
|
|
11831
11969
|
"data": {
|
|
11832
|
-
"
|
|
11833
|
-
"
|
|
11834
|
-
"
|
|
11835
|
-
"
|
|
11836
|
-
"
|
|
11837
|
-
|
|
11838
|
-
|
|
11839
|
-
|
|
11840
|
-
|
|
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 (
|
|
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
|
-
|
|
14225
|
-
|
|
14226
|
-
|
|
14227
|
-
|
|
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)
|
|
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' });
|