Day 1 (monorepo + Node layout engine): - Turborepo + pnpm workspaces with apps/web, apps/render-worker, and packages for types, layout-engine, prompts, api-lib. - @banner-studio/types: BannerSpec contract, every layer kind, ResolvedLayer, zod schemas mirroring each interface. - @banner-studio/layout-engine: Dropflow WASM wrapper, text measurement, shrink-to-fit, push_siblings, resolveLayout. Snapshot-tested. Day 2 (browser parity + AI pipeline): - Layout engine ./browser subpath: same resolveLayout in the browser via Dropflow WASM build. Quarantined wasm-locator import (dropflow 0.5.1 exports gap). - Cross-group push_siblings bug fix: deltas now thread through group recursion via a shared accumulator; regression test added. - DEMO_TEMPLATE_300x250 promoted to packages/layout-engine/src/templates/. - @banner-studio/prompts: versioned extract + generate prompts with zod-defined tool schemas (claude-sonnet-4-6, forced tool-use). - @banner-studio/api-lib: CSV feed loader, extract/generate/route-node/ assemble agents, orchestrator returning fully-resolved BannerSpec. Generate agent retries on character-limit overflow. - apps/web (Next.js 14 App Router): /api/generate route, /parity diff page, promise-singleton browser engine init. - feeds/demo.csv with five hand-authored rows of varied length. - SLICE_DEVIATIONS.md documents the five intentional gaps from ARCHITECTURE.md with V1 reversal paths. Verified end-to-end: POST /api/generate against the live Claude API returns three resolved BannerSpecs and two honestly-skipped rows (overflow after two attempts). 26 unit + integration tests passing.
133 lines
No EOL
3.4 KiB
JSON
133 lines
No EOL
3.4 KiB
JSON
{
|
|
"copy": {
|
|
"headline": "Hello world",
|
|
"subheadline": "Subtitle line",
|
|
"cta": "Shop now"
|
|
},
|
|
"artboards": [
|
|
{
|
|
"artboard_id": "300x250",
|
|
"width": 300,
|
|
"height": 250,
|
|
"background": "#ffffff",
|
|
"layers": [
|
|
{
|
|
"layer_id": "hero",
|
|
"type": "smart_asset",
|
|
"computed_x": 0,
|
|
"computed_y": 0,
|
|
"computed_width": 300,
|
|
"computed_height": 250
|
|
},
|
|
{
|
|
"layer_id": "main-group",
|
|
"type": "group",
|
|
"computed_x": 20,
|
|
"computed_y": 60,
|
|
"computed_width": 260,
|
|
"computed_height": 140
|
|
},
|
|
{
|
|
"layer_id": "headline",
|
|
"type": "text",
|
|
"content": "Hello world",
|
|
"character_count": 11,
|
|
"within_character_limit": true,
|
|
"computed_x": 20,
|
|
"computed_y": 60,
|
|
"computed_width": 260,
|
|
"computed_height": 40,
|
|
"computed_font_size": 28,
|
|
"layout_log": {
|
|
"original_height": 40,
|
|
"computed_height": 40,
|
|
"siblings_pushed": [],
|
|
"font_size_reduced": false,
|
|
"overflow_triggered": false
|
|
}
|
|
},
|
|
{
|
|
"layer_id": "subheadline",
|
|
"type": "text",
|
|
"content": "Subtitle line",
|
|
"character_count": 13,
|
|
"within_character_limit": true,
|
|
"computed_x": 20,
|
|
"computed_y": 110,
|
|
"computed_width": 260,
|
|
"computed_height": 40,
|
|
"computed_font_size": 16,
|
|
"layout_log": {
|
|
"original_height": 40,
|
|
"computed_height": 40,
|
|
"siblings_pushed": [],
|
|
"font_size_reduced": false,
|
|
"overflow_triggered": false
|
|
}
|
|
},
|
|
{
|
|
"layer_id": "cta",
|
|
"type": "text",
|
|
"content": "Shop now",
|
|
"character_count": 8,
|
|
"within_character_limit": true,
|
|
"computed_x": 20,
|
|
"computed_y": 170,
|
|
"computed_width": 120,
|
|
"computed_height": 28,
|
|
"computed_font_size": 14,
|
|
"layout_log": {
|
|
"original_height": 28,
|
|
"computed_height": 28,
|
|
"siblings_pushed": [],
|
|
"font_size_reduced": false,
|
|
"overflow_triggered": false
|
|
}
|
|
}
|
|
],
|
|
"timeline": {
|
|
"duration_ms": 5000,
|
|
"events": [
|
|
{
|
|
"layer_id": "hero",
|
|
"type": "fade_in",
|
|
"at_ms": 0,
|
|
"duration_ms": 400
|
|
},
|
|
{
|
|
"layer_id": "headline",
|
|
"type": "fade_in",
|
|
"at_ms": 200,
|
|
"duration_ms": 400
|
|
},
|
|
{
|
|
"layer_id": "subheadline",
|
|
"type": "fade_in",
|
|
"at_ms": 400,
|
|
"duration_ms": 400
|
|
},
|
|
{
|
|
"layer_id": "cta",
|
|
"type": "fade_in",
|
|
"at_ms": 600,
|
|
"duration_ms": 400
|
|
},
|
|
{
|
|
"layer_id": "cta",
|
|
"type": "hold",
|
|
"at_ms": 1000,
|
|
"duration_ms": 3500
|
|
},
|
|
{
|
|
"layer_id": "cta",
|
|
"type": "fade_out",
|
|
"at_ms": 4500,
|
|
"duration_ms": 500
|
|
}
|
|
]
|
|
}
|
|
}
|
|
],
|
|
"captured_at": "2026-05-14T17:23:47.228Z",
|
|
"captured_with": "node @banner-studio/layout-engine"
|
|
} |