Ajoute le pipeline PageSpec (validation, rendu, publication /p/{slug}),
les démos TipTap /demo, et le simulateur Carnot (modes frigo/PAC/moteur,
énergie kJ vs puissance W, unités K/°C/°F) avec correctifs d’équations KaTeX.
Co-authored-by: Cursor <cursoragent@cursor.com>
49 lines
1.4 KiB
TypeScript
49 lines
1.4 KiB
TypeScript
'use client'
|
|
|
|
import { PageView } from '@/components/interactive-page/page-view'
|
|
import { validateInteractivePage, type PageSpecV1 } from '@/lib/interactive-page'
|
|
import { AlertCircle } from 'lucide-react'
|
|
|
|
/**
|
|
* Public / preview shell for published interactive pages.
|
|
* Parses stored PageSpecV1 JSON from `publishedContent`.
|
|
*/
|
|
export function InteractivePublishedPage({
|
|
publishedContent,
|
|
isStale,
|
|
}: {
|
|
publishedContent: string
|
|
isStale?: boolean
|
|
}) {
|
|
let page: PageSpecV1 | null = null
|
|
let error: string | null = null
|
|
try {
|
|
const raw = JSON.parse(publishedContent)
|
|
const result = validateInteractivePage(raw)
|
|
if (result.ok) page = result.page
|
|
else error = result.issues[0]?.message || 'PageSpec invalide'
|
|
} catch {
|
|
error = 'JSON de page interactive illisible'
|
|
}
|
|
|
|
if (!page) {
|
|
return (
|
|
<div className="mx-auto flex max-w-lg gap-3 p-10 text-sm text-destructive">
|
|
<AlertCircle className="h-5 w-5 shrink-0" />
|
|
<p>{error || 'Page interactive indisponible'}</p>
|
|
</div>
|
|
)
|
|
}
|
|
|
|
return (
|
|
<div>
|
|
{isStale ? (
|
|
<div className="border-b border-amber-500/30 bg-amber-500/10 px-4 py-2 text-center text-xs text-amber-800 dark:text-amber-200">
|
|
Le contenu source a évolué — cette page interactive est à régénérer.
|
|
</div>
|
|
) : null}
|
|
<PageView page={page} demoMode="interactive" />
|
|
</div>
|
|
)
|
|
}
|