Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,7 @@ export const BookPreviewFrame = forwardRef<BookPreviewFrameHandle, BookPreviewFr
<script src="https://cdn.tailwindcss.com"><\/script>
<style>
@import url("https://fonts.googleapis.com/css2?family=Merriweather:ital,wght@0,300..800;1,300..800&display=swap");
:root { --page-height: 100vh; }
body { margin: 0; }
body, p, h1, h2, h3, h4, h5, h6, span, div, button, input, textarea, select {
font-family: "Merriweather", serif;
Expand Down Expand Up @@ -244,6 +245,10 @@ ${interactiveScript}
doc.body.appendChild(scriptEl)
}

// Inject parent viewport height as CSS variable so overlay HTML can
// constrain images to fit without using vh (which doesn't work in iframes).
doc.documentElement.style.setProperty("--page-height", `${window.innerHeight}px`)

// Apply data-background-color from content to iframe body
if (applyBodyBackground !== false) {
const bgEl = doc.querySelector("[data-background-color]")
Expand Down Expand Up @@ -390,6 +395,10 @@ ${selectors}:hover {

// Re-measure on window resize (e.g. browser resize changes iframe width)
const onResize = () => {
const doc = iframe.contentDocument
if (doc) {
doc.documentElement.style.setProperty("--page-height", `${window.innerHeight}px`)
}
if (settledRef.current) measureHeight()
}
window.addEventListener("resize", onResize)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ function titleCase(slug: string): string {
/** Human-friendly display names for strategy keys */
const STRATEGY_DISPLAY_NAMES: Record<string, string> = {
llm: "AI Generated",
"llm-overlay": "AI Overlay",
dynamic: "Dynamic",
}

Expand All @@ -49,6 +50,7 @@ function strategyDisplayName(slug: string): string {
const RENDER_STRATEGY_DESCRIPTIONS: Record<string, string> = {
dynamic: "Automatically picks the best strategy per section type",
llm: "LLM generates HTML from section content",
"llm-overlay": "LLM positions text over background images",
two_column: "Fixed two-column template layout",
two_column_story: "Two-column template for story content",
}
Expand Down
7 changes: 7 additions & 0 deletions config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,13 @@ render_strategies:
model: openai:gpt-5.2
max_retries: 25
timeout: 180
llm-overlay:
render_type: llm
config:
prompt: web_generation_html_overlay
model: openai:gpt-5.2
max_retries: 25
timeout: 180
two_column:
render_type: template
config:
Expand Down
7 changes: 7 additions & 0 deletions config/presets/reference.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,13 @@ render_strategies:
model: openai:gpt-5.2
max_retries: 25
timeout: 180
llm-overlay:
render_type: llm
config:
prompt: web_generation_html_overlay
model: openai:gpt-5.2
max_retries: 25
timeout: 180

section_render_strategies: {}

Expand Down
7 changes: 7 additions & 0 deletions config/presets/storybook.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,13 @@ render_strategies:
model: openai:gpt-5.2
max_retries: 25
timeout: 180
llm-overlay:
render_type: llm
config:
prompt: web_generation_html_overlay
model: openai:gpt-5.2
max_retries: 25
timeout: 180

section_render_strategies: {}

Expand Down
8 changes: 8 additions & 0 deletions config/presets/textbook.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,14 @@ render_strategies:
max_retries: 25
timeout: 180
temperature: 0.3
llm-overlay:
render_type: llm
config:
prompt: web_generation_html_overlay
model: openai:gpt-5.2
max_retries: 25
timeout: 180
temperature: 0.3
two_column:
render_type: template
config:
Expand Down
48 changes: 48 additions & 0 deletions packages/pipeline/src/render-llm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,52 @@ export async function renderSectionLlm(
const isActivity = config.renderType === "activity"
const taskType = isActivity ? "activity-rendering" : "web-rendering"

// Build structured groups (preserves groupId/groupType for prompts that need it)
const groups: Array<{
group_id: string
group_type: string
texts: Array<{ text_id: string; text_type: string; text: string }>
}> = []
for (const part of input.parts) {
if (part.type === "group") {
groups.push({
group_id: part.groupId,
group_type: part.groupType,
texts: part.texts.map((t) => ({
text_id: t.textId,
text_type: t.textType,
text: t.text,
})),
})
}
}

// Build ordered parts list preserving document flow (text groups + images interleaved).
// This helps overlay prompts understand spatial relationships between content.
const orderedParts: Array<
| { part_type: "text_group"; group_id: string; group_type: string; texts: Array<{ text_id: string; text_type: string; text: string }> }
| { part_type: "image"; image_id: string }
> = []
for (const part of input.parts) {
if (part.type === "group") {
orderedParts.push({
part_type: "text_group",
group_id: part.groupId,
group_type: part.groupType,
texts: part.texts.map((t) => ({
text_id: t.textId,
text_type: t.textType,
text: t.text,
})),
})
} else {
orderedParts.push({
part_type: "image",
image_id: part.imageId,
})
}
}

const context = {
label: input.label,
page_image_base64: input.pageImageBase64,
Expand All @@ -41,6 +87,8 @@ export async function renderSectionLlm(
text_type: t.textType,
text: t.text,
})),
groups,
ordered_parts: orderedParts,
images: images.map((img) => ({
image_id: img.imageId,
image_base64: img.imageBase64,
Expand Down
Loading