feat: page interactive, démos Play/Step et simulateur Carnot
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>
This commit is contained in:
@@ -0,0 +1,48 @@
|
||||
'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>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user