Files
Momento/memento-note/lib/ai/services/interactive-demo-client.service.ts
Antigravity 69c99e4f4f 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>
2026-07-24 17:51:43 +00:00

75 lines
1.8 KiB
TypeScript

import type { InteractiveDemoV1, ValidationIssue } from '@/lib/interactive-demo'
export type GenerateInteractiveDemoResponse =
| { ok: true; demo: InteractiveDemoV1; attempts: number }
| {
ok: false
error: string
issues?: ValidationIssue[]
quotaExceeded?: boolean
status?: number
}
export async function generateInteractiveDemo(params: {
content: string
selection?: string | null
lang?: string
noteId?: string
}): Promise<GenerateInteractiveDemoResponse> {
const controller = new AbortController()
const timeout = setTimeout(() => controller.abort(), 170_000)
let res: Response
try {
res = await fetch('/api/ai/interactive-demo', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
content: params.content,
selection: params.selection,
lang: params.lang,
noteId: params.noteId,
}),
signal: controller.signal,
})
} catch (err) {
clearTimeout(timeout)
const aborted = err instanceof Error && err.name === 'AbortError'
return {
ok: false,
error: aborted
? 'Génération trop longue — réessaie (ou raccourcis la note)'
: err instanceof Error
? err.message
: 'Network error',
}
} finally {
clearTimeout(timeout)
}
const data = await res.json().catch(() => ({}))
if (res.status === 402) {
return {
ok: false,
error: data.error || 'Quota exceeded',
quotaExceeded: true,
status: 402,
}
}
if (!res.ok) {
return {
ok: false,
error: data.error || `HTTP ${res.status}`,
issues: data.issues,
status: res.status,
}
}
return {
ok: true,
demo: data.demo,
attempts: data.attempts ?? 1,
}
}