(function () { const ASSET_BASE = window.EPIC_ASSET_BASE || "/assets/"; const API_BASE = window.EPIC_API_BASE || ""; const root = document.getElementById("diy-app"); const themeToToken = { classroom: "storybook", questbook: "atelier", comic: "comic", }; const themes = { classroom: { label: "Classroom" }, questbook: { label: "Questbook" }, comic: { label: "Comic" }, }; const icons = { back: '', wand: '', }; const params = new URLSearchParams(window.location.search); let state = { workflow_draft: { ordinary_goal: params.get("goal") || "Clean up my room before dinner", selected_theme_id: normalizedTheme(params.get("theme") || "questbook"), selected_generation_reference_ids: params.getAll("ref").length ? params.getAll("ref") : ["parent-photo-demo", "child-photo-demo", "custom-image-reference-demo"], }, preview: null, trace_json: {}, save_to_app_status: "coming_soon", }; function asset(path) { return `${ASSET_BASE}${path}`; } function escapeHtml(value) { return String(value) .replace(/&/g, "&") .replace(//g, ">") .replace(/"/g, """) .replace(/'/g, "'"); } function normalizedTheme(value) { return themes[value] ? value : "questbook"; } async function postJson(path, payload) { const response = await fetch(`${API_BASE}${path}`, { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify(payload), }); if (!response.ok) throw new Error(`Request failed: ${response.status}`); return response.json(); } async function updatePreview() { try { state = await postJson("/api/diy-preview", { ordinary_goal: state.workflow_draft.ordinary_goal, selected_theme_id: state.workflow_draft.selected_theme_id, selected_generation_reference_ids: state.workflow_draft.selected_generation_reference_ids, }); } catch (error) { state.trace_json = { error: "local_diy_preview_api_unavailable", message: String(error.message || error), save_to_app_status: "coming_soon", }; } render(); } function stepHtml(label, step) { return `
${escapeHtml(label)} ${escapeHtml(step.model || step.result_asset_ref || step.overlay_text || "Ready")} ${escapeHtml(step.runtime || step.output_size_px || step.fallback_used || "")}
`; } function nativeWorkflowHtml() { return `

DIY Lab

Open Full Workflow

The full Gradio workflow canvas is beta and opens separately; this surface is the V2 mobile mirror used for local UAT.

`; } function render() { if (!root) return; const draft = state.workflow_draft; const preview = state.preview; const trace = state.trace_json || {}; document.documentElement.dataset.theme = themeToToken[normalizedTheme(draft.selected_theme_id)]; root.innerHTML = `
${icons.back}Main App Isolated surface
${nativeWorkflowHtml()}

V2 Preview Inputs

${escapeHtml(themes[normalizedTheme(draft.selected_theme_id)].label)}
${Object.entries(themes) .map(([id, item]) => ` `) .join("")}
${draft.selected_generation_reference_ids.map((ref) => `${escapeHtml(ref)}`).join("")}

Pipeline

Quality
${stepHtml("Text / Quest JSON", trace.text_step || {})} ${stepHtml("Image Request / Result", trace.image_step || {})} ${stepHtml("Audio Request / Result", trace.audio_step || {})} ${stepHtml("Composed Card", trace.composed_card_step || {})}
${preview ? `
${escapeHtml(preview.generated_title)} DIY preview
${escapeHtml(preview.overlay_text.text)}

${escapeHtml(preview.generated_title)}

${escapeHtml(preview.generated_narration)}

${escapeHtml(preview.generated_reward_label)}
` : ""}

Trace

Model details

${escapeHtml(JSON.stringify(trace.quality_mode || {}, null, 2))}

${escapeHtml(JSON.stringify(trace, null, 2))}
`; } document.addEventListener("click", (event) => { const button = event.target.closest("[data-action]"); if (!button || button.disabled) return; if (button.dataset.action === "diy-theme") { state.workflow_draft.selected_theme_id = normalizedTheme(button.dataset.theme); updatePreview(); } if (button.dataset.action === "update-diy-preview") updatePreview(); }); document.addEventListener("input", (event) => { const field = event.target.closest("[data-field]"); if (!field) return; if (field.dataset.field === "diy-goal") state.workflow_draft.ordinary_goal = field.value; }); render(); updatePreview(); })();