diff --git a/apps/api/src/app.ts b/apps/api/src/app.ts index 61a2dcd..98bc591 100644 --- a/apps/api/src/app.ts +++ b/apps/api/src/app.ts @@ -84,7 +84,7 @@ app.route("/api", createTextCatalogRoutes(booksDir)) app.route("/api", createTTSRoutes(booksDir)) app.route("/api", createStepRoutes(stepService, pipelineService, booksDir, promptsDir, configPath)) app.route("/api", createPresetRoutes(configPath)) -app.route("/api", createAdtPreviewRoutes(booksDir, webAssetsDir)) +app.route("/api", createAdtPreviewRoutes(booksDir, webAssetsDir, configPath)) app.route("/api", createSpeechConfigRoutes(configPath)) export default app diff --git a/apps/api/src/routes/adt-preview.ts b/apps/api/src/routes/adt-preview.ts index e5ea145..30f1777 100644 --- a/apps/api/src/routes/adt-preview.ts +++ b/apps/api/src/routes/adt-preview.ts @@ -24,6 +24,7 @@ import { buildQuizAnswers, buildTextCatalog, pad3, + loadBookConfig, } from "@adt/pipeline" // --------------------------------------------------------------------------- @@ -212,6 +213,7 @@ function buildPreviewConfig(storage: Storage, language: string) { export function createAdtPreviewRoutes( booksDir: string, webAssetsDir: string, + configPath?: string, ): Hono { const app = new Hono() @@ -441,6 +443,10 @@ export function createAdtPreviewRoutes( if (!filename.endsWith(".html")) throw new HTTPException(404, { message: "Not found" }) const pageId = filename.replace(/\.html$/, "") + const safeLabel = parseBookLabel(label) + const config = loadBookConfig(safeLabel, booksDir, configPath) + const applyBodyBackground = config.apply_body_background + return withStorage(label, (storage) => { const title = getBookTitle(storage) const language = getBookLanguage(storage) @@ -471,6 +477,7 @@ export function createAdtPreviewRoutes( hasMath: false, bundleVersion: "1", skipContentWrapper: true, + applyBodyBackground, }) c.header("Content-Type", "text/html; charset=utf-8") @@ -503,6 +510,7 @@ export function createAdtPreviewRoutes( activityAnswers, hasMath: false, bundleVersion: "1", + applyBodyBackground, }) c.header("Content-Type", "text/html; charset=utf-8") diff --git a/apps/api/src/routes/package.ts b/apps/api/src/routes/package.ts index 336725d..37d69ec 100644 --- a/apps/api/src/routes/package.ts +++ b/apps/api/src/routes/package.ts @@ -79,6 +79,7 @@ export function createPackageRoutes( outputLanguages, title, webAssetsDir, + applyBodyBackground: config.apply_body_background, }) return c.json({ status: "completed", label: safeLabel }) diff --git a/apps/api/src/services/export-service.ts b/apps/api/src/services/export-service.ts index 4d34ceb..0e406b8 100644 --- a/apps/api/src/services/export-service.ts +++ b/apps/api/src/services/export-service.ts @@ -59,6 +59,7 @@ export async function exportBook( outputLanguages, title, webAssetsDir, + applyBodyBackground: config.apply_body_background, }) } diff --git a/apps/studio/src/components/pipeline/stages/BookPreviewFrame.tsx b/apps/studio/src/components/pipeline/stages/BookPreviewFrame.tsx index b8a2b73..d50c2f1 100644 --- a/apps/studio/src/components/pipeline/stages/BookPreviewFrame.tsx +++ b/apps/studio/src/components/pipeline/stages/BookPreviewFrame.tsx @@ -19,6 +19,8 @@ export interface BookPreviewFrameProps { onSelectElement?: (dataId: string, rect: DOMRect) => void /** Called when a text element is edited (blur/Enter after contenteditable) */ onTextChanged?: (dataId: string, newText: string, fullHtml: string) => void + /** When true (default), applies data-background-color to the iframe body */ + applyBodyBackground?: boolean } /** @@ -41,6 +43,7 @@ export const BookPreviewFrame = forwardRef(null) @@ -241,6 +244,14 @@ ${interactiveScript} doc.body.appendChild(scriptEl) } + // Apply data-background-color from content to iframe body + if (applyBodyBackground !== false) { + const bgEl = doc.querySelector("[data-background-color]") + doc.body.style.backgroundColor = bgEl?.getAttribute("data-background-color") ?? "" + } else { + doc.body.style.backgroundColor = "" + } + // Measure multiple times to catch late reflows from Tailwind CDN, fonts, and images. // Wait one frame so the browser queues font loads for the new content, // then wait for fonts.ready so we measure the final layout. @@ -275,7 +286,7 @@ ${interactiveScript} // When html prop changes, update the body directly (no iframe reload) useEffect(() => { if (readyRef.current) injectAndMeasure(sanitizedHtml) - }, [sanitizedHtml]) + }, [sanitizedHtml, applyBodyBackground]) // Inject/update pruned element styles into the iframe useEffect(() => { diff --git a/apps/studio/src/components/pipeline/stages/StoryboardSectionDetail.tsx b/apps/studio/src/components/pipeline/stages/StoryboardSectionDetail.tsx index 3e52e5d..0e387e3 100644 --- a/apps/studio/src/components/pipeline/stages/StoryboardSectionDetail.tsx +++ b/apps/studio/src/components/pipeline/stages/StoryboardSectionDetail.tsx @@ -5,6 +5,7 @@ import { useQuery, useQueryClient } from "@tanstack/react-query" import { api } from "@/api/client" import type { PageDetail, VersionEntry } from "@/api/client" import { useApiKey } from "@/hooks/use-api-key" +import { useActiveConfig } from "@/hooks/use-debug" import { useStepHeader } from "../StepViewRouter" import { BookPreviewFrame, type BookPreviewFrameHandle } from "./BookPreviewFrame" import { SectionEditToolbar } from "./SectionEditToolbar" @@ -314,6 +315,8 @@ export function StoryboardSectionDetail({ const queryClient = useQueryClient() const { apiKey, hasApiKey } = useApiKey() const { headerSlotEl } = useStepHeader() + const { data: activeConfigData } = useActiveConfig(bookLabel) + const applyBodyBackground = (activeConfigData?.merged as Record | undefined)?.apply_body_background !== false const [saving, setSaving] = useState(false) const [rerendering, setRerendering] = useState(false) @@ -1265,6 +1268,7 @@ export function StoryboardSectionDetail({ changedElements={changedElements} onSelectElement={handleSelectElement} onTextChanged={handleTextChanged} + applyBodyBackground={applyBodyBackground} /> ) : (
diff --git a/apps/studio/src/components/pipeline/stages/StoryboardSettings.tsx b/apps/studio/src/components/pipeline/stages/StoryboardSettings.tsx index 15466e6..3c447af 100644 --- a/apps/studio/src/components/pipeline/stages/StoryboardSettings.tsx +++ b/apps/studio/src/components/pipeline/stages/StoryboardSettings.tsx @@ -81,6 +81,7 @@ export function StoryboardSettings({ bookLabel, headerTarget, tab = "general" }: const [renderingTemplateName, setRenderingTemplateName] = useState("") const [renderingTemperature, setRenderingTemperature] = useState("") const [styleguide, setStyleguide] = useState("") + const [applyBodyBackground, setApplyBodyBackground] = useState(true) const [sectioningPromptDraft, setSectioningPromptDraft] = useState(null) const [renderingPromptDraft, setRenderingPromptDraft] = useState(null) const [renderingTemplateDraft, setRenderingTemplateDraft] = useState(null) @@ -174,6 +175,8 @@ export function StoryboardSettings({ bookLabel, headerTarget, tab = "general" }: } // Styleguide setStyleguide(typeof merged.styleguide === "string" ? merged.styleguide : "") + // Body background + setApplyBodyBackground(merged.apply_body_background !== false) // Rendering config comes from the default render strategy if (merged.render_strategies && merged.default_render_strategy) { const strategies = merged.render_strategies as Record @@ -295,6 +298,9 @@ export function StoryboardSettings({ bookLabel, headerTarget, tab = "general" }: if (shouldWrite("styleguide")) { overrides.styleguide = styleguide || undefined } + if (shouldWrite("apply_body_background")) { + overrides.apply_body_background = applyBodyBackground + } // Write rendering temperature into the default render strategy config if (shouldWrite("rendering_temperature") && defaultRenderStrategy) { const existingStrategies = (overrides.render_strategies ?? merged?.render_strategies ?? {}) as Record> @@ -681,6 +687,25 @@ export function StoryboardSettings({ bookLabel, headerTarget, tab = "general" }: Lower values produce more consistent styling across pages.

+ +
+

+ Display +

+
+ { setApplyBodyBackground(v); markDirty("apply_body_background") }} + /> + +
+

+ When enabled, background colors from the styleguide are applied to the full page body. +

+
{/* Prompt editor */} diff --git a/apps/studio/src/routes/books.new.tsx b/apps/studio/src/routes/books.new.tsx index 80891ee..8450150 100644 --- a/apps/studio/src/routes/books.new.tsx +++ b/apps/studio/src/routes/books.new.tsx @@ -178,6 +178,7 @@ function AddBookPage() { const [textGroupTypes, setTextGroupTypes] = useState>({}) const [sectionTypes, setSectionTypes] = useState>({}) const [sectioningMode, setSectioningMode] = useState("section") + const [applyBodyBackground, setApplyBodyBackground] = useState(true) // Styleguide preview const [styleguidePreviewOpen, setStyleguidePreviewOpen] = useState(false) @@ -314,6 +315,9 @@ function AddBookPage() { // Styleguide from preset setStyleguide(typeof config.styleguide === "string" ? config.styleguide : "") + // Body background from preset + setApplyBodyBackground(config.apply_body_background !== false) + // Custom layout auto-expands advanced panel if (layoutType === "custom") { setShowAdvancedLayout(true) @@ -412,6 +416,7 @@ function AddBookPage() { configOverrides.output_languages = Array.from(outputLanguages) } configOverrides.spread_mode = spreadMode + configOverrides.apply_body_background = applyBodyBackground if (parsedStartPage !== undefined) { configOverrides.start_page = parsedStartPage } @@ -756,6 +761,21 @@ function AddBookPage() { Merge facing pages as spreads (cover + page pairs).

+
+
+ + +
+

+ When enabled, background colors from the styleguide are applied to the full page body. +

+
+ + + + + Style Guide Preview — Sri Lanka Grade 2 + + + + + +
+ + +
+

Style Guide Preview

+

Sri Lanka Grade 2 — Child-Friendly Theme

+
+ + + + +

Color Palette (from original book)

+ +
+

Colors extracted from Sri Lankan Grade 2 Sinhala textbook

+
+
+

Warm Peach

+

#F2D4BC

+
+
+

Cream

+

#FFF5EB

+
+
+

Sky Blue

+

#5BACDB

+
+
+

Light Blue

+

#B8E0F7

+
+
+

Nature Green

+

#3D8B37

+
+
+

Warm Maroon

+

#8B2252

+
+
+

Playful Pink

+

#F4A7B9

+
+
+

Soft Purple

+

#7B68AE

+
+
+

Sunny Orange

+

#F5A623

+
+
+

Golden Yellow

+

#F5C542

+
+
+

Dark Brown

+

#3D2B1F

+
+
+

Leaf Green

+

#5AA84A

+
+
+
+ +
+ + + + +

Text Styles (Large for Young Readers)

+ +
+

book_title — h1

+

The Great Journey Begins

+
+ +
+

book_subtitle — h2

+

A story of discovery and wonder

+
+ +
+

chapter_title — h1

+

Chapter Title Example

+
+ +
+

section_heading — h1

+

Section Heading Example

+
+ +
+

activity_title — h2

+

Activity Title Example

+
+ +
+

section_text — p (DOUBLED size for young readers)

+

This is section text. It uses a large, comfortable reading size perfect for Grade 2 children. The text is roughly double the default size to ensure easy readability.

+
+ +
+

instruction_text — p

+

Look at the following pictures and answer the questions below.

+
+ +
+

standalone_text — p

+

This is standalone text used for general-purpose content outside of cards or specific sections.

+
+ +
+

image_associated_text — p

+

Caption: Children playing under a large tree by the river.

+
+ +
+ + + + +

Components

+ + +
+

Chapter Badge (colorful, child-friendly) — badge color varies per chapter

+
+
+
+

LESSON

+

1

+
+
+

The Music Class

+
+
+
+ +
+
+
+

LESSON

+

4

+
+
+

The Swing Song

+
+
+
+ +
+
+
+

LESSON

+

11

+
+
+

The Star Poem

+
+
+
+
+ + +
+

Content Card (frosted glass, no borders)

+
+

The children went to the river bank. Today they were going to have a music class under the big tree.

+

"I will be the teacher," said little sister Mali.

+
+
+ + +
+

Text Group (paragraphs without card)

+
+

First paragraph in a text group. Large, clear text for young readers.

+

Second paragraph. Notice the generous size — perfect for children just learning to read.

+
+
+ +
+ + + + +

Image Styles

+ +
+

Single Image — rounded, soft shadow, no border

+
+ Single Image Placeholder +
+
+ +
+

Image Grid (2 columns) — no borders

+
+
+ Image 1 +
+
+ Image 2 +
+
+ Image 3 +
+
+ Image 4 +
+
+
+ +
+

Text and Image Side by Side (PRIMARY layout)

+
+
+

This layout places text and an image side by side. It maximizes visible content for young readers.

+

Notice the large text size — roughly double the default theme.

+
+
+
+ Side Image +
+
+
+
+ +
+ + + + +

Decorations (Stars Only)

+ +
+

Star Decorations — for poems, songs & activities only

+
+
+
+
+
+
+
+

Scattered star decorations using palette colors

+
+
+ +
+ + + + +

Page Templates

+ + +
+

Template: Chapter/Lesson Start Page (warm gradient)

+
+
+
+
+ +
+
+
+

LESSON

+

1

+
+
+

The Music Class

+
+
+
+ +
+
+
+

The children went to the river bank to have a music class.

+

"What are we doing today?" asked the teacher. "Let's have a music class," said Pavani.

+
+
+
+
+ Chapter Image +
+
+
+
+
+
+
+
+ + +
+

Template: Regular Content Page (cool gradient, alternating layout)

+
+
+
+
+ +
+
+
+ Content Image 1 +
+
+
+
+

The clouds smiled down at the children as they played in the garden.

+

The banana tree swayed gently in the warm breeze.

+
+
+
+ +
+
+

The children carried water for the plants. They loved helping in the garden every day.

+
+
+
+ Content Image 2 +
+
+
+
+
+
+
+
+ + +
+

Template: Poem / Song Page (with star decorations)

+
+
+
+
+
+ +
+
+
+
+
+ +

The Swing Song

+ +
+
+

The big cashew tree stands tall

+

By the village path we know

+

A swing hangs from its branches wide

+

Swinging gently to and fro

+
+
+

The wind sings through the leaves above

+

As fruits grow ripe and round

+

Come swing with us, come swing with us

+

The sweetest swing we found

+
+
+
+
+
+
+
+
+ + +
+

Template: Table of Contents

+
+
+
+
+
+ +
+

Sinhala Reader

+

Table of Contents

+
+ +
+
+

Lesson 1

+

The Music Class

+ +

1

+
+
+

Lesson 4

+

The Swing Song

+ +

10

+
+
+

Lesson 7

+

Clouds and Rain

+ +

17

+
+
+

Lesson 11

+

The Star Poem

+ +

34

+
+
+
+
+
+
+
+
+ +
+ + + + +

Required Structure Reference

+ +
+

Outer Container — full-bleed background (max-w-none overrides container breakpoints)

+
+
<div class="container content mx-auto flex min-h-screen w-full
+     max-w-none items-start justify-center px-4 py-6"
+    data-background-color="BACKGROUND_COLOR" id="content"
+    style="background: LIGHT_GRADIENT;">
+  <section class="w-full"
+      data-section-id="SECTION_ID"
+      data-section-type="SECTION_TYPE"
+      data-text-color="#3D2B1F"
+      id="simple-main" role="article">
+    <div class="mx-auto w-full max-w-6xl space-y-4">
+      <!-- Page content here -->
+    </div>
+  </section>
+</div>
+
+
+ + +
+ Generated from assets/styleguides/sri-lanka-grade2.md +
+ +
+ + diff --git a/assets/styleguides/sri-lanka-grade2.md b/assets/styleguides/sri-lanka-grade2.md new file mode 100644 index 0000000..2f375be --- /dev/null +++ b/assets/styleguides/sri-lanka-grade2.md @@ -0,0 +1,427 @@ +# Styleguide — Sri Lanka Grade 2 (Child-Friendly) + +This styleguide is designed for young children (Grade 2, ages ~7). Use large, readable text and soft, light backgrounds inspired by the original Sri Lankan textbook. Keep the visual design clean and uncluttered. Maximize content visibility by placing text and images side by side wherever possible. + +## Color Palette (extracted from original book) + +These colors are drawn from the original textbook illustrations. Use them sparingly — primarily for headings, badges, and star decorations. Backgrounds should be very light washes or gentle gradients of these tones. + +| Role | Color | Usage | +|------|-------|-------| +| Warm Peach | #F2D4BC | Light background washes | +| Cream | #FFF5EB | Primary page background | +| Sky Blue | #5BACDB | Headings, badges | +| Light Blue | #B8E0F7 | Light background washes | +| Nature Green | #3D8B37 | Headings, badges | +| Leaf Green | #5AA84A | Accent headings | +| Warm Maroon | #8B2252 | Headings, chapter titles | +| Dark Brown | #3D2B1F | Body text color | +| Playful Pink | #F4A7B9 | Star decorations | +| Soft Purple | #7B68AE | Badges, star decorations | +| Sunny Orange | #F5A623 | Badges, star decorations | +| Golden Yellow | #F5C542 | Star decorations | + +## Required Container Structure + +Every page MUST use this exact outer container. The page background color is controlled ONLY by the `data-background-color` attribute — the system applies it to the `` element automatically. + +**CRITICAL — NO backgrounds on containers**: Do NOT add any `style="background: ..."`, `style="background-color: ..."`, `bg-*` classes, or gradient styles to the outer container, section, or any inner divs. The ONLY background for the page comes from `data-background-color` on the outer container. The system reads this attribute and applies it to the body. + +```html +
+
+ +
+
+``` + +**Background color rules:** +- Set `data-background-color` to a soft, pale color from the palette — this fills the ENTIRE page body +- Use very light, washed-out tones: `#FFF5EB` (cream), `#FFF0E6` (warm peach), `#F0F7FD` (pale blue), `#F5F0FA` (pale purple), `#F0F8EF` (pale green) +- Alternate between warm tones and cool tones across pages for variety +- NEVER use saturated or bright colors — always keep them pale and airy +- NEVER add any background styles to any HTML element — only use `data-background-color` + +**Layout notes**: Use `items-start` (not `items-center`) and `py-6` (not `py-12`) so content starts near the top and more fits in a single view. Content may extend beyond the viewport — that is fine, `min-h-screen` is a minimum not a maximum. + +## Inner Container (REQUIRED for all content pages) + +Inside the section, ALWAYS use this inner container structure: + +```html +
+ +
+``` + +Use `max-w-6xl` (wider than default) and `space-y-4` (tighter than default) to fit more content on screen. + +--- + +## Decorations + +Keep decorations minimal and tasteful. The only decorative element encouraged is **star decorations** for poem/song/activity pages. + +### Star Decorations (inspired by the original book) + +For poem or activity pages, scatter decorative stars using palette colors: + +```html +
+
+
+``` + +**Decoration rules:** +- Stars ONLY on poem, song, and activity pages — not on regular content pages +- No colored borders on cards or content boxes +- No colored borders on images +- No dot dividers or section separators +- Rounded corners on cards and images are fine (`rounded-3xl`) +- Soft shadows are fine (`shadow-sm`, `shadow-md`) + +--- + +## Text Styles + +All text is LARGE for young readers. Paragraph text is roughly double the default size. + +| text_type | Element | Classes | +|-----------|---------|---------| +| book_title | h1 | text-5xl md:text-6xl font-extrabold leading-tight | +| book_subtitle | h2 | text-3xl md:text-4xl font-semibold | +| chapter_title | h1 | text-4xl md:text-5xl font-bold leading-tight | +| section_heading | h1 | text-4xl md:text-5xl font-bold leading-tight | +| activity_title | h2 | text-3xl md:text-4xl font-bold leading-tight | +| section_text | p | text-2xl md:text-3xl leading-relaxed | +| instruction_text | p | text-xl md:text-2xl leading-relaxed italic | +| standalone_text | p | text-xl md:text-2xl leading-relaxed | +| image_associated_text | p | text-lg md:text-xl italic mt-2 | + +**Note:** These sizes are intentionally large — this book is for 7-year-old children who need big, clear text. + +## Image Styles + +Images should be prominent and cleanly presented. No borders on images. + +| Type | Classes | +|------|---------| +| Single image | w-full rounded-3xl shadow-md | +| Multiple images | rounded-2xl shadow-sm | +| Image grid container | grid grid-cols-2 gap-3 | + +```html + +``` + +--- + +## Layout Strategy: Maximize Visible Content + +**CRITICAL**: Lay out content so as much as possible is visible in a single view. Prefer side-by-side layouts over stacked layouts. + +### Preferred: Text and Image Side by Side + +This is the PRIMARY layout. Use it whenever a page has both text and an image. Alternate which side the image appears on (left vs right) across pages. + +**Image on RIGHT, text on LEFT:** + +```html +
+
+
+

Heading

+

Paragraph text here.

+

More paragraph text.

+
+
+ +
+
+
+``` + +**Image on LEFT, text on RIGHT:** + +```html +
+
+
+ +
+
+

Heading

+

Paragraph text here.

+
+
+
+``` + +### Extended Content (scrolling is OK) + +If a page has lots of content, it is perfectly fine to extend beyond the viewport height. Use the side-by-side layout first, then stack additional content below: + +```html +
+ +
+
+ +
+
+ +
+
+ +
+

More text continues below...

+
+
+``` + +--- + +## Components + +### Chapter Badge (colorful, child-friendly) + +When there is a chapter or lesson number, use this EXACT structure with vibrant colors: + +```html +
+
+
+

LESSON

+

1

+
+
+

Lesson Title Here

+
+
+
+``` + +Vary the badge background color across chapters: #3D8B37, #5BACDB, #8B2252, #F5A623, #7B68AE. + +### Content Card (for body text) + +Wrap body paragraphs in a clean card with a soft frosted background. No colored borders. + +```html +
+

Paragraph text

+

More text

+
+``` + +### Text Group (for paragraphs without card) + +```html +
+

Paragraph text

+
+``` + +--- + +## Page Templates + +### Template: Chapter/Lesson Start Page + +Use when page has a chapter/lesson number. Use side-by-side layout with the image: + +```html +
+
+
+ +
+
+
+

LESSON

+

1

+
+
+

The Music Class

+
+
+
+ +
+
+
+

Paragraph one.

+

Paragraph two.

+
+
+
+ +
+
+
+
+
+``` + +### Template: Regular Content Page (side by side) + +Use for pages with text and images. **Always prefer side-by-side layout.** + +```html +
+
+
+ +
+
+ +
+
+
+

First paragraph.

+

Second paragraph.

+
+
+
+ +
+
+

Another paragraph.

+
+
+ +
+
+
+
+
+``` + +### Template: Poem / Song Page (with star decorations) + +For poems, songs, or rhymes — use decorative stars and a centered layout: + +```html +
+
+
+
+ +
+
+
+
+
+ +

Poem Title

+ +
+
+

First line of poem

+

Second line of poem

+

Third line of poem

+

Fourth line of poem

+
+
+
+
+
+
+``` + +### Template: Text and Image Side by Side (reusable inner block) + +```html +
+
+
+ +
+
+ +
+
+
+``` + +### Template: Table of Contents + +Use this EXACT structure for table of contents pages: + +```html +
+
+
+ +
+ + +
+
+ +
+

Book Title

+

Table of Contents

+
+ +
+
+

Lesson 1

+

Lesson Title

+ +

2

+
+ +
+
+
+
+
+
+
+``` + +**Table of Contents Entry Row (repeat for each lesson):** + +```html +
+

Lesson 1

+

The Music Class

+ +

2

+
+``` + +**Important for Table of Contents:** +- Use `text-xl` for entries (larger than default for young readers) +- Use `font-bold` for lesson numbers and page numbers +- Title should be `text-4xl md:text-5xl` +- Subtitle should be `text-2xl md:text-3xl` +- Use subtle gray dotted line separator +- Keep the card spacious with `max-w-3xl` + +--- + +## General Rules + +1. **Page background via `data-background-color` ONLY** — never add `style="background: ..."` or `bg-*` classes to any element. The system reads `data-background-color` from the outer container and applies it to the body. Use soft palette tones like `#FFF5EB`, `#FFF0E6`, `#F0F7FD`, `#F5F0FA`, `#F0F8EF`. +2. **ALWAYS use side-by-side layout** when both text and images are present. +3. **Alternate image placement** — image on left for odd pages, image on right for even pages (or vice versa). +4. **Star decorations ONLY on poem/song/activity pages** — no other decorative elements. +5. **Use large text** — all body text must be `text-2xl md:text-3xl` minimum. +6. **No borders on images** — use only rounded corners and soft shadows. +7. **No colored borders on cards** — use only frosted glass backgrounds (`rgba(255, 255, 255, 0.7)`). +8. **No dot dividers** — if sections need separation, use simple spacing (`space-y-4`). +9. **Content may extend beyond viewport** — scrolling is acceptable. Pack content densely. +10. **Rounded corners everywhere** — use `rounded-3xl` on cards, images, and containers. +11. **Heading colors** — use maroon (#8B2252) or green (#3D8B37) for headings, never plain black. diff --git a/packages/pipeline/src/master.ts b/packages/pipeline/src/master.ts index a04d864..887f5a8 100644 --- a/packages/pipeline/src/master.ts +++ b/packages/pipeline/src/master.ts @@ -179,6 +179,7 @@ export async function runMaster( outputLanguages, title: bookTitle, webAssetsDir: options.webAssetsDir, + applyBodyBackground: config.apply_body_background, }, progress) } } finally { diff --git a/packages/pipeline/src/package-web.ts b/packages/pipeline/src/package-web.ts index b6e2973..81d4152 100644 --- a/packages/pipeline/src/package-web.ts +++ b/packages/pipeline/src/package-web.ts @@ -25,6 +25,7 @@ export interface PackageAdtWebOptions { title: string webAssetsDir: string bundleVersion?: string + applyBodyBackground?: boolean } interface PageEntry { @@ -55,6 +56,7 @@ export async function packageAdtWeb( title, webAssetsDir, bundleVersion = "1", + applyBodyBackground, } = options const language = normalizeLocale(rawLanguage) const outputLanguages = Array.from(new Set(rawOutputLanguages.map((code) => normalizeLocale(code)))) @@ -173,6 +175,7 @@ export async function packageAdtWeb( activityAnswers, hasMath: containsMathContent(rewrittenHtml), bundleVersion, + applyBodyBackground, }) fs.writeFileSync(path.join(adtDir, `${page.pageId}.html`), pageHtml) @@ -202,6 +205,7 @@ export async function packageAdtWeb( hasMath: false, bundleVersion, skipContentWrapper: true, + applyBodyBackground, }) fs.writeFileSync(path.join(adtDir, `${quizId}.html`), quizPageHtml) @@ -417,6 +421,7 @@ export interface RenderPageOptions { /** When true, content is placed directly in without a
wrapper. * Used for quiz pages whose template provides its own #content element. */ skipContentWrapper?: boolean + applyBodyBackground?: boolean } export function renderPageHtml(opts: RenderPageOptions): string { @@ -435,6 +440,15 @@ export function renderPageHtml(opts: RenderPageOptions): string { ${opts.content}
` + // Extract data-background-color from content to apply on + let bodyStyle = "" + if (opts.applyBodyBackground !== false) { + const bgMatch = opts.content.match(/data-background-color="([^"]*)"/) + bodyStyle = bgMatch?.[1] + ? ` style="background-color: ${escapeAttr(bgMatch[1])};"` + : "" + } + return ` @@ -451,7 +465,7 @@ export function renderPageHtml(opts: RenderPageOptions): string { ${mathScript} - + ${contentBlock} ${answersScript}
diff --git a/packages/pipeline/src/pipeline-dag.ts b/packages/pipeline/src/pipeline-dag.ts index 7e12380..7312a25 100644 --- a/packages/pipeline/src/pipeline-dag.ts +++ b/packages/pipeline/src/pipeline-dag.ts @@ -847,6 +847,7 @@ export async function runFullPipeline( outputLanguages, title: bookTitle, webAssetsDir: options.webAssetsDir, + applyBodyBackground: config.apply_body_background, }, progressOnly(p)) }) diff --git a/packages/types/src/config.ts b/packages/types/src/config.ts index 3874b84..f6468e6 100644 --- a/packages/types/src/config.ts +++ b/packages/types/src/config.ts @@ -105,6 +105,7 @@ export const AppConfig = z image_cropping: StepConfig.optional(), layout_type: LayoutType.optional(), spread_mode: z.boolean().optional(), + apply_body_background: z.boolean().optional(), start_page: z.number().int().min(1).optional(), end_page: z.number().int().min(1).optional(), speech: SpeechConfig.optional(),