Julian Bilcke commited on
Commit
355629c
·
1 Parent(s): b182234

improve everything using AI

Browse files
Files changed (4) hide show
  1. CLAUDE.md +32 -9
  2. README.md +40 -4
  3. app.py +264 -100
  4. page_layouts.yaml +1396 -199
CLAUDE.md CHANGED
@@ -39,25 +39,48 @@ pip install -r requirements.txt
39
  - Supports custom styles and random style selection
40
  - Each preset includes prefix, suffix, and negative prompt components
41
 
42
- 4. **Page Layouts System** (`app.py:89-111`)
43
  - `load_page_layouts()`: Loads multi-image layouts from `page_layouts.yaml`
44
- - Supports 1-4 images per page with various layout configurations
 
 
45
  - Dynamic layout selection based on number of images
46
-
47
- 5. **PDF Generation** (`app.py:166-223`)
48
- - `create_pdf_with_layout()`: Creates PDF with multiple images in selected layout
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
49
  - Uses ReportLab for high-quality PDF generation
50
  - Preserves image quality at 95% JPEG compression
51
  - A4 page size with flexible positioning system
 
52
 
53
- 6. **Multi-Image Generation** (`app.py:225-307`)
54
- - `infer_multiple()`: Generates multiple images and combines into PDF
 
55
  - Progressive generation with status updates
56
  - Seed management for reproducibility across multiple images
57
  - Returns PDF file, preview image, and seed information
58
 
59
- 7. **Gradio Interface** (`app.py:380-500+`)
60
- - Slider for selecting 1-4 images per page
61
  - Dynamic layout dropdown that updates based on image count
62
  - Style preset dropdown with custom style text option
63
  - PDF download and image preview outputs
 
39
  - Supports custom styles and random style selection
40
  - Each preset includes prefix, suffix, and negative prompt components
41
 
42
+ 4. **Page Layouts System** (`app.py:89-145`)
43
  - `load_page_layouts()`: Loads multi-image layouts from `page_layouts.yaml`
44
+ - `get_layout_choices()`: Returns available layouts for a given number of images
45
+ - `get_layout_metadata()`: Extracts panel metadata (type, focus, composition) for each position
46
+ - Supports 1-8 images per page with 5-6 layout variations each
47
  - Dynamic layout selection based on number of images
48
+ - **Panel Metadata System**: Each panel position includes metadata that describes:
49
+ - `panel_type`: establishing/action/closeup/dialogue/reaction/transition/detail/splash
50
+ - `focus`: environment/character/characters/action/emotion/object/event
51
+ - `composition`: wide/tall/square/portrait/landscape
52
+ - Metadata is used to guide the LLM in generating appropriate scene descriptions
53
+
54
+ 5. **Story Generation System** (`app.py:147-265`)
55
+ - `generate_story_scenes()`: Uses Hugging Face InferenceClient with Qwen3-235B to generate scene descriptions
56
+ - Takes panel metadata as input to generate contextually appropriate content
57
+ - Adapts descriptions based on panel type, focus, and composition
58
+ - Returns structured scene data with captions and dialogue
59
+ - `parse_yaml_scenes()`: Parses LLM output into structured scene data
60
+
61
+ 6. **Image Size Calculation** (`app.py:267-330`)
62
+ - `get_image_size_for_position()`: Calculates precise image dimensions based on layout aspect ratio
63
+ - Uses 8px rounding for model compatibility while maintaining aspect ratio accuracy
64
+ - Ensures images fill their layout containers without floating
65
+ - `get_layout_position_for_image()`: Retrieves position data for a specific panel
66
+
67
+ 7. **PDF Generation** (`app.py:450-540`)
68
+ - `create_single_page_pdf()`: Creates PDF page with images arranged per layout
69
+ - `create_multi_page_pdf()`: Combines multiple pages into a single document
70
  - Uses ReportLab for high-quality PDF generation
71
  - Preserves image quality at 95% JPEG compression
72
  - A4 page size with flexible positioning system
73
+ - Smart filling: fills space completely when aspect ratios match (<2% difference)
74
 
75
+ 8. **Multi-Image Generation** (`app.py:545-650`)
76
+ - `infer_page()`: Main generation orchestrator
77
+ - Generates multiple images and combines into PDF
78
  - Progressive generation with status updates
79
  - Seed management for reproducibility across multiple images
80
  - Returns PDF file, preview image, and seed information
81
 
82
+ 9. **Gradio Interface** (`app.py:750-900+`)
83
+ - Slider for selecting 1-8 images per page
84
  - Dynamic layout dropdown that updates based on image count
85
  - Style preset dropdown with custom style text option
86
  - PDF download and image preview outputs
README.md CHANGED
@@ -1,13 +1,49 @@
1
  ---
2
- title: Qwen Image Fast
3
- emoji: 🖼️
4
  colorFrom: yellow
5
  colorTo: green
6
  sdk: gradio
7
  sdk_version: 5.39.0
8
  app_file: app.py
9
  pinned: false
10
- short_description: Generate images in 8-steps
11
  ---
12
 
13
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  ---
2
+ title: AiComicFactory2
3
+ emoji: 🦸
4
  colorFrom: yellow
5
  colorTo: green
6
  sdk: gradio
7
  sdk_version: 5.39.0
8
  app_file: app.py
9
  pinned: false
10
+ short_description: Generate PDF comic books
11
  ---
12
 
13
+ ## What is this?
14
+
15
+ This space is an app to generate comic books in PDF in a playful and easy way using AI.
16
+
17
+ It is designed to allow people having fun, without the technical constraint of manually typing text in speech bubbles etc. It is deliberately not designed for human-based manual editing.
18
+
19
+ ## How can I use it?
20
+
21
+ The AiComicFactory2 is a standard Python/Gradio app that is free and open-source, but you need to have access to a GPU to run it.
22
+
23
+ The easiest way is to run it directly from my space on Hugging Face using the ZeroGPU system, which allows you to benefit from a free quota that gets refilled regularly.
24
+
25
+ You can subscribe to a PRO account on Hugging Face to get higher usage quotas 🫶
26
+
27
+ ### Running in local
28
+
29
+ Alternatively if you already own your own GPU, then you simply have to install the Gradio app and run it!
30
+
31
+ While the commands to use varies depending on wether you use a Python package manager and your version of python, the basic CLI workflow is usually like this:
32
+
33
+ ```bash
34
+ # prerequisites not covered by this quickstart:
35
+ # you need a terminal, Python, Git and Git LFS
36
+
37
+ # Get the code using Git
38
+ git clone git@hf.co:spaces/jbilcke-hf/AiComicFactory2
39
+
40
+ cd AiComicFactory2
41
+
42
+ # global install of dependencies
43
+ # the executable might be pip3 depending on your system
44
+ # but you should use a venv / package manager
45
+ pip install -r requirements.txt
46
+
47
+ # run the app (the executable might be python3, python3.12, python3.13 etc.. depending on your system)
48
+ python app.py
49
+ ```
app.py CHANGED
@@ -55,10 +55,10 @@ def load_page_layouts():
55
  print(f"Error loading page layouts: {e}")
56
  # Fallback to basic layouts
57
  return {
58
- 1: [{"id": "full_page", "label": "Full Page", "positions": [[0.05, 0.05, 0.9, 0.9]]}],
59
- 2: [{"id": "horizontal_split", "label": "Horizontal Split", "positions": [[0.05, 0.05, 0.425, 0.9], [0.525, 0.05, 0.425, 0.9]]}],
60
- 3: [{"id": "grid", "label": "Grid", "positions": [[0.05, 0.05, 0.283, 0.5], [0.358, 0.05, 0.283, 0.5], [0.666, 0.05, 0.283, 0.5]]}],
61
- 4: [{"id": "grid_2x2", "label": "2x2 Grid", "positions": [[0.05, 0.05, 0.425, 0.425], [0.525, 0.05, 0.425, 0.425], [0.05, 0.525, 0.425, 0.425], [0.525, 0.525, 0.425, 0.425]]}]
62
  }
63
 
64
  # Load layouts at startup
@@ -72,6 +72,33 @@ def get_layout_choices(num_images: int) -> List[Tuple[str, str]]:
72
  # Return empty list if no layouts found (shouldn't happen with our config)
73
  return [("Default", "default")]
74
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
75
  def get_random_style_preset():
76
  """Get a random style preset (excluding 'no_style' and 'random')."""
77
  eligible_keys = [k for k in STYLE_PRESETS.keys() if k not in ['no_style', 'random']]
@@ -125,7 +152,7 @@ def apply_style_preset(prompt, style_preset_key, custom_style_text=""):
125
 
126
  # --- Story Generation using Hugging Face InferenceClient ---
127
 
128
- def generate_story_scenes(story_prompt, num_scenes, style_context=""):
129
  """
130
  Generates a sequence of scene descriptions with captions and dialogues.
131
 
@@ -133,6 +160,7 @@ def generate_story_scenes(story_prompt, num_scenes, style_context=""):
133
  story_prompt: The user's story prompt
134
  num_scenes: Number of scenes to generate
135
  style_context: Optional style context to consider
 
136
 
137
  Returns:
138
  List of dicts with 'caption' and 'dialogue' keys
@@ -156,29 +184,105 @@ def generate_story_scenes(story_prompt, num_scenes, style_context=""):
156
  api_key=api_key,
157
  )
158
 
159
- # Create system prompt for story generation
160
- system_prompt = f"""You are a comic book story writer. Generate exactly {num_scenes} scenes for a comic page based on the user's story prompt.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
161
 
162
  IMPORTANT INSTRUCTIONS:
163
  1. Output ONLY a YAML list with exactly {num_scenes} items
164
  2. Each item must have exactly two fields:
165
  - caption: A detailed visual description of the scene (describe characters, clothing, location, action, expressions)
166
  - dialogue: Natural language description of what the character says/exclaims/shouts (can be empty string if no dialogue)
167
- 3. For captions: Be very descriptive. Repeat character descriptions in each scene (appearance, clothes, etc.)
168
- 4. For dialogue: Write it as a natural language action that will be added to the scene description
169
- - Format: "The [character] says: [what they say]" or "The [character] exclaims: [what they exclaim]"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
170
  - DO NOT include character names in the dialogue text itself
171
- - Use verbs like: says, exclaims, shouts, whispers, asks, replies, thinks
172
- 5. Keep continuity between scenes to tell a coherent story
173
- 6. Make each scene visually distinct but connected to the narrative
 
174
 
175
  Example output format:
176
- - caption: "A young woman with long red hair wearing a blue detective coat stands in a dark alley, holding a magnifying glass up to examine mysterious glowing footprints on the wet pavement"
177
- dialogue: "The detective exclaims: These tracks aren't human!"
178
- - caption: "The same red-haired woman in the blue coat backs away in shock as a massive shark fin emerges from a puddle in the alley, water splashing everywhere"
179
- dialogue: "The detective shouts: OH NO, SHARKS IN THE CITY!"
180
- - caption: "The red-haired detective in blue coat runs down the alley, looking back over her shoulder at the shark fin pursuing her through the puddles"
181
- dialogue: "The detective thinks to herself: I need to warn everyone!"
182
 
183
  Generate exactly {num_scenes} scenes. Output ONLY the YAML list, no other text."""
184
 
@@ -300,64 +404,73 @@ pipe.load_lora_weights(
300
  "lightx2v/Qwen-Image-Lightning", weight_name="Qwen-Image-Lightning-8steps-V1.1.safetensors"
301
  )
302
  pipe.fuse_lora()
303
- #pipe.unload_lora_weights()
304
-
305
- #pipe.load_lora_weights("flymy-ai/qwen-image-realism-lora")
306
- #pipe.fuse_lora()
307
- #pipe.unload_lora_weights()
308
-
309
 
310
  # --- UI Constants and Helpers ---
311
  MAX_SEED = np.iinfo(np.int32).max
312
 
313
  def get_image_size_for_position(position_data, image_index, num_images, max_resolution=1024):
314
- """Determines optimal image size based on its position in the layout.
315
-
 
 
 
 
316
  Args:
317
- position_data: Layout position data [x, y, width, height] in relative units
318
  image_index: Index of the current image (0-based)
319
  num_images: Total number of images in the layout
320
  max_resolution: Maximum resolution for any dimension (default 1024)
321
 
322
  Returns:
323
- tuple: (width, height) optimized for the position's aspect ratio
324
  """
325
  if not position_data:
326
  return max_resolution, max_resolution # Default square
327
 
328
  x_rel, y_rel, w_rel, h_rel = position_data
329
- aspect_ratio = w_rel / h_rel if h_rel > 0 else 1.0
 
 
 
330
 
331
- # Calculate dimensions maintaining aspect ratio
332
- if aspect_ratio >= 1: # Wider than tall
333
  width = max_resolution
334
- height = int(max_resolution / aspect_ratio)
335
- # Ensure height is at least 384 for quality
336
- if height < 384:
337
- height = 384
338
- width = int(384 * aspect_ratio)
339
  else: # Taller than wide
340
  height = max_resolution
341
- width = int(max_resolution * aspect_ratio)
342
- # Ensure width is at least 384 for quality
343
- if width < 384:
344
- width = 384
345
- height = int(384 / aspect_ratio)
346
-
347
- # Round to nearest 64 for better compatibility
348
- width = (width // 64) * 64
349
- height = (height // 64) * 64
350
-
351
- # Ensure we don't exceed max_resolution after rounding
352
  if width > max_resolution:
353
  width = max_resolution
 
354
  if height > max_resolution:
355
  height = max_resolution
356
-
357
- # Minimum size check (increased from 256 to 384 for better quality)
358
- width = max(width, 384)
359
- height = max(height, 384)
360
-
 
 
 
 
 
 
 
 
 
 
 
 
361
  return width, height
362
 
363
  def get_layout_position_for_image(layout_id, num_images, image_index):
@@ -391,7 +504,13 @@ def get_layout_position_for_image(layout_id, num_images, image_index):
391
  [0.666, 0.4, 0.283, 0.275], [0.666, 0.7, 0.283, 0.275]],
392
  6: [[0.05, 0.05, 0.425, 0.283], [0.525, 0.05, 0.425, 0.283],
393
  [0.05, 0.358, 0.425, 0.283], [0.525, 0.358, 0.425, 0.283],
394
- [0.05, 0.666, 0.425, 0.283], [0.525, 0.666, 0.425, 0.283]]
 
 
 
 
 
 
395
  }
396
 
397
  positions = fallback_positions.get(num_images, fallback_positions[1])
@@ -522,26 +641,28 @@ def create_single_page_pdf(images: List[Image.Image], layout_id: str, num_images
522
  if num_images == 1:
523
  positions = [[0.02, 0.02, 0.96, 0.96]]
524
  elif num_images == 2:
525
- # Horizontal split with gap
526
  positions = [[0.02, 0.02, 0.47, 0.96], [0.51, 0.02, 0.47, 0.96]]
527
  elif num_images == 3:
528
- # Three horizontal panels with gaps
529
  positions = [[0.02, 0.2, 0.31, 0.6], [0.345, 0.2, 0.31, 0.6], [0.67, 0.2, 0.31, 0.6]]
530
  elif num_images == 4:
531
- # 2x2 grid with gaps
532
  positions = [[0.02, 0.02, 0.47, 0.47], [0.51, 0.02, 0.47, 0.47],
533
  [0.02, 0.51, 0.47, 0.47], [0.51, 0.51, 0.47, 0.47]]
534
  elif num_images == 5:
535
- # Hero top with 4 small panels below
536
  positions = [[0.02, 0.02, 0.96, 0.44], [0.02, 0.48, 0.31, 0.5], [0.345, 0.48, 0.31, 0.5],
537
  [0.67, 0.48, 0.31, 0.24], [0.67, 0.74, 0.31, 0.24]]
538
  elif num_images == 6:
539
- # 2x3 grid with gaps
540
  positions = [[0.02, 0.02, 0.47, 0.31], [0.51, 0.02, 0.47, 0.31],
541
  [0.02, 0.345, 0.47, 0.31], [0.51, 0.345, 0.47, 0.31],
542
  [0.02, 0.67, 0.47, 0.31], [0.51, 0.67, 0.47, 0.31]]
 
 
 
 
 
 
 
 
543
  else:
544
- # For more than 6, create a simple grid
545
  positions = [[0.02, 0.02, 0.96, 0.96]]
546
  else:
547
  positions = layout["positions"]
@@ -556,9 +677,6 @@ def create_single_page_pdf(images: List[Image.Image], layout_id: str, num_images
556
  # Add small padding between panels (1% of page dimensions)
557
  padding = 0.01
558
 
559
- # Don't scale up - use the positions as defined in the layout
560
- # This prevents overlapping when there are multiple images
561
-
562
  # Apply padding to prevent images from touching edges
563
  if x_rel < padding:
564
  x_rel = padding
@@ -576,38 +694,49 @@ def create_single_page_pdf(images: List[Image.Image], layout_id: str, num_images
576
  width = w_rel * page_width
577
  height = h_rel * page_height
578
 
579
- # Calculate image aspect ratio and layout aspect ratio
580
  img_aspect = image.width / image.height
581
  layout_aspect = width / height
582
-
583
- # Preserve aspect ratio while fitting in the allocated space
584
- if img_aspect > layout_aspect:
585
- # Image is wider than the layout space
586
- new_height = width / img_aspect
587
- y_offset = (height - new_height) / 2
588
  actual_width = width
589
- actual_height = new_height
590
- actual_x = x
591
- actual_y = y + y_offset
592
- else:
593
- # Image is taller than the layout space
594
- new_width = height * img_aspect
595
- x_offset = (width - new_width) / 2
596
- actual_width = new_width
597
  actual_height = height
598
- actual_x = x + x_offset
599
  actual_y = y
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
600
 
601
  # Convert PIL image to format suitable for ReportLab
602
  img_buffer = io.BytesIO()
603
- # Save with good quality
604
  image.save(img_buffer, format='JPEG', quality=95)
605
  img_buffer.seek(0)
606
 
607
- # Draw the image on the PDF preserving aspect ratio
 
 
608
  pdf.drawImage(ImageReader(img_buffer), actual_x, actual_y,
609
  width=actual_width, height=actual_height,
610
- preserveAspectRatio=True, mask='auto')
611
 
612
  # Save the PDF
613
  pdf.save()
@@ -655,7 +784,7 @@ def create_multi_page_pdf(session_manager: SessionManager) -> str:
655
  return str(pdf_path)
656
 
657
  # --- Main Inference Function (with session support) ---
658
- @spaces.GPU(duration=180) # Increased duration for up to 6 images
659
  def infer_page(
660
  prompt,
661
  guidance_scale=1.0,
@@ -677,8 +806,9 @@ def infer_page(
677
  num_inference_steps (int): The number of denoising steps.
678
  style_preset (str): The key of the style preset to apply.
679
  custom_style_text (str): Custom style text when 'no_style' is selected.
680
- num_images (int): Number of images to generate (1-6).
681
  layout (str): The layout ID for arranging images in the PDF.
 
682
  session_state: Current session state dictionary.
683
  progress (gr.Progress): A Gradio Progress object to track generation.
684
 
@@ -702,9 +832,20 @@ def infer_page(
702
  generated_images = []
703
  used_seeds = []
704
 
705
- # Generate story scenes
 
 
 
 
 
 
 
 
 
 
 
706
  progress(0, f"Generating story with {num_images} scenes...")
707
- scenes = generate_story_scenes(prompt, int(num_images), style_preset)
708
 
709
  # Generate the requested number of images
710
  for i in range(int(num_images)):
@@ -718,6 +859,21 @@ def infer_page(
718
  # Use scene caption and dialogue for this image
719
  scene_prompt = scenes[i]['caption']
720
  scene_dialogue = scenes[i]['dialogue']
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
721
 
722
  # Generate single image with automatic aspect ratio
723
  image, used_seed = infer_single_auto(
@@ -767,10 +923,10 @@ def infer_single_auto(
767
  num_images=1,
768
  guidance_scale=1.0,
769
  num_inference_steps=8,
770
- dialogue="", # New parameter for dialogue
771
  style_preset="no_style",
772
  custom_style_text="",
773
- max_resolution=1024, # New parameter for max resolution
774
  ):
775
  """
776
  Generates an image with automatically determined aspect ratio based on layout position.
@@ -780,7 +936,15 @@ def infer_single_auto(
780
 
781
  # Automatically determine image size based on position with custom max resolution
782
  width, height = get_image_size_for_position(position_data, image_index, num_images, max_resolution)
783
-
 
 
 
 
 
 
 
 
784
  # Set up the generator for reproducibility
785
  generator = torch.Generator(device="cuda").manual_seed(seed)
786
 
@@ -793,7 +957,6 @@ def infer_single_auto(
793
 
794
  # Add dialogue to the prompt if present
795
  if dialogue and dialogue.strip():
796
- # Simply append the dialogue as it's already properly formatted from the LLM
797
  styled_prompt = f"{styled_prompt}. {dialogue.strip()}"
798
 
799
  # Use style negative prompt if available, otherwise default
@@ -811,12 +974,11 @@ def infer_single_auto(
811
  height=height,
812
  num_inference_steps=num_inference_steps,
813
  generator=generator,
814
- true_cfg_scale=guidance_scale, # Use true_cfg_scale for this model
815
  ).images[0]
816
 
817
  # Convert to grayscale if using manga_no_color style
818
  if style_preset == "manga_no_color":
819
- # Convert to grayscale while preserving quality
820
  image = image.convert('L').convert('RGB')
821
 
822
  return image, seed
@@ -980,10 +1142,10 @@ with gr.Blocks(css=css) as demo:
980
  num_images_slider = gr.Slider(
981
  label="Images per page",
982
  minimum=1,
983
- maximum=6,
984
  step=1,
985
  value=1,
986
- info="Number of images to generate for the PDF (1-6)"
987
  )
988
 
989
  # Page layout dropdown
@@ -1032,17 +1194,19 @@ with gr.Blocks(css=css) as demo:
1032
  with gr.Accordion("Examples", open=True):
1033
  styled_examples = [
1034
  ["A capybara wearing a suit holding a sign that reads Hello World", "no_style", "", 1],
1035
- ["sharks raining down on san francisco", "anime", "", 2],
1036
- ["A beautiful landscape with mountains and a lake", "watercolor", "", 3],
1037
- ["A knight fighting a dragon", "medieval", "", 4],
1038
- ["Space battle with laser beams", "sci-fi", "", 5],
1039
- ["Detective investigating a mystery", "noir", "", 6],
 
 
1040
  ]
1041
 
1042
  gr.Examples(
1043
  examples=styled_examples,
1044
  inputs=[prompt, style_preset, custom_style_text, num_images_slider],
1045
- outputs=None, # Don't show outputs for examples
1046
  fn=None,
1047
  cache_examples=False
1048
  )
 
55
  print(f"Error loading page layouts: {e}")
56
  # Fallback to basic layouts
57
  return {
58
+ "1_image": [{"id": "full_page", "label": "Full Page", "positions": [[0.05, 0.05, 0.9, 0.9]]}],
59
+ "2_images": [{"id": "horizontal_split", "label": "Horizontal Split", "positions": [[0.05, 0.05, 0.425, 0.9], [0.525, 0.05, 0.425, 0.9]]}],
60
+ "3_images": [{"id": "grid", "label": "Grid", "positions": [[0.05, 0.05, 0.283, 0.5], [0.358, 0.05, 0.283, 0.5], [0.666, 0.05, 0.283, 0.5]]}],
61
+ "4_images": [{"id": "grid_2x2", "label": "2x2 Grid", "positions": [[0.05, 0.05, 0.425, 0.425], [0.525, 0.05, 0.425, 0.425], [0.05, 0.525, 0.425, 0.425], [0.525, 0.525, 0.425, 0.425]]}]
62
  }
63
 
64
  # Load layouts at startup
 
72
  # Return empty list if no layouts found (shouldn't happen with our config)
73
  return [("Default", "default")]
74
 
75
+ def get_layout_metadata(layout_id: str, num_images: int) -> List[Dict]:
76
+ """Get metadata for each panel in a layout.
77
+
78
+ Args:
79
+ layout_id: ID of the selected layout
80
+ num_images: Total number of images
81
+
82
+ Returns:
83
+ List of metadata dicts with panel_type, focus, composition, shot_type, and camera_angle
84
+ """
85
+ key = f"{num_images}_image" if num_images == 1 else f"{num_images}_images"
86
+ layouts = PAGE_LAYOUTS.get(key, [])
87
+ layout = next((l for l in layouts if l["id"] == layout_id), None)
88
+
89
+ if layout and "metadata" in layout:
90
+ return layout["metadata"]
91
+
92
+ # Fallback metadata if not found
93
+ fallback_meta = {
94
+ "panel_type": "action",
95
+ "focus": "character",
96
+ "composition": "square",
97
+ "shot_type": "medium",
98
+ "camera_angle": "eye_level"
99
+ }
100
+ return [fallback_meta] * num_images
101
+
102
  def get_random_style_preset():
103
  """Get a random style preset (excluding 'no_style' and 'random')."""
104
  eligible_keys = [k for k in STYLE_PRESETS.keys() if k not in ['no_style', 'random']]
 
152
 
153
  # --- Story Generation using Hugging Face InferenceClient ---
154
 
155
+ def generate_story_scenes(story_prompt, num_scenes, style_context="", panel_metadata=None):
156
  """
157
  Generates a sequence of scene descriptions with captions and dialogues.
158
 
 
160
  story_prompt: The user's story prompt
161
  num_scenes: Number of scenes to generate
162
  style_context: Optional style context to consider
163
+ panel_metadata: List of metadata dicts for each panel
164
 
165
  Returns:
166
  List of dicts with 'caption' and 'dialogue' keys
 
184
  api_key=api_key,
185
  )
186
 
187
+ # Build panel descriptions from metadata
188
+ panel_descriptions = []
189
+ if panel_metadata and len(panel_metadata) == num_scenes:
190
+ for i, meta in enumerate(panel_metadata):
191
+ panel_type = meta.get('panel_type', 'action')
192
+ focus = meta.get('focus', 'character')
193
+ composition = meta.get('composition', 'square')
194
+ shot_type = meta.get('shot_type', 'medium')
195
+ camera_angle = meta.get('camera_angle', 'eye_level')
196
+
197
+ # Format shot type for readability
198
+ shot_display = shot_type.replace('_', ' ').title()
199
+ angle_display = camera_angle.replace('_', ' ').title()
200
+
201
+ # Create a descriptive text for this panel
202
+ desc = f"Panel {i+1}/{num_scenes} - {composition.upper()} composition, {shot_display} shot at {angle_display} angle, {panel_type} panel focusing on {focus}"
203
+ panel_descriptions.append(desc)
204
+ else:
205
+ # Fallback if no metadata
206
+ panel_descriptions = [f"Panel {i+1}/{num_scenes}" for i in range(num_scenes)]
207
+
208
+ # Create system prompt with panel-specific guidance
209
+ system_prompt = f"""You are a comic book story writer with expertise in cinematography and visual storytelling. Generate exactly {num_scenes} scenes for a comic page based on the user's story prompt.
210
+
211
+ PANEL LAYOUT INFORMATION:
212
+ The page has {num_scenes} panels with the following characteristics:
213
+ {chr(10).join(f"- {desc}" for desc in panel_descriptions)}
214
 
215
  IMPORTANT INSTRUCTIONS:
216
  1. Output ONLY a YAML list with exactly {num_scenes} items
217
  2. Each item must have exactly two fields:
218
  - caption: A detailed visual description of the scene (describe characters, clothing, location, action, expressions)
219
  - dialogue: Natural language description of what the character says/exclaims/shouts (can be empty string if no dialogue)
220
+
221
+ 3. **ADAPT EACH SCENE TO ITS PANEL TYPE:**
222
+ - ESTABLISHING panels: Describe the full environment, setting, atmosphere, time of day, location details
223
+ - ACTION panels: Focus on dynamic movement, motion lines, impact, energy, physical activity
224
+ - CLOSEUP panels: Describe facial features, eyes, expressions, emotions in extreme detail
225
+ - DIALOGUE panels: Focus on character interactions, body language during conversation
226
+ - REACTION panels: Emphasize emotional responses, facial expressions, body language
227
+ - DETAIL panels: Zoom in on specific objects, hands, symbols, or small but important elements
228
+ - TRANSITION panels: Show passage of time, change of location, or connecting moments
229
+ - SPLASH panels: Epic, dramatic, full-scene moments with maximum visual impact
230
+
231
+ 4. **ADAPT TO SHOT TYPE (CINEMATOGRAPHY):**
232
+ - EXTREME WIDE SHOT: Vast landscapes, tiny characters in massive environments, epic scale
233
+ - WIDE SHOT: Full scene with characters and environment, establishing context
234
+ - FULL SHOT: Entire character from head to toe, showing their full body and stance
235
+ - MEDIUM FULL SHOT: Character from knees up, showing most of body with some environment
236
+ - MEDIUM SHOT: Character from waist up, balanced between character and setting
237
+ - MEDIUM CLOSEUP: Head and shoulders, focusing on face while showing some context
238
+ - CLOSEUP: Face filling frame, detailed facial features and expressions
239
+ - EXTREME CLOSEUP: Tiny detail - just eyes, hands, mouth, or specific object filling frame
240
+
241
+ 5. **ADAPT TO CAMERA ANGLE:**
242
+ - EYE LEVEL: Neutral, straightforward angle - camera at character's eye level
243
+ - HIGH ANGLE: Camera looking down on subject - can make them seem vulnerable, small, or overwhelmed
244
+ - LOW ANGLE: Camera looking up at subject - makes them seem powerful, imposing, heroic
245
+ - OVERHEAD/BIRD'S EYE: Camera directly above looking down - shows spatial relationships, isolation
246
+ - DUTCH ANGLE/CANTED: Tilted camera - creates tension, disorientation, chaos, unease
247
+ - OVER THE SHOULDER (OTS): Camera behind one character looking at another - intimate conversation
248
+ - POV (Point of View): Camera as character's eyes - immersive, first-person perspective
249
+
250
+ 6. **ADAPT TO COMPOSITION:**
251
+ - WIDE/LANDSCAPE: Emphasize horizontal elements, panoramic views, sweeping scenes, breadth
252
+ - TALL/PORTRAIT: Emphasize vertical elements, full-body shots, top-to-bottom action, height
253
+ - SQUARE: Balanced composition, centered subjects, symmetrical arrangements
254
+
255
+ 7. **ADAPT TO FOCUS:**
256
+ - CHARACTER: Detailed character description (appearance, clothing, pose, expression)
257
+ - CHARACTERS (plural): Multiple people, their relationships, positioning, interactions
258
+ - ENVIRONMENT: Setting details, location, atmosphere, background elements, mood
259
+ - EVENT: What's happening, the action, the moment being captured, the incident
260
+ - EMOTION: Facial expression, body language, emotional state, feelings
261
+ - OBJECT: Detailed description of an important item, prop, symbol, or artifact
262
+ - ACTION: Movement, impact, dynamic poses, energy, motion
263
+
264
+ 8. **CONSIDER PANEL PROGRESSION:**
265
+ - You're creating panel X of {num_scenes} - consider where you are in the story flow
266
+ - Early panels (1-2/{num_scenes}): Establish setting and introduce characters
267
+ - Middle panels: Build action, develop conflict, show character reactions
268
+ - Later panels ({num_scenes-1}-{num_scenes}/{num_scenes}): Resolve the moment, provide reaction or cliffhanger
269
+
270
+ 9. For captions: Be VERY descriptive. Include shot type language like "wide shot of...", "close-up on...", "overhead view of...". Repeat character descriptions in each scene if needed.
271
+
272
+ 10. For dialogue: Write as natural language action: "The [character] says: [what they say]" or "The [character] exclaims: [what they exclaim]"
273
  - DO NOT include character names in the dialogue text itself
274
+ - Use verbs like: says, exclaims, shouts, whispers, asks, replies, thinks, mutters, screams
275
+
276
+ 11. Keep continuity between scenes to tell a coherent story
277
+ 12. Make each scene visually distinct but connected to the narrative
278
 
279
  Example output format:
280
+ - caption: "Extreme wide shot from high angle of a dark alley at night, rain pouring down heavily, neon signs casting red and blue reflections in vast puddles covering the ground, tall buildings looming menacingly on both sides creating a narrow canyon, a young woman with long red hair wearing a blue detective coat stands small in the center of the frame examining glowing footprints on the wet pavement with a magnifying glass, dramatic lighting from above"
281
+ dialogue: "The detective whispers to herself: These tracks... they're not human"
282
+ - caption: "Extreme close-up at eye level of the detective's piercing green eyes widening in shock and fear, her pupils dilating rapidly, individual beads of rain clinging to her dark eyelashes, her face illuminated by an eerie pulsing blue glow from below, wrinkles forming on her forehead"
283
+ dialogue: ""
284
+ - caption: "Full shot at low angle, the red-haired detective in the blue coat leaping backwards dynamically with motion blur streaks, her coat billowing dramatically, a massive jagged shark fin erupting violently from a puddle behind her, water exploding upward in huge spray with droplets frozen mid-air, her expression one of pure terror, arms flailing"
285
+ dialogue: "The detective shouts at the top of her lungs: OH NO! SHARKS IN THE CITY!"
286
 
287
  Generate exactly {num_scenes} scenes. Output ONLY the YAML list, no other text."""
288
 
 
404
  "lightx2v/Qwen-Image-Lightning", weight_name="Qwen-Image-Lightning-8steps-V1.1.safetensors"
405
  )
406
  pipe.fuse_lora()
 
 
 
 
 
 
407
 
408
  # --- UI Constants and Helpers ---
409
  MAX_SEED = np.iinfo(np.int32).max
410
 
411
  def get_image_size_for_position(position_data, image_index, num_images, max_resolution=1024):
412
+ """
413
+ Calculate EXACT image dimensions to match layout aspect ratio perfectly.
414
+
415
+ This function calculates pixel dimensions that precisely match the aspect ratio
416
+ of the layout rectangle to ensure images fill their containers without floating.
417
+
418
  Args:
419
+ position_data: Layout position data [x, y, width, height] in relative units (0-1)
420
  image_index: Index of the current image (0-based)
421
  num_images: Total number of images in the layout
422
  max_resolution: Maximum resolution for any dimension (default 1024)
423
 
424
  Returns:
425
+ tuple: (width, height) with exact aspect ratio matching the layout
426
  """
427
  if not position_data:
428
  return max_resolution, max_resolution # Default square
429
 
430
  x_rel, y_rel, w_rel, h_rel = position_data
431
+
432
+ # Calculate the EXACT aspect ratio from the layout rectangle
433
+ # This is crucial - we must match this aspect ratio precisely
434
+ layout_aspect_ratio = w_rel / h_rel if h_rel > 0 else 1.0
435
 
436
+ # Scale to max_resolution while maintaining EXACT aspect ratio
437
+ if layout_aspect_ratio >= 1: # Wider than tall
438
  width = max_resolution
439
+ height = max_resolution / layout_aspect_ratio
 
 
 
 
440
  else: # Taller than wide
441
  height = max_resolution
442
+ width = max_resolution * layout_aspect_ratio
443
+
444
+ # Round to nearest 8 pixels for model compatibility
445
+ # Using 8px instead of 64px preserves aspect ratio much better
446
+ # Most diffusion models work well with multiples of 8
447
+ width = round(width / 8) * 8
448
+ height = round(height / 8) * 8
449
+
450
+ # After rounding, ensure we maintain the aspect ratio as closely as possible
451
+ # and don't exceed max_resolution
 
452
  if width > max_resolution:
453
  width = max_resolution
454
+ height = round((max_resolution / layout_aspect_ratio) / 8) * 8
455
  if height > max_resolution:
456
  height = max_resolution
457
+ width = round((max_resolution * layout_aspect_ratio) / 8) * 8
458
+
459
+ # Ensure minimum size of 256px (reduced from 384 for more flexibility)
460
+ # while maintaining the layout aspect ratio
461
+ min_size = 256
462
+ if width < min_size or height < min_size:
463
+ if layout_aspect_ratio >= 1: # Wider image
464
+ width = max(min_size, width)
465
+ height = round((width / layout_aspect_ratio) / 8) * 8
466
+ else: # Taller image
467
+ height = max(min_size, height)
468
+ width = round((height * layout_aspect_ratio) / 8) * 8
469
+
470
+ # Final safety checks
471
+ width = max(min_size, min(int(width), max_resolution))
472
+ height = max(min_size, min(int(height), max_resolution))
473
+
474
  return width, height
475
 
476
  def get_layout_position_for_image(layout_id, num_images, image_index):
 
504
  [0.666, 0.4, 0.283, 0.275], [0.666, 0.7, 0.283, 0.275]],
505
  6: [[0.05, 0.05, 0.425, 0.283], [0.525, 0.05, 0.425, 0.283],
506
  [0.05, 0.358, 0.425, 0.283], [0.525, 0.358, 0.425, 0.283],
507
+ [0.05, 0.666, 0.425, 0.283], [0.525, 0.666, 0.425, 0.283]],
508
+ 7: [[0.28, 0.02, 0.44, 0.3], [0.02, 0.25, 0.3, 0.25], [0.68, 0.25, 0.3, 0.25],
509
+ [0.25, 0.35, 0.5, 0.3], [0.02, 0.52, 0.3, 0.25], [0.68, 0.52, 0.3, 0.25],
510
+ [0.28, 0.68, 0.44, 0.3]],
511
+ 8: [[0.02, 0.02, 0.23, 0.47], [0.27, 0.02, 0.23, 0.47], [0.52, 0.02, 0.23, 0.47],
512
+ [0.77, 0.02, 0.21, 0.47], [0.02, 0.51, 0.23, 0.47], [0.27, 0.51, 0.23, 0.47],
513
+ [0.52, 0.51, 0.23, 0.47], [0.77, 0.51, 0.21, 0.47]]
514
  }
515
 
516
  positions = fallback_positions.get(num_images, fallback_positions[1])
 
641
  if num_images == 1:
642
  positions = [[0.02, 0.02, 0.96, 0.96]]
643
  elif num_images == 2:
 
644
  positions = [[0.02, 0.02, 0.47, 0.96], [0.51, 0.02, 0.47, 0.96]]
645
  elif num_images == 3:
 
646
  positions = [[0.02, 0.2, 0.31, 0.6], [0.345, 0.2, 0.31, 0.6], [0.67, 0.2, 0.31, 0.6]]
647
  elif num_images == 4:
 
648
  positions = [[0.02, 0.02, 0.47, 0.47], [0.51, 0.02, 0.47, 0.47],
649
  [0.02, 0.51, 0.47, 0.47], [0.51, 0.51, 0.47, 0.47]]
650
  elif num_images == 5:
 
651
  positions = [[0.02, 0.02, 0.96, 0.44], [0.02, 0.48, 0.31, 0.5], [0.345, 0.48, 0.31, 0.5],
652
  [0.67, 0.48, 0.31, 0.24], [0.67, 0.74, 0.31, 0.24]]
653
  elif num_images == 6:
 
654
  positions = [[0.02, 0.02, 0.47, 0.31], [0.51, 0.02, 0.47, 0.31],
655
  [0.02, 0.345, 0.47, 0.31], [0.51, 0.345, 0.47, 0.31],
656
  [0.02, 0.67, 0.47, 0.31], [0.51, 0.67, 0.47, 0.31]]
657
+ elif num_images == 7:
658
+ positions = [[0.28, 0.02, 0.44, 0.3], [0.02, 0.25, 0.3, 0.25], [0.68, 0.25, 0.3, 0.25],
659
+ [0.25, 0.35, 0.5, 0.3], [0.02, 0.52, 0.3, 0.25], [0.68, 0.52, 0.3, 0.25],
660
+ [0.28, 0.68, 0.44, 0.3]]
661
+ elif num_images == 8:
662
+ positions = [[0.02, 0.02, 0.23, 0.47], [0.27, 0.02, 0.23, 0.47], [0.52, 0.02, 0.23, 0.47],
663
+ [0.77, 0.02, 0.21, 0.47], [0.02, 0.51, 0.23, 0.47], [0.27, 0.51, 0.23, 0.47],
664
+ [0.52, 0.51, 0.23, 0.47], [0.77, 0.51, 0.21, 0.47]]
665
  else:
 
666
  positions = [[0.02, 0.02, 0.96, 0.96]]
667
  else:
668
  positions = layout["positions"]
 
677
  # Add small padding between panels (1% of page dimensions)
678
  padding = 0.01
679
 
 
 
 
680
  # Apply padding to prevent images from touching edges
681
  if x_rel < padding:
682
  x_rel = padding
 
694
  width = w_rel * page_width
695
  height = h_rel * page_height
696
 
697
+ # Calculate aspect ratios for comparison
698
  img_aspect = image.width / image.height
699
  layout_aspect = width / height
700
+ aspect_diff = abs(img_aspect - layout_aspect) / layout_aspect
701
+
702
+ # If aspect ratios match closely (within 2%), fill the space completely
703
+ # Otherwise, preserve aspect ratio to avoid distortion
704
+ if aspect_diff < 0.02: # Less than 2% difference
705
+ # Aspect ratios match well - fill the space completely
706
  actual_width = width
 
 
 
 
 
 
 
 
707
  actual_height = height
708
+ actual_x = x
709
  actual_y = y
710
+ else:
711
+ # Significant aspect ratio difference - preserve it to avoid distortion
712
+ if img_aspect > layout_aspect:
713
+ # Image is wider than the layout space
714
+ new_height = width / img_aspect
715
+ y_offset = (height - new_height) / 2
716
+ actual_width = width
717
+ actual_height = new_height
718
+ actual_x = x
719
+ actual_y = y + y_offset
720
+ else:
721
+ # Image is taller than the layout space
722
+ new_width = height * img_aspect
723
+ x_offset = (width - new_width) / 2
724
+ actual_width = new_width
725
+ actual_height = height
726
+ actual_x = x + x_offset
727
+ actual_y = y
728
 
729
  # Convert PIL image to format suitable for ReportLab
730
  img_buffer = io.BytesIO()
 
731
  image.save(img_buffer, format='JPEG', quality=95)
732
  img_buffer.seek(0)
733
 
734
+ # Draw the image on the PDF
735
+ # When aspect ratios match (aspect_diff < 0.02), we fill completely
736
+ # Otherwise we preserve aspect ratio to prevent distortion
737
  pdf.drawImage(ImageReader(img_buffer), actual_x, actual_y,
738
  width=actual_width, height=actual_height,
739
+ preserveAspectRatio=(aspect_diff >= 0.02), mask='auto')
740
 
741
  # Save the PDF
742
  pdf.save()
 
784
  return str(pdf_path)
785
 
786
  # --- Main Inference Function (with session support) ---
787
+ @spaces.GPU(duration=240) # Increased duration for up to 8 images
788
  def infer_page(
789
  prompt,
790
  guidance_scale=1.0,
 
806
  num_inference_steps (int): The number of denoising steps.
807
  style_preset (str): The key of the style preset to apply.
808
  custom_style_text (str): Custom style text when 'no_style' is selected.
809
+ num_images (int): Number of images to generate (1-8).
810
  layout (str): The layout ID for arranging images in the PDF.
811
+ max_resolution: Maximum resolution for any dimension.
812
  session_state: Current session state dictionary.
813
  progress (gr.Progress): A Gradio Progress object to track generation.
814
 
 
832
  generated_images = []
833
  used_seeds = []
834
 
835
+ # Get panel metadata for this layout
836
+ panel_metadata = get_layout_metadata(layout, int(num_images))
837
+
838
+ # Debug: print metadata
839
+ print(f"\n=== LAYOUT METADATA for {layout} with {num_images} images ===")
840
+ for i, meta in enumerate(panel_metadata):
841
+ shot_type = meta.get('shot_type', 'medium').replace('_', ' ').title()
842
+ camera_angle = meta.get('camera_angle', 'eye_level').replace('_', ' ').title()
843
+ print(f"Panel {i+1}/{num_images}: {meta['panel_type']} | Focus: {meta['focus']} | {meta['composition']} | {shot_type} @ {camera_angle}")
844
+ print("=" * 80 + "\n")
845
+
846
+ # Generate story scenes with metadata
847
  progress(0, f"Generating story with {num_images} scenes...")
848
+ scenes = generate_story_scenes(prompt, int(num_images), style_preset, panel_metadata)
849
 
850
  # Generate the requested number of images
851
  for i in range(int(num_images)):
 
859
  # Use scene caption and dialogue for this image
860
  scene_prompt = scenes[i]['caption']
861
  scene_dialogue = scenes[i]['dialogue']
862
+
863
+ # Get metadata for this panel
864
+ panel_meta = panel_metadata[i] if i < len(panel_metadata) else {}
865
+
866
+ # Debug output
867
+ print(f"\n--- Generating Panel {i+1}/{num_images} ---")
868
+ print(f"Type: {panel_meta.get('panel_type', 'unknown')}")
869
+ print(f"Focus: {panel_meta.get('focus', 'unknown')}")
870
+ print(f"Composition: {panel_meta.get('composition', 'unknown')}")
871
+ shot_display = panel_meta.get('shot_type', 'medium').replace('_', ' ').title()
872
+ angle_display = panel_meta.get('camera_angle', 'eye_level').replace('_', ' ').title()
873
+ print(f"Shot: {shot_display} at {angle_display} angle")
874
+ print(f"Caption: {scene_prompt[:100]}..." if len(scene_prompt) > 100 else f"Caption: {scene_prompt}")
875
+ print(f"Dialogue: {scene_dialogue if scene_dialogue else '(none)'}")
876
+ print("-" * 80)
877
 
878
  # Generate single image with automatic aspect ratio
879
  image, used_seed = infer_single_auto(
 
923
  num_images=1,
924
  guidance_scale=1.0,
925
  num_inference_steps=8,
926
+ dialogue="",
927
  style_preset="no_style",
928
  custom_style_text="",
929
+ max_resolution=1024,
930
  ):
931
  """
932
  Generates an image with automatically determined aspect ratio based on layout position.
 
936
 
937
  # Automatically determine image size based on position with custom max resolution
938
  width, height = get_image_size_for_position(position_data, image_index, num_images, max_resolution)
939
+
940
+ # Calculate layout aspect ratio for verification
941
+ if position_data:
942
+ x_rel, y_rel, w_rel, h_rel = position_data
943
+ layout_aspect = w_rel / h_rel if h_rel > 0 else 1.0
944
+ image_aspect = width / height
945
+ aspect_error = abs(image_aspect - layout_aspect) / layout_aspect * 100
946
+ print(f"Image {image_index + 1}/{num_images}: Layout aspect={layout_aspect:.4f}, Image aspect={image_aspect:.4f}, Error={aspect_error:.2f}%")
947
+
948
  # Set up the generator for reproducibility
949
  generator = torch.Generator(device="cuda").manual_seed(seed)
950
 
 
957
 
958
  # Add dialogue to the prompt if present
959
  if dialogue and dialogue.strip():
 
960
  styled_prompt = f"{styled_prompt}. {dialogue.strip()}"
961
 
962
  # Use style negative prompt if available, otherwise default
 
974
  height=height,
975
  num_inference_steps=num_inference_steps,
976
  generator=generator,
977
+ true_cfg_scale=guidance_scale,
978
  ).images[0]
979
 
980
  # Convert to grayscale if using manga_no_color style
981
  if style_preset == "manga_no_color":
 
982
  image = image.convert('L').convert('RGB')
983
 
984
  return image, seed
 
1142
  num_images_slider = gr.Slider(
1143
  label="Images per page",
1144
  minimum=1,
1145
+ maximum=8,
1146
  step=1,
1147
  value=1,
1148
+ info="Number of images to generate for the PDF (1-8)"
1149
  )
1150
 
1151
  # Page layout dropdown
 
1194
  with gr.Accordion("Examples", open=True):
1195
  styled_examples = [
1196
  ["A capybara wearing a suit holding a sign that reads Hello World", "no_style", "", 1],
1197
+ ["Two astronauts discovering alien technology on Mars", "flying_saucer", "", 2],
1198
+ ["Detective solving a mystery in a noir city", "manga_no_color", "", 3],
1199
+ ["Epic battle between robots and monsters", "american_comic_90", "", 4],
1200
+ ["Journey through an enchanted forest", "franco_belgian", "", 5],
1201
+ ["Space station crew dealing with an emergency", "render", "", 6],
1202
+ ["Medieval knights on a quest", "medieval", "", 7],
1203
+ ["Superhero team assembling for final battle", "american_comic_90", "", 8],
1204
  ]
1205
 
1206
  gr.Examples(
1207
  examples=styled_examples,
1208
  inputs=[prompt, style_preset, custom_style_text, num_images_slider],
1209
+ outputs=None,
1210
  fn=None,
1211
  cache_examples=False
1212
  )
page_layouts.yaml CHANGED
@@ -1,245 +1,1442 @@
1
  # Page layouts configuration for multi-image PDF generation
2
  # Each layout defines how images are arranged on a page
3
  # Positions are defined as (x, y, width, height) in relative units (0-1)
 
 
 
 
 
 
 
 
4
 
5
  layouts:
6
  1_image:
7
- - id: "full_page"
8
- label: "Full Page"
9
- description: "Single image covering the full page"
10
  positions:
11
- - [0.02, 0.02, 0.96, 0.96] # x, y, width, height (2% margins)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
12
 
13
  2_images:
14
  - id: "horizontal_split"
15
- label: "Layout A - Horizontal Split"
16
- description: "Two images side by side"
17
  positions:
18
- - [0.02, 0.02, 0.47, 0.96] # Left image
19
- - [0.51, 0.02, 0.47, 0.96] # Right image
 
 
 
20
 
21
  - id: "vertical_split"
22
- label: "Layout B - Vertical Split"
23
- description: "Two images stacked vertically"
24
  positions:
25
- - [0.02, 0.02, 0.96, 0.47] # Top image
26
- - [0.02, 0.51, 0.96, 0.47] # Bottom image
 
 
 
27
 
28
- - id: "dominant_left"
29
- label: "Layout C - Large Left"
30
- description: "Large image on left, small on right"
31
  positions:
32
- - [0.02, 0.02, 0.65, 0.96] # Large left image
33
- - [0.69, 0.2, 0.29, 0.6] # Small right image
 
 
 
34
 
35
- - id: "dominant_top"
36
- label: "Layout D - Large Top"
37
- description: "Large image on top, small on bottom"
38
  positions:
39
- - [0.02, 0.02, 0.96, 0.65] # Large top image
40
- - [0.2, 0.69, 0.6, 0.29] # Small bottom image
 
 
 
41
 
42
- 3_images:
43
- - id: "grid_horizontal"
44
- label: "Layout A - Horizontal Strip"
45
- description: "Three images in a row"
46
  positions:
47
- - [0.02, 0.2, 0.31, 0.6] # Left
48
- - [0.345, 0.2, 0.31, 0.6] # Middle
49
- - [0.67, 0.2, 0.31, 0.6] # Right
 
 
50
 
51
- - id: "grid_vertical"
52
- label: "Layout B - Vertical Strip"
53
- description: "Three images in a column"
 
54
  positions:
55
- - [0.2, 0.02, 0.6, 0.31] # Top
56
- - [0.2, 0.345, 0.6, 0.31] # Middle
57
- - [0.2, 0.67, 0.6, 0.31] # Bottom
 
 
 
 
58
 
59
  - id: "hero_top"
60
- label: "Layout C - Hero Top"
61
- description: "Large image on top, two small below"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
62
  positions:
63
- - [0.02, 0.02, 0.96, 0.55] # Large top
64
- - [0.02, 0.59, 0.47, 0.39] # Bottom left
65
- - [0.51, 0.59, 0.47, 0.39] # Bottom right
 
 
 
 
66
 
67
- - id: "hero_left"
68
- label: "Layout D - Hero Left"
69
- description: "Large image on left, two small on right"
70
  positions:
71
- - [0.02, 0.02, 0.55, 0.96] # Large left
72
- - [0.59, 0.02, 0.39, 0.47] # Top right
73
- - [0.59, 0.51, 0.39, 0.47] # Bottom right
 
 
 
 
74
 
75
- - id: "diagonal"
76
- label: "Layout E - Diagonal"
77
- description: "Diagonal arrangement"
78
  positions:
79
- - [0.05, 0.05, 0.4, 0.4] # Top left
80
- - [0.3, 0.3, 0.4, 0.4] # Center
81
- - [0.55, 0.55, 0.4, 0.4] # Bottom right
 
 
 
 
82
 
83
  4_images:
84
  - id: "grid_2x2"
85
- label: "Layout A - 2x2 Grid"
86
- description: "Four equal images in a grid"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
87
  positions:
88
- - [0.02, 0.02, 0.47, 0.47] # Top left
89
- - [0.51, 0.02, 0.47, 0.47] # Top right
90
- - [0.02, 0.51, 0.47, 0.47] # Bottom left
91
- - [0.51, 0.51, 0.47, 0.47] # Bottom right
 
 
 
 
 
 
 
 
 
92
 
93
- - id: "strip_horizontal"
94
- label: "Layout B - Horizontal Strip"
95
- description: "Four images in a row"
96
  positions:
97
- - [0.05, 0.3, 0.2125, 0.4] # First
98
- - [0.2875, 0.3, 0.2125, 0.4] # Second
99
- - [0.525, 0.3, 0.2125, 0.4] # Third
100
- - [0.7625, 0.3, 0.2125, 0.4] # Fourth
 
 
 
 
 
 
 
 
 
101
 
102
- - id: "strip_vertical"
103
- label: "Layout C - Vertical Strip"
104
- description: "Four images in a column"
105
  positions:
106
- - [0.3, 0.05, 0.4, 0.2125] # First
107
- - [0.3, 0.2875, 0.4, 0.2125] # Second
108
- - [0.3, 0.525, 0.4, 0.2125] # Third
109
- - [0.3, 0.7625, 0.4, 0.2125] # Fourth
 
 
 
 
 
 
 
 
 
110
 
111
- - id: "hero_with_strip"
112
- label: "Layout D - Hero with Strip"
113
- description: "One large image with three small ones"
114
  positions:
115
- - [0.05, 0.05, 0.6, 0.6] # Large main
116
- - [0.7, 0.05, 0.25, 0.283] # Small top
117
- - [0.7, 0.358, 0.25, 0.283] # Small middle
118
- - [0.7, 0.666, 0.25, 0.283] # Small bottom
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
119
 
120
  - id: "l_shape"
121
- label: "Layout E - L Shape"
122
- description: "L-shaped arrangement"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
123
  positions:
124
- - [0.05, 0.05, 0.425, 0.425] # Top left (large)
125
- - [0.525, 0.05, 0.425, 0.425] # Top right (large)
126
- - [0.05, 0.525, 0.425, 0.425] # Bottom left
127
- - [0.525, 0.7, 0.425, 0.25] # Bottom right (small)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
128
 
129
  5_images:
130
- - id: "us_comic_action"
131
- label: "US Comic - Action Scene"
132
- description: "Classic American superhero comic layout with large establishing shot"
133
- positions:
134
- - [0.02, 0.02, 0.96, 0.44] # Wide establishing shot (panoramic)
135
- - [0.02, 0.48, 0.31, 0.5] # Action panel 1
136
- - [0.345, 0.48, 0.31, 0.5] # Action panel 2
137
- - [0.67, 0.48, 0.31, 0.24] # Close-up 1
138
- - [0.67, 0.74, 0.31, 0.24] # Close-up 2
139
-
140
- - id: "manga_vertical_flow"
141
- label: "Manga - Vertical Flow"
142
- description: "Japanese manga style with vertical reading flow"
143
- positions:
144
- - [0.51, 0.02, 0.47, 0.38] # Top right (read first in manga)
145
- - [0.02, 0.02, 0.47, 0.38] # Top left
146
- - [0.51, 0.42, 0.47, 0.28] # Middle right
147
- - [0.02, 0.42, 0.47, 0.28] # Middle left
148
- - [0.02, 0.72, 0.96, 0.26] # Bottom wide panel
149
-
150
- - id: "euro_bd_grid"
151
- label: "European BD - Clear Grid"
152
- description: "Franco-Belgian clear line style with regular panels"
153
- positions:
154
- - [0.02, 0.02, 0.47, 0.31] # Row 1 left
155
- - [0.51, 0.02, 0.47, 0.31] # Row 1 right
156
- - [0.02, 0.345, 0.96, 0.31] # Row 2 wide
157
- - [0.02, 0.67, 0.47, 0.31] # Row 3 left
158
- - [0.51, 0.67, 0.47, 0.31] # Row 3 right
159
-
160
- - id: "diagonal_dynamic"
161
- label: "Dynamic Diagonal"
162
- description: "Action-oriented diagonal composition"
163
- positions:
164
- - [0.05, 0.05, 0.5, 0.4] # Large top left
165
- - [0.6, 0.05, 0.35, 0.25] # Small top right
166
- - [0.3, 0.35, 0.4, 0.3] # Center focus
167
- - [0.05, 0.7, 0.35, 0.25] # Bottom left
168
- - [0.6, 0.7, 0.35, 0.25] # Bottom right
169
-
170
- - id: "spiral_focus"
171
- label: "Spiral Focus"
172
- description: "Panels arranged in a spiral leading to center"
173
- positions:
174
- - [0.05, 0.05, 0.35, 0.35] # Top left
175
- - [0.425, 0.05, 0.525, 0.25] # Top wide
176
- - [0.7, 0.35, 0.25, 0.6] # Right tall
177
- - [0.425, 0.7, 0.525, 0.25] # Bottom wide
178
- - [0.25, 0.35, 0.4, 0.3] # Center focus
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
179
 
180
  6_images:
181
- - id: "classic_comic_grid"
182
- label: "Classic Comic Grid"
183
- description: "Traditional 2x3 American comic book grid"
184
- positions:
185
- - [0.02, 0.02, 0.47, 0.31] # Row 1 left
186
- - [0.51, 0.02, 0.47, 0.31] # Row 1 right
187
- - [0.02, 0.345, 0.47, 0.31] # Row 2 left
188
- - [0.51, 0.345, 0.47, 0.31] # Row 2 right
189
- - [0.02, 0.67, 0.47, 0.31] # Row 3 left
190
- - [0.51, 0.67, 0.47, 0.31] # Row 3 right
191
-
192
- - id: "manga_4koma"
193
- label: "Manga - 4-Koma Plus"
194
- description: "Japanese 4-panel strip with header and footer"
195
- positions:
196
- - [0.02, 0.02, 0.96, 0.16] # Header panel
197
- - [0.02, 0.2, 0.47, 0.23] # Strip 1
198
- - [0.51, 0.2, 0.47, 0.23] # Strip 2
199
- - [0.02, 0.45, 0.47, 0.23] # Strip 3
200
- - [0.51, 0.45, 0.47, 0.23] # Strip 4
201
- - [0.02, 0.7, 0.96, 0.28] # Footer/punchline
202
-
203
- - id: "euro_bd_cinematic"
204
- label: "European BD - Cinematic"
205
- description: "Cinematic European style with varied panel sizes"
206
- positions:
207
- - [0.02, 0.02, 0.96, 0.28] # Wide establishing
208
- - [0.02, 0.32, 0.31, 0.28] # Small 1
209
- - [0.345, 0.32, 0.31, 0.28] # Small 2
210
- - [0.67, 0.32, 0.31, 0.28] # Small 3
211
- - [0.02, 0.62, 0.47, 0.36] # Medium left
212
- - [0.51, 0.62, 0.47, 0.36] # Medium right
213
-
214
- - id: "action_sequence"
215
- label: "Action Sequence"
216
- description: "Fast-paced action scene layout"
217
- positions:
218
- - [0.02, 0.02, 0.65, 0.38] # Large action shot
219
- - [0.69, 0.02, 0.29, 0.18] # Speed line 1
220
- - [0.69, 0.22, 0.29, 0.18] # Speed line 2
221
- - [0.02, 0.42, 0.31, 0.56] # Vertical impact 1
222
- - [0.345, 0.42, 0.31, 0.56] # Vertical impact 2
223
- - [0.67, 0.42, 0.31, 0.56] # Vertical impact 3
224
-
225
- - id: "storytelling_flow"
226
- label: "Storytelling Flow"
227
- description: "Natural reading flow for narrative scenes"
228
- positions:
229
- - [0.05, 0.05, 0.425, 0.25] # Scene 1
230
- - [0.525, 0.05, 0.425, 0.25] # Scene 2
231
- - [0.05, 0.35, 0.9, 0.2] # Wide transition
232
- - [0.05, 0.6, 0.425, 0.35] # Scene 3
233
- - [0.525, 0.6, 0.425, 0.175] # Scene 4a
234
- - [0.525, 0.8, 0.425, 0.175] # Scene 4b
235
-
236
- - id: "focus_surround"
237
- label: "Focus with Details"
238
- description: "Central focus with surrounding detail panels"
239
- positions:
240
- - [0.25, 0.25, 0.5, 0.5] # Large center focus
241
- - [0.05, 0.05, 0.35, 0.15] # Top left detail
242
- - [0.6, 0.05, 0.35, 0.15] # Top right detail
243
- - [0.05, 0.8, 0.35, 0.15] # Bottom left detail
244
- - [0.6, 0.8, 0.35, 0.15] # Bottom right detail
245
- - [0.05, 0.4, 0.15, 0.3] # Left side detail
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  # Page layouts configuration for multi-image PDF generation
2
  # Each layout defines how images are arranged on a page
3
  # Positions are defined as (x, y, width, height) in relative units (0-1)
4
+ # Coordinate system: (0,0) is top-left, X increases right, Y increases down
5
+ #
6
+ # Panel metadata helps guide image generation:
7
+ # - panel_type: establishing/action/closeup/dialogue/reaction/transition/detail/splash
8
+ # - focus: environment/character/characters/action/emotion/object/event
9
+ # - composition: wide/tall/square/portrait/landscape
10
+ # - shot_type: extreme_wide/wide/full/medium_full/medium/medium_closeup/closeup/extreme_closeup
11
+ # - camera_angle: eye_level/high_angle/low_angle/overhead/dutch_angle/over_shoulder/pov
12
 
13
  layouts:
14
  1_image:
15
+ - id: "full_bleed"
16
+ label: "Full Bleed"
17
+ description: "Single image filling entire page"
18
  positions:
19
+ - [0.0, 0.0, 1.0, 1.0]
20
+ metadata:
21
+ - {panel_type: "splash", focus: "event", composition: "square", shot_type: "full", camera_angle: "eye_level"}
22
+
23
+ - id: "classic_frame"
24
+ label: "Classic Frame"
25
+ description: "Single image with traditional margins"
26
+ positions:
27
+ - [0.05, 0.05, 0.9, 0.9]
28
+ metadata:
29
+ - {panel_type: "establishing", focus: "environment", composition: "square", shot_type: "wide", camera_angle: "high_angle"}
30
+
31
+ - id: "portrait_focus"
32
+ label: "Portrait Focus"
33
+ description: "Vertical emphasis for character shots"
34
+ positions:
35
+ - [0.15, 0.02, 0.7, 0.96]
36
+ metadata:
37
+ - {panel_type: "closeup", focus: "character", composition: "portrait", shot_type: "medium_closeup", camera_angle: "eye_level"}
38
+
39
+ - id: "cinematic_wide"
40
+ label: "Cinematic Wide"
41
+ description: "Wide letterbox format"
42
+ positions:
43
+ - [0.02, 0.25, 0.96, 0.5]
44
+ metadata:
45
+ - {panel_type: "establishing", focus: "environment", composition: "wide", shot_type: "extreme_wide", camera_angle: "eye_level"}
46
+
47
+ - id: "floating_center"
48
+ label: "Floating Center"
49
+ description: "Centered with breathing room"
50
+ positions:
51
+ - [0.1, 0.15, 0.8, 0.7]
52
+ metadata:
53
+ - {panel_type: "dialogue", focus: "character", composition: "square", shot_type: "medium", camera_angle: "eye_level"}
54
 
55
  2_images:
56
  - id: "horizontal_split"
57
+ label: "Even Split"
58
+ description: "Two equal panels side by side"
59
  positions:
60
+ - [0.02, 0.02, 0.47, 0.96]
61
+ - [0.51, 0.02, 0.47, 0.96]
62
+ metadata:
63
+ - {panel_type: "establishing", focus: "environment", composition: "portrait", shot_type: "wide", camera_angle: "eye_level"}
64
+ - {panel_type: "action", focus: "character", composition: "portrait", shot_type: "full", camera_angle: "low_angle"}
65
 
66
  - id: "vertical_split"
67
+ label: "Top & Bottom"
68
+ description: "Stacked panels"
69
  positions:
70
+ - [0.02, 0.02, 0.96, 0.47]
71
+ - [0.02, 0.51, 0.96, 0.47]
72
+ metadata:
73
+ - {panel_type: "establishing", focus: "environment", composition: "wide", shot_type: "extreme_wide", camera_angle: "high_angle"}
74
+ - {panel_type: "action", focus: "event", composition: "wide", shot_type: "full", camera_angle: "eye_level"}
75
 
76
+ - id: "hero_sidekick"
77
+ label: "Hero & Sidekick"
78
+ description: "Large main panel with small detail"
79
  positions:
80
+ - [0.02, 0.02, 0.65, 0.96]
81
+ - [0.69, 0.25, 0.29, 0.5]
82
+ metadata:
83
+ - {panel_type: "action", focus: "character", composition: "portrait", shot_type: "full", camera_angle: "eye_level"}
84
+ - {panel_type: "reaction", focus: "emotion", composition: "portrait", shot_type: "closeup", camera_angle: "eye_level"}
85
 
86
+ - id: "before_after"
87
+ label: "Before & After"
88
+ description: "Cause and effect layout"
89
  positions:
90
+ - [0.02, 0.02, 0.96, 0.44]
91
+ - [0.02, 0.48, 0.96, 0.5]
92
+ metadata:
93
+ - {panel_type: "establishing", focus: "environment", composition: "wide", shot_type: "wide", camera_angle: "eye_level"}
94
+ - {panel_type: "action", focus: "event", composition: "wide", shot_type: "medium_full", camera_angle: "dutch_angle"}
95
 
96
+ - id: "diagonal_tension"
97
+ label: "Diagonal Tension"
98
+ description: "Overlapping dynamic panels"
 
99
  positions:
100
+ - [0.02, 0.02, 0.6, 0.6]
101
+ - [0.38, 0.38, 0.6, 0.6]
102
+ metadata:
103
+ - {panel_type: "action", focus: "event", composition: "square", shot_type: "medium", camera_angle: "high_angle"}
104
+ - {panel_type: "action", focus: "event", composition: "square", shot_type: "medium", camera_angle: "low_angle"}
105
 
106
+ 3_images:
107
+ - id: "triptych"
108
+ label: "Triptych"
109
+ description: "Three equal vertical panels"
110
  positions:
111
+ - [0.02, 0.02, 0.31, 0.96]
112
+ - [0.345, 0.02, 0.31, 0.96]
113
+ - [0.67, 0.02, 0.31, 0.96]
114
+ metadata:
115
+ - {panel_type: "establishing", focus: "environment", composition: "portrait", shot_type: "wide", camera_angle: "eye_level"}
116
+ - {panel_type: "action", focus: "character", composition: "portrait", shot_type: "full", camera_angle: "eye_level"}
117
+ - {panel_type: "reaction", focus: "emotion", composition: "portrait", shot_type: "medium_closeup", camera_angle: "eye_level"}
118
 
119
  - id: "hero_top"
120
+ label: "Establishing Shot"
121
+ description: "Large top panel with details below"
122
+ positions:
123
+ - [0.02, 0.02, 0.96, 0.5]
124
+ - [0.02, 0.54, 0.47, 0.44]
125
+ - [0.51, 0.54, 0.47, 0.44]
126
+ metadata:
127
+ - {panel_type: "establishing", focus: "environment", composition: "wide", shot_type: "extreme_wide", camera_angle: "high_angle"}
128
+ - {panel_type: "action", focus: "character", composition: "square", shot_type: "medium", camera_angle: "eye_level"}
129
+ - {panel_type: "reaction", focus: "emotion", composition: "square", shot_type: "closeup", camera_angle: "eye_level"}
130
+
131
+ - id: "l_shape"
132
+ label: "L-Shape Flow"
133
+ description: "Reading flow in L pattern"
134
+ positions:
135
+ - [0.02, 0.02, 0.47, 0.47]
136
+ - [0.51, 0.02, 0.47, 0.47]
137
+ - [0.02, 0.51, 0.96, 0.47]
138
+ metadata:
139
+ - {panel_type: "dialogue", focus: "character", composition: "square", shot_type: "medium_closeup", camera_angle: "eye_level"}
140
+ - {panel_type: "dialogue", focus: "character", composition: "square", shot_type: "medium_closeup", camera_angle: "over_shoulder"}
141
+ - {panel_type: "action", focus: "event", composition: "wide", shot_type: "full", camera_angle: "eye_level"}
142
+
143
+ - id: "spotlight"
144
+ label: "Spotlight"
145
+ description: "Central focus with side panels"
146
  positions:
147
+ - [0.02, 0.15, 0.28, 0.7]
148
+ - [0.32, 0.02, 0.36, 0.96]
149
+ - [0.7, 0.15, 0.28, 0.7]
150
+ metadata:
151
+ - {panel_type: "reaction", focus: "character", composition: "portrait", shot_type: "medium_closeup", camera_angle: "eye_level"}
152
+ - {panel_type: "action", focus: "character", composition: "portrait", shot_type: "full", camera_angle: "low_angle"}
153
+ - {panel_type: "reaction", focus: "character", composition: "portrait", shot_type: "closeup", camera_angle: "eye_level"}
154
 
155
+ - id: "vertical_flow"
156
+ label: "Vertical Flow"
157
+ description: "Three stacked panels for sequential action"
158
  positions:
159
+ - [0.02, 0.02, 0.96, 0.31]
160
+ - [0.02, 0.345, 0.96, 0.31]
161
+ - [0.02, 0.67, 0.96, 0.31]
162
+ metadata:
163
+ - {panel_type: "establishing", focus: "environment", composition: "wide", shot_type: "wide", camera_angle: "eye_level"}
164
+ - {panel_type: "action", focus: "event", composition: "wide", shot_type: "medium_full", camera_angle: "eye_level"}
165
+ - {panel_type: "action", focus: "event", composition: "wide", shot_type: "medium", camera_angle: "dutch_angle"}
166
 
167
+ - id: "manga_action"
168
+ label: "Manga Action"
169
+ description: "Dynamic manga-style layout"
170
  positions:
171
+ - [0.52, 0.02, 0.46, 0.45]
172
+ - [0.02, 0.02, 0.48, 0.65]
173
+ - [0.02, 0.69, 0.96, 0.29]
174
+ metadata:
175
+ - {panel_type: "reaction", focus: "emotion", composition: "square", shot_type: "closeup", camera_angle: "low_angle"}
176
+ - {panel_type: "action", focus: "character", composition: "portrait", shot_type: "full", camera_angle: "low_angle"}
177
+ - {panel_type: "action", focus: "event", composition: "wide", shot_type: "wide", camera_angle: "overhead"}
178
 
179
  4_images:
180
  - id: "grid_2x2"
181
+ label: "Classic Grid"
182
+ description: "Traditional 2x2 layout"
183
+ positions:
184
+ - [0.02, 0.02, 0.47, 0.47]
185
+ - [0.51, 0.02, 0.47, 0.47]
186
+ - [0.02, 0.51, 0.47, 0.47]
187
+ - [0.51, 0.51, 0.47, 0.47]
188
+ metadata:
189
+ - {panel_type: "establishing", focus: "environment", composition: "square", shot_type: "wide", camera_angle: "high_angle"}
190
+ - {panel_type: "dialogue", focus: "character", composition: "square", shot_type: "medium_closeup", camera_angle: "eye_level"}
191
+ - {panel_type: "action", focus: "event", composition: "square", shot_type: "medium", camera_angle: "eye_level"}
192
+ - {panel_type: "reaction", focus: "emotion", composition: "square", shot_type: "closeup", camera_angle: "eye_level"}
193
+
194
+ - id: "widescreen"
195
+ label: "Widescreen Strips"
196
+ description: "Four cinematic horizontal strips"
197
+ positions:
198
+ - [0.02, 0.02, 0.96, 0.23]
199
+ - [0.02, 0.27, 0.96, 0.23]
200
+ - [0.02, 0.52, 0.96, 0.23]
201
+ - [0.02, 0.77, 0.96, 0.21]
202
+ metadata:
203
+ - {panel_type: "establishing", focus: "environment", composition: "wide", shot_type: "extreme_wide", camera_angle: "eye_level"}
204
+ - {panel_type: "dialogue", focus: "characters", composition: "wide", shot_type: "medium", camera_angle: "eye_level"}
205
+ - {panel_type: "action", focus: "event", composition: "wide", shot_type: "full", camera_angle: "low_angle"}
206
+ - {panel_type: "reaction", focus: "emotion", composition: "wide", shot_type: "medium_closeup", camera_angle: "eye_level"}
207
+
208
+ - id: "hero_cluster"
209
+ label: "Hero with Cluster"
210
+ description: "Large panel with three supporting"
211
+ positions:
212
+ - [0.02, 0.02, 0.6, 0.63]
213
+ - [0.64, 0.02, 0.34, 0.3]
214
+ - [0.64, 0.34, 0.34, 0.31]
215
+ - [0.02, 0.67, 0.96, 0.31]
216
+ metadata:
217
+ - {panel_type: "action", focus: "event", composition: "square", shot_type: "full", camera_angle: "low_angle"}
218
+ - {panel_type: "detail", focus: "object", composition: "landscape", shot_type: "extreme_closeup", camera_angle: "eye_level"}
219
+ - {panel_type: "reaction", focus: "emotion", composition: "landscape", shot_type: "closeup", camera_angle: "eye_level"}
220
+ - {panel_type: "transition", focus: "environment", composition: "wide", shot_type: "wide", camera_angle: "eye_level"}
221
+
222
+ - id: "comic_strip"
223
+ label: "Comic Strip"
224
+ description: "Newspaper strip style"
225
+ positions:
226
+ - [0.02, 0.3, 0.23, 0.4]
227
+ - [0.27, 0.3, 0.23, 0.4]
228
+ - [0.52, 0.3, 0.23, 0.4]
229
+ - [0.77, 0.3, 0.21, 0.4]
230
+ metadata:
231
+ - {panel_type: "establishing", focus: "environment", composition: "portrait", shot_type: "full", camera_angle: "eye_level"}
232
+ - {panel_type: "dialogue", focus: "characters", composition: "portrait", shot_type: "medium", camera_angle: "eye_level"}
233
+ - {panel_type: "action", focus: "event", composition: "portrait", shot_type: "medium", camera_angle: "eye_level"}
234
+ - {panel_type: "reaction", focus: "emotion", composition: "portrait", shot_type: "closeup", camera_angle: "eye_level"}
235
+
236
+ - id: "z_pattern"
237
+ label: "Z-Pattern"
238
+ description: "Natural reading flow in Z shape"
239
+ positions:
240
+ - [0.02, 0.02, 0.47, 0.35]
241
+ - [0.51, 0.02, 0.47, 0.35]
242
+ - [0.02, 0.39, 0.47, 0.59]
243
+ - [0.51, 0.39, 0.47, 0.59]
244
+ metadata:
245
+ - {panel_type: "establishing", focus: "environment", composition: "landscape", shot_type: "wide", camera_angle: "high_angle"}
246
+ - {panel_type: "dialogue", focus: "character", composition: "landscape", shot_type: "medium", camera_angle: "eye_level"}
247
+ - {panel_type: "action", focus: "character", composition: "portrait", shot_type: "full", camera_angle: "low_angle"}
248
+ - {panel_type: "action", focus: "event", composition: "portrait", shot_type: "medium_full", camera_angle: "dutch_angle"}
249
+
250
+ - id: "explosion"
251
+ label: "Explosion"
252
+ description: "Central impact with surrounding panels"
253
+ positions:
254
+ - [0.02, 0.02, 0.35, 0.35]
255
+ - [0.63, 0.02, 0.35, 0.35]
256
+ - [0.27, 0.27, 0.46, 0.46]
257
+ - [0.02, 0.63, 0.96, 0.35]
258
+ metadata:
259
+ - {panel_type: "establishing", focus: "environment", composition: "square", shot_type: "wide", camera_angle: "eye_level"}
260
+ - {panel_type: "detail", focus: "object", composition: "square", shot_type: "extreme_closeup", camera_angle: "overhead"}
261
+ - {panel_type: "action", focus: "event", composition: "square", shot_type: "medium", camera_angle: "low_angle"}
262
+ - {panel_type: "reaction", focus: "characters", composition: "wide", shot_type: "medium_full", camera_angle: "eye_level"}
263
+
264
+ 5_images:
265
+ - id: "hero_banner"
266
+ label: "Hero Banner"
267
+ description: "Wide establishing shot with four panels below"
268
+ positions:
269
+ - [0.02, 0.02, 0.96, 0.38]
270
+ - [0.02, 0.42, 0.47, 0.28]
271
+ - [0.51, 0.42, 0.47, 0.28]
272
+ - [0.02, 0.72, 0.47, 0.26]
273
+ - [0.51, 0.72, 0.47, 0.26]
274
+ metadata:
275
+ - {panel_type: "establishing", focus: "environment", composition: "wide", shot_type: "extreme_wide", camera_angle: "high_angle"}
276
+ - {panel_type: "dialogue", focus: "character", composition: "landscape", shot_type: "medium_closeup", camera_angle: "eye_level"}
277
+ - {panel_type: "dialogue", focus: "character", composition: "landscape", shot_type: "medium_closeup", camera_angle: "over_shoulder"}
278
+ - {panel_type: "action", focus: "event", composition: "landscape", shot_type: "medium", camera_angle: "eye_level"}
279
+ - {panel_type: "reaction", focus: "emotion", composition: "landscape", shot_type: "closeup", camera_angle: "eye_level"}
280
+
281
+ - id: "manga_vertical"
282
+ label: "Manga Vertical"
283
+ description: "Japanese right-to-left vertical flow"
284
+ positions:
285
+ - [0.52, 0.02, 0.46, 0.32]
286
+ - [0.02, 0.02, 0.48, 0.32]
287
+ - [0.52, 0.36, 0.46, 0.3]
288
+ - [0.02, 0.36, 0.48, 0.3]
289
+ - [0.02, 0.68, 0.96, 0.3]
290
+ metadata:
291
+ - {panel_type: "establishing", focus: "environment", composition: "landscape", shot_type: "wide", camera_angle: "eye_level"}
292
+ - {panel_type: "dialogue", focus: "character", composition: "landscape", shot_type: "medium", camera_angle: "eye_level"}
293
+ - {panel_type: "action", focus: "character", composition: "landscape", shot_type: "full", camera_angle: "low_angle"}
294
+ - {panel_type: "reaction", focus: "emotion", composition: "landscape", shot_type: "closeup", camera_angle: "eye_level"}
295
+ - {panel_type: "action", focus: "event", composition: "wide", shot_type: "full", camera_angle: "overhead"}
296
+
297
+ - id: "pyramid"
298
+ label: "Pyramid"
299
+ description: "Building tension from top to bottom"
300
+ positions:
301
+ - [0.25, 0.02, 0.5, 0.25]
302
+ - [0.02, 0.29, 0.47, 0.3]
303
+ - [0.51, 0.29, 0.47, 0.3]
304
+ - [0.02, 0.61, 0.31, 0.37]
305
+ - [0.67, 0.61, 0.31, 0.37]
306
+ metadata:
307
+ - {panel_type: "establishing", focus: "environment", composition: "landscape", shot_type: "wide", camera_angle: "high_angle"}
308
+ - {panel_type: "dialogue", focus: "character", composition: "landscape", shot_type: "medium", camera_angle: "eye_level"}
309
+ - {panel_type: "dialogue", focus: "character", composition: "landscape", shot_type: "medium_closeup", camera_angle: "over_shoulder"}
310
+ - {panel_type: "action", focus: "event", composition: "portrait", shot_type: "full", camera_angle: "low_angle"}
311
+ - {panel_type: "action", focus: "event", composition: "portrait", shot_type: "medium", camera_angle: "dutch_angle"}
312
+
313
+ - id: "spotlight_drama"
314
+ label: "Spotlight Drama"
315
+ description: "Central focus with corner details"
316
+ positions:
317
+ - [0.02, 0.02, 0.35, 0.35]
318
+ - [0.63, 0.02, 0.35, 0.35]
319
+ - [0.22, 0.22, 0.56, 0.56]
320
+ - [0.02, 0.63, 0.35, 0.35]
321
+ - [0.63, 0.63, 0.35, 0.35]
322
+ metadata:
323
+ - {panel_type: "detail", focus: "object", composition: "square", shot_type: "extreme_closeup", camera_angle: "overhead"}
324
+ - {panel_type: "detail", focus: "object", composition: "square", shot_type: "extreme_closeup", camera_angle: "eye_level"}
325
+ - {panel_type: "action", focus: "character", composition: "square", shot_type: "full", camera_angle: "low_angle"}
326
+ - {panel_type: "reaction", focus: "emotion", composition: "square", shot_type: "closeup", camera_angle: "low_angle"}
327
+ - {panel_type: "reaction", focus: "emotion", composition: "square", shot_type: "closeup", camera_angle: "high_angle"}
328
+
329
+ - id: "euro_bd"
330
+ label: "Euro BD Classic"
331
+ description: "Franco-Belgian structured layout"
332
+ positions:
333
+ - [0.02, 0.02, 0.47, 0.29]
334
+ - [0.51, 0.02, 0.47, 0.29]
335
+ - [0.02, 0.33, 0.96, 0.31]
336
+ - [0.02, 0.66, 0.47, 0.32]
337
+ - [0.51, 0.66, 0.47, 0.32]
338
+ metadata:
339
+ - {panel_type: "establishing", focus: "environment", composition: "landscape", shot_type: "wide", camera_angle: "eye_level"}
340
+ - {panel_type: "dialogue", focus: "character", composition: "landscape", shot_type: "medium", camera_angle: "eye_level"}
341
+ - {panel_type: "action", focus: "event", composition: "wide", shot_type: "full", camera_angle: "eye_level"}
342
+ - {panel_type: "dialogue", focus: "characters", composition: "landscape", shot_type: "medium", camera_angle: "over_shoulder"}
343
+ - {panel_type: "reaction", focus: "emotion", composition: "landscape", shot_type: "medium_closeup", camera_angle: "eye_level"}
344
+
345
+ - id: "action_burst"
346
+ label: "Action Burst"
347
+ description: "Dynamic superhero action layout"
348
+ positions:
349
+ - [0.02, 0.02, 0.55, 0.55]
350
+ - [0.59, 0.02, 0.39, 0.26]
351
+ - [0.59, 0.3, 0.39, 0.27]
352
+ - [0.02, 0.59, 0.47, 0.39]
353
+ - [0.51, 0.59, 0.47, 0.39]
354
+ metadata:
355
+ - {panel_type: "action", focus: "event", composition: "square", shot_type: "full", camera_angle: "low_angle"}
356
+ - {panel_type: "detail", focus: "object", composition: "landscape", shot_type: "extreme_closeup", camera_angle: "eye_level"}
357
+ - {panel_type: "closeup", focus: "emotion", composition: "landscape", shot_type: "closeup", camera_angle: "low_angle"}
358
+ - {panel_type: "action", focus: "character", composition: "landscape", shot_type: "medium_full", camera_angle: "dutch_angle"}
359
+ - {panel_type: "reaction", focus: "characters", composition: "landscape", shot_type: "medium", camera_angle: "eye_level"}
360
+
361
+ 6_images:
362
+ - id: "classic_grid"
363
+ label: "Classic 2x3 Grid"
364
+ description: "Traditional American comic layout"
365
+ positions:
366
+ - [0.02, 0.02, 0.47, 0.31]
367
+ - [0.51, 0.02, 0.47, 0.31]
368
+ - [0.02, 0.345, 0.47, 0.31]
369
+ - [0.51, 0.345, 0.47, 0.31]
370
+ - [0.02, 0.67, 0.47, 0.31]
371
+ - [0.51, 0.67, 0.47, 0.31]
372
+ metadata:
373
+ - {panel_type: "establishing", focus: "environment", composition: "landscape", shot_type: "wide", camera_angle: "high_angle"}
374
+ - {panel_type: "dialogue", focus: "character", composition: "landscape", shot_type: "medium_closeup", camera_angle: "eye_level"}
375
+ - {panel_type: "dialogue", focus: "character", composition: "landscape", shot_type: "medium_closeup", camera_angle: "over_shoulder"}
376
+ - {panel_type: "action", focus: "event", composition: "landscape", shot_type: "medium", camera_angle: "eye_level"}
377
+ - {panel_type: "action", focus: "event", composition: "landscape", shot_type: "full", camera_angle: "low_angle"}
378
+ - {panel_type: "reaction", focus: "emotion", composition: "landscape", shot_type: "closeup", camera_angle: "eye_level"}
379
+
380
+ - id: "hero_surround"
381
+ label: "Hero Surrounded"
382
+ description: "Large central panel with five around it"
383
  positions:
384
+ - [0.02, 0.02, 0.31, 0.28]
385
+ - [0.67, 0.02, 0.31, 0.28]
386
+ - [0.02, 0.7, 0.31, 0.28]
387
+ - [0.67, 0.7, 0.31, 0.28]
388
+ - [0.25, 0.25, 0.5, 0.5]
389
+ - [0.35, 0.35, 0.63, 0.33]
390
+ metadata:
391
+ - {panel_type: "detail", focus: "object", composition: "square", shot_type: "extreme_closeup", camera_angle: "overhead"}
392
+ - {panel_type: "detail", focus: "object", composition: "square", shot_type: "extreme_closeup", camera_angle: "eye_level"}
393
+ - {panel_type: "detail", focus: "object", composition: "square", shot_type: "closeup", camera_angle: "low_angle"}
394
+ - {panel_type: "detail", focus: "character", composition: "square", shot_type: "closeup", camera_angle: "eye_level"}
395
+ - {panel_type: "action", focus: "character", composition: "square", shot_type: "full", camera_angle: "low_angle"}
396
+ - {panel_type: "action", focus: "event", composition: "landscape", shot_type: "medium", camera_angle: "dutch_angle"}
397
 
398
+ - id: "staircase"
399
+ label: "Staircase"
400
+ description: "Diagonal reading flow"
401
  positions:
402
+ - [0.02, 0.02, 0.45, 0.3]
403
+ - [0.49, 0.02, 0.49, 0.3]
404
+ - [0.02, 0.34, 0.45, 0.3]
405
+ - [0.49, 0.34, 0.49, 0.3]
406
+ - [0.02, 0.66, 0.45, 0.32]
407
+ - [0.49, 0.66, 0.49, 0.32]
408
+ metadata:
409
+ - {panel_type: "establishing", focus: "environment", composition: "landscape", shot_type: "wide", camera_angle: "eye_level"}
410
+ - {panel_type: "dialogue", focus: "character", composition: "landscape", shot_type: "medium", camera_angle: "eye_level"}
411
+ - {panel_type: "dialogue", focus: "character", composition: "landscape", shot_type: "medium_closeup", camera_angle: "over_shoulder"}
412
+ - {panel_type: "action", focus: "event", composition: "landscape", shot_type: "medium_full", camera_angle: "eye_level"}
413
+ - {panel_type: "action", focus: "character", composition: "landscape", shot_type: "full", camera_angle: "low_angle"}
414
+ - {panel_type: "reaction", focus: "emotion", composition: "landscape", shot_type: "closeup", camera_angle: "eye_level"}
415
 
416
+ - id: "splash_detail"
417
+ label: "Splash with Details"
418
+ description: "Full-page moment with detail insets"
419
  positions:
420
+ - [0.05, 0.05, 0.4, 0.4]
421
+ - [0.55, 0.05, 0.4, 0.4]
422
+ - [0.05, 0.55, 0.4, 0.4]
423
+ - [0.55, 0.55, 0.4, 0.4]
424
+ - [0.25, 0.25, 0.5, 0.5]
425
+ - [0.35, 0.35, 0.3, 0.3]
426
+ metadata:
427
+ - {panel_type: "detail", focus: "environment", composition: "square", shot_type: "closeup", camera_angle: "eye_level"}
428
+ - {panel_type: "detail", focus: "object", composition: "square", shot_type: "extreme_closeup", camera_angle: "overhead"}
429
+ - {panel_type: "detail", focus: "object", composition: "square", shot_type: "closeup", camera_angle: "low_angle"}
430
+ - {panel_type: "detail", focus: "character", composition: "square", shot_type: "closeup", camera_angle: "eye_level"}
431
+ - {panel_type: "action", focus: "event", composition: "square", shot_type: "full", camera_angle: "low_angle"}
432
+ - {panel_type: "closeup", focus: "emotion", composition: "square", shot_type: "extreme_closeup", camera_angle: "eye_level"}
433
 
434
+ - id: "manga_4koma_plus"
435
+ label: "Manga 4-Koma Plus"
436
+ description: "Japanese 4-panel with header/footer"
437
  positions:
438
+ - [0.02, 0.02, 0.96, 0.15]
439
+ - [0.02, 0.19, 0.47, 0.25]
440
+ - [0.51, 0.19, 0.47, 0.25]
441
+ - [0.02, 0.46, 0.47, 0.25]
442
+ - [0.51, 0.46, 0.47, 0.25]
443
+ - [0.02, 0.73, 0.96, 0.25]
444
+ metadata:
445
+ - {panel_type: "establishing", focus: "environment", composition: "wide", shot_type: "extreme_wide", camera_angle: "eye_level"}
446
+ - {panel_type: "dialogue", focus: "character", composition: "landscape", shot_type: "medium", camera_angle: "eye_level"}
447
+ - {panel_type: "dialogue", focus: "character", composition: "landscape", shot_type: "medium", camera_angle: "over_shoulder"}
448
+ - {panel_type: "action", focus: "event", composition: "landscape", shot_type: "medium_full", camera_angle: "eye_level"}
449
+ - {panel_type: "action", focus: "event", composition: "landscape", shot_type: "full", camera_angle: "dutch_angle"}
450
+ - {panel_type: "reaction", focus: "emotion", composition: "wide", shot_type: "medium_closeup", camera_angle: "eye_level"}
451
+
452
+ - id: "tension_build"
453
+ label: "Tension Builder"
454
+ description: "Progressive revelation layout"
455
+ positions:
456
+ - [0.02, 0.02, 0.96, 0.22]
457
+ - [0.02, 0.26, 0.47, 0.22]
458
+ - [0.51, 0.26, 0.47, 0.22]
459
+ - [0.02, 0.5, 0.31, 0.48]
460
+ - [0.345, 0.5, 0.31, 0.48]
461
+ - [0.67, 0.5, 0.31, 0.48]
462
+ metadata:
463
+ - {panel_type: "establishing", focus: "environment", composition: "wide", shot_type: "wide", camera_angle: "high_angle"}
464
+ - {panel_type: "dialogue", focus: "character", composition: "landscape", shot_type: "medium", camera_angle: "eye_level"}
465
+ - {panel_type: "dialogue", focus: "character", composition: "landscape", shot_type: "medium_closeup", camera_angle: "over_shoulder"}
466
+ - {panel_type: "action", focus: "character", composition: "portrait", shot_type: "full", camera_angle: "low_angle"}
467
+ - {panel_type: "action", focus: "character", composition: "portrait", shot_type: "medium_full", camera_angle: "dutch_angle"}
468
+ - {panel_type: "closeup", focus: "emotion", composition: "portrait", shot_type: "extreme_closeup", camera_angle: "eye_level"}
469
+
470
+ 7_images:
471
+ - id: "hero_hexagon"
472
+ label: "Hero Hexagon"
473
+ description: "Central focus with six surrounding"
474
+ positions:
475
+ - [0.28, 0.02, 0.44, 0.3]
476
+ - [0.02, 0.25, 0.3, 0.25]
477
+ - [0.68, 0.25, 0.3, 0.25]
478
+ - [0.25, 0.35, 0.5, 0.3]
479
+ - [0.02, 0.52, 0.3, 0.25]
480
+ - [0.68, 0.52, 0.3, 0.25]
481
+ - [0.28, 0.68, 0.44, 0.3]
482
+ metadata:
483
+ - {panel_type: "establishing", focus: "environment", composition: "landscape", shot_type: "wide", camera_angle: "high_angle"}
484
+ - {panel_type: "detail", focus: "object", composition: "landscape", shot_type: "closeup", camera_angle: "eye_level"}
485
+ - {panel_type: "detail", focus: "object", composition: "landscape", shot_type: "extreme_closeup", camera_angle: "overhead"}
486
+ - {panel_type: "action", focus: "character", composition: "landscape", shot_type: "full", camera_angle: "low_angle"}
487
+ - {panel_type: "dialogue", focus: "character", composition: "landscape", shot_type: "medium_closeup", camera_angle: "eye_level"}
488
+ - {panel_type: "dialogue", focus: "character", composition: "landscape", shot_type: "medium_closeup", camera_angle: "over_shoulder"}
489
+ - {panel_type: "reaction", focus: "emotion", composition: "landscape", shot_type: "closeup", camera_angle: "eye_level"}
490
+
491
+ - id: "narrative_flow"
492
+ label: "Narrative Flow"
493
+ description: "Story progression with varied sizes"
494
+ positions:
495
+ - [0.02, 0.02, 0.47, 0.28]
496
+ - [0.51, 0.02, 0.47, 0.28]
497
+ - [0.02, 0.32, 0.96, 0.24]
498
+ - [0.02, 0.58, 0.31, 0.4]
499
+ - [0.345, 0.58, 0.31, 0.4]
500
+ - [0.67, 0.58, 0.31, 0.2]
501
+ - [0.67, 0.8, 0.31, 0.18]
502
+ metadata:
503
+ - {panel_type: "establishing", focus: "environment", composition: "landscape", shot_type: "wide", camera_angle: "eye_level"}
504
+ - {panel_type: "dialogue", focus: "character", composition: "landscape", shot_type: "medium", camera_angle: "eye_level"}
505
+ - {panel_type: "action", focus: "event", composition: "wide", shot_type: "full", camera_angle: "low_angle"}
506
+ - {panel_type: "action", focus: "character", composition: "portrait", shot_type: "full", camera_angle: "eye_level"}
507
+ - {panel_type: "action", focus: "character", composition: "portrait", shot_type: "medium_full", camera_angle: "dutch_angle"}
508
+ - {panel_type: "closeup", focus: "emotion", composition: "portrait", shot_type: "closeup", camera_angle: "low_angle"}
509
+ - {panel_type: "detail", focus: "object", composition: "portrait", shot_type: "extreme_closeup", camera_angle: "overhead"}
510
+
511
+ - id: "double_spread"
512
+ label: "Double Feature"
513
+ description: "Two hero panels with details"
514
+ positions:
515
+ - [0.02, 0.02, 0.47, 0.45]
516
+ - [0.51, 0.02, 0.47, 0.45]
517
+ - [0.02, 0.49, 0.31, 0.24]
518
+ - [0.345, 0.49, 0.31, 0.24]
519
+ - [0.67, 0.49, 0.31, 0.24]
520
+ - [0.02, 0.75, 0.47, 0.23]
521
+ - [0.51, 0.75, 0.47, 0.23]
522
+ metadata:
523
+ - {panel_type: "action", focus: "event", composition: "portrait", shot_type: "full", camera_angle: "low_angle"}
524
+ - {panel_type: "action", focus: "event", composition: "portrait", shot_type: "full", camera_angle: "high_angle"}
525
+ - {panel_type: "detail", focus: "object", composition: "landscape", shot_type: "extreme_closeup", camera_angle: "eye_level"}
526
+ - {panel_type: "dialogue", focus: "character", composition: "landscape", shot_type: "medium_closeup", camera_angle: "eye_level"}
527
+ - {panel_type: "detail", focus: "object", composition: "landscape", shot_type: "closeup", camera_angle: "overhead"}
528
+ - {panel_type: "reaction", focus: "emotion", composition: "landscape", shot_type: "closeup", camera_angle: "eye_level"}
529
+ - {panel_type: "reaction", focus: "emotion", composition: "landscape", shot_type: "medium_closeup", camera_angle: "over_shoulder"}
530
+
531
+ - id: "spiral_narrative"
532
+ label: "Spiral Narrative"
533
+ description: "Circular reading flow"
534
+ positions:
535
+ - [0.28, 0.02, 0.44, 0.25]
536
+ - [0.52, 0.15, 0.46, 0.25]
537
+ - [0.52, 0.42, 0.46, 0.25]
538
+ - [0.28, 0.55, 0.44, 0.25]
539
+ - [0.02, 0.42, 0.44, 0.25]
540
+ - [0.02, 0.15, 0.44, 0.25]
541
+ - [0.3, 0.3, 0.4, 0.22]
542
+ metadata:
543
+ - {panel_type: "establishing", focus: "environment", composition: "landscape", shot_type: "wide", camera_angle: "high_angle"}
544
+ - {panel_type: "dialogue", focus: "character", composition: "landscape", shot_type: "medium", camera_angle: "eye_level"}
545
+ - {panel_type: "action", focus: "event", composition: "landscape", shot_type: "medium_full", camera_angle: "eye_level"}
546
+ - {panel_type: "action", focus: "event", composition: "landscape", shot_type: "full", camera_angle: "low_angle"}
547
+ - {panel_type: "dialogue", focus: "character", composition: "landscape", shot_type: "medium_closeup", camera_angle: "over_shoulder"}
548
+ - {panel_type: "reaction", focus: "emotion", composition: "landscape", shot_type: "closeup", camera_angle: "eye_level"}
549
+ - {panel_type: "closeup", focus: "character", composition: "landscape", shot_type: "extreme_closeup", camera_angle: "pov"}
550
+
551
+ - id: "action_montage"
552
+ label: "Action Montage"
553
+ description: "Fast-paced action sequence"
554
+ positions:
555
+ - [0.02, 0.02, 0.96, 0.3]
556
+ - [0.02, 0.34, 0.31, 0.3]
557
+ - [0.345, 0.34, 0.31, 0.3]
558
+ - [0.67, 0.34, 0.31, 0.3]
559
+ - [0.02, 0.66, 0.23, 0.32]
560
+ - [0.27, 0.66, 0.23, 0.32]
561
+ - [0.52, 0.66, 0.46, 0.32]
562
+ metadata:
563
+ - {panel_type: "establishing", focus: "environment", composition: "wide", shot_type: "extreme_wide", camera_angle: "eye_level"}
564
+ - {panel_type: "action", focus: "event", composition: "square", shot_type: "medium", camera_angle: "dutch_angle"}
565
+ - {panel_type: "action", focus: "event", composition: "square", shot_type: "medium_full", camera_angle: "low_angle"}
566
+ - {panel_type: "action", focus: "event", composition: "square", shot_type: "full", camera_angle: "high_angle"}
567
+ - {panel_type: "closeup", focus: "emotion", composition: "portrait", shot_type: "closeup", camera_angle: "low_angle"}
568
+ - {panel_type: "closeup", focus: "emotion", composition: "portrait", shot_type: "extreme_closeup", camera_angle: "eye_level"}
569
+ - {panel_type: "reaction", focus: "characters", composition: "landscape", shot_type: "medium", camera_angle: "eye_level"}
570
+
571
+ - id: "cinematic_sequence"
572
+ label: "Cinematic Sequence"
573
+ description: "Movie-like panel progression"
574
+ positions:
575
+ - [0.02, 0.02, 0.96, 0.28]
576
+ - [0.02, 0.32, 0.47, 0.2]
577
+ - [0.51, 0.32, 0.47, 0.2]
578
+ - [0.02, 0.54, 0.31, 0.21]
579
+ - [0.345, 0.54, 0.31, 0.21]
580
+ - [0.67, 0.54, 0.31, 0.21]
581
+ - [0.02, 0.77, 0.96, 0.21]
582
+ metadata:
583
+ - {panel_type: "establishing", focus: "environment", composition: "wide", shot_type: "extreme_wide", camera_angle: "high_angle"}
584
+ - {panel_type: "dialogue", focus: "character", composition: "landscape", shot_type: "medium", camera_angle: "eye_level"}
585
+ - {panel_type: "dialogue", focus: "character", composition: "landscape", shot_type: "medium_closeup", camera_angle: "over_shoulder"}
586
+ - {panel_type: "action", focus: "event", composition: "square", shot_type: "medium_full", camera_angle: "eye_level"}
587
+ - {panel_type: "action", focus: "event", composition: "square", shot_type: "full", camera_angle: "dutch_angle"}
588
+ - {panel_type: "closeup", focus: "emotion", composition: "square", shot_type: "extreme_closeup", camera_angle: "pov"}
589
+ - {panel_type: "reaction", focus: "characters", composition: "wide", shot_type: "medium", camera_angle: "eye_level"}
590
+
591
+ 8_images:
592
+ - id: "mega_grid"
593
+ label: "Mega Grid"
594
+ description: "Classic 4x2 grid"
595
+ positions:
596
+ - [0.02, 0.02, 0.23, 0.47]
597
+ - [0.27, 0.02, 0.23, 0.47]
598
+ - [0.52, 0.02, 0.23, 0.47]
599
+ - [0.77, 0.02, 0.21, 0.47]
600
+ - [0.02, 0.51, 0.23, 0.47]
601
+ - [0.27, 0.51, 0.23, 0.47]
602
+ - [0.52, 0.51, 0.23, 0.47]
603
+ - [0.77, 0.51, 0.21, 0.47]
604
+ metadata:
605
+ - {panel_type: "establishing", focus: "environment", composition: "portrait", shot_type: "wide", camera_angle: "high_angle"}
606
+ - {panel_type: "dialogue", focus: "character", composition: "portrait", shot_type: "medium", camera_angle: "eye_level"}
607
+ - {panel_type: "dialogue", focus: "character", composition: "portrait", shot_type: "medium_closeup", camera_angle: "over_shoulder"}
608
+ - {panel_type: "action", focus: "event", composition: "portrait", shot_type: "medium_full", camera_angle: "eye_level"}
609
+ - {panel_type: "action", focus: "event", composition: "portrait", shot_type: "full", camera_angle: "low_angle"}
610
+ - {panel_type: "action", focus: "character", composition: "portrait", shot_type: "medium", camera_angle: "dutch_angle"}
611
+ - {panel_type: "closeup", focus: "emotion", composition: "portrait", shot_type: "closeup", camera_angle: "eye_level"}
612
+ - {panel_type: "reaction", focus: "emotion", composition: "portrait", shot_type: "extreme_closeup", camera_angle: "low_angle"}
613
+
614
+ - id: "chapter_opener"
615
+ label: "Chapter Opener"
616
+ description: "Splash page with progressive reveal"
617
+ positions:
618
+ - [0.02, 0.02, 0.96, 0.45]
619
+ - [0.02, 0.49, 0.23, 0.24]
620
+ - [0.27, 0.49, 0.23, 0.24]
621
+ - [0.52, 0.49, 0.23, 0.24]
622
+ - [0.77, 0.49, 0.21, 0.24]
623
+ - [0.02, 0.75, 0.23, 0.23]
624
+ - [0.27, 0.75, 0.23, 0.23]
625
+ - [0.52, 0.75, 0.46, 0.23]
626
+ metadata:
627
+ - {panel_type: "splash", focus: "environment", composition: "wide", shot_type: "extreme_wide", camera_angle: "high_angle"}
628
+ - {panel_type: "detail", focus: "object", composition: "square", shot_type: "extreme_closeup", camera_angle: "overhead"}
629
+ - {panel_type: "detail", focus: "character", composition: "square", shot_type: "closeup", camera_angle: "low_angle"}
630
+ - {panel_type: "dialogue", focus: "character", composition: "square", shot_type: "medium_closeup", camera_angle: "eye_level"}
631
+ - {panel_type: "dialogue", focus: "character", composition: "square", shot_type: "medium", camera_angle: "over_shoulder"}
632
+ - {panel_type: "action", focus: "event", composition: "square", shot_type: "medium_full", camera_angle: "eye_level"}
633
+ - {panel_type: "closeup", focus: "emotion", composition: "square", shot_type: "extreme_closeup", camera_angle: "pov"}
634
+ - {panel_type: "reaction", focus: "characters", composition: "landscape", shot_type: "medium", camera_angle: "eye_level"}
635
+
636
+ - id: "parallel_stories"
637
+ label: "Parallel Stories"
638
+ description: "Two simultaneous narratives"
639
+ positions:
640
+ - [0.02, 0.02, 0.47, 0.23]
641
+ - [0.51, 0.02, 0.47, 0.23]
642
+ - [0.02, 0.27, 0.47, 0.23]
643
+ - [0.51, 0.27, 0.47, 0.23]
644
+ - [0.02, 0.52, 0.47, 0.23]
645
+ - [0.51, 0.52, 0.47, 0.23]
646
+ - [0.02, 0.77, 0.47, 0.21]
647
+ - [0.51, 0.77, 0.47, 0.21]
648
+ metadata:
649
+ - {panel_type: "establishing", focus: "environment", composition: "landscape", shot_type: "wide", camera_angle: "eye_level"}
650
+ - {panel_type: "establishing", focus: "environment", composition: "landscape", shot_type: "wide", camera_angle: "high_angle"}
651
+ - {panel_type: "dialogue", focus: "character", composition: "landscape", shot_type: "medium", camera_angle: "eye_level"}
652
+ - {panel_type: "dialogue", focus: "character", composition: "landscape", shot_type: "medium_closeup", camera_angle: "over_shoulder"}
653
+ - {panel_type: "action", focus: "event", composition: "landscape", shot_type: "full", camera_angle: "low_angle"}
654
+ - {panel_type: "action", focus: "event", composition: "landscape", shot_type: "medium_full", camera_angle: "dutch_angle"}
655
+ - {panel_type: "reaction", focus: "emotion", composition: "landscape", shot_type: "closeup", camera_angle: "eye_level"}
656
+ - {panel_type: "reaction", focus: "emotion", composition: "landscape", shot_type: "closeup", camera_angle: "eye_level"}
657
+
658
+ - id: "hero_explosion"
659
+ label: "Hero Explosion"
660
+ description: "Central impact radiating outward"
661
+ positions:
662
+ - [0.02, 0.02, 0.3, 0.3]
663
+ - [0.68, 0.02, 0.3, 0.3]
664
+ - [0.02, 0.68, 0.3, 0.3]
665
+ - [0.68, 0.68, 0.3, 0.3]
666
+ - [0.34, 0.02, 0.32, 0.28]
667
+ - [0.02, 0.34, 0.28, 0.32]
668
+ - [0.7, 0.34, 0.28, 0.32]
669
+ - [0.34, 0.7, 0.32, 0.28]
670
+ metadata:
671
+ - {panel_type: "detail", focus: "object", composition: "square", shot_type: "closeup", camera_angle: "overhead"}
672
+ - {panel_type: "detail", focus: "object", composition: "square", shot_type: "extreme_closeup", camera_angle: "eye_level"}
673
+ - {panel_type: "detail", focus: "character", composition: "square", shot_type: "closeup", camera_angle: "low_angle"}
674
+ - {panel_type: "detail", focus: "character", composition: "square", shot_type: "closeup", camera_angle: "high_angle"}
675
+ - {panel_type: "action", focus: "event", composition: "landscape", shot_type: "medium", camera_angle: "dutch_angle"}
676
+ - {panel_type: "action", focus: "event", composition: "portrait", shot_type: "full", camera_angle: "low_angle"}
677
+ - {panel_type: "action", focus: "event", composition: "portrait", shot_type: "medium_full", camera_angle: "high_angle"}
678
+ - {panel_type: "closeup", focus: "emotion", composition: "landscape", shot_type: "extreme_closeup", camera_angle: "pov"}
679
+
680
+ - id: "magazine_style"
681
+ label: "Magazine Style"
682
+ description: "Editorial layout with varied sizes"
683
+ positions:
684
+ - [0.02, 0.02, 0.63, 0.35]
685
+ - [0.67, 0.02, 0.31, 0.35]
686
+ - [0.02, 0.39, 0.31, 0.28]
687
+ - [0.35, 0.39, 0.31, 0.28]
688
+ - [0.68, 0.39, 0.3, 0.28]
689
+ - [0.02, 0.69, 0.31, 0.29]
690
+ - [0.35, 0.69, 0.31, 0.29]
691
+ - [0.68, 0.69, 0.3, 0.29]
692
+ metadata:
693
+ - {panel_type: "establishing", focus: "environment", composition: "landscape", shot_type: "wide", camera_angle: "high_angle"}
694
+ - {panel_type: "dialogue", focus: "character", composition: "portrait", shot_type: "medium", camera_angle: "eye_level"}
695
+ - {panel_type: "detail", focus: "object", composition: "square", shot_type: "extreme_closeup", camera_angle: "overhead"}
696
+ - {panel_type: "action", focus: "event", composition: "square", shot_type: "medium_full", camera_angle: "eye_level"}
697
+ - {panel_type: "dialogue", focus: "character", composition: "square", shot_type: "medium_closeup", camera_angle: "over_shoulder"}
698
+ - {panel_type: "action", focus: "character", composition: "square", shot_type: "full", camera_angle: "low_angle"}
699
+ - {panel_type: "closeup", focus: "emotion", composition: "square", shot_type: "closeup", camera_angle: "eye_level"}
700
+ - {panel_type: "reaction", focus: "emotion", composition: "square", shot_type: "extreme_closeup", camera_angle: "pov"}
701
+
702
+ - id: "epic_finale"
703
+ label: "Epic Finale"
704
+ description: "Climactic page layout"
705
+ positions:
706
+ - [0.02, 0.02, 0.31, 0.25]
707
+ - [0.345, 0.02, 0.31, 0.25]
708
+ - [0.67, 0.02, 0.31, 0.25]
709
+ - [0.02, 0.29, 0.47, 0.4]
710
+ - [0.51, 0.29, 0.47, 0.4]
711
+ - [0.02, 0.71, 0.31, 0.27]
712
+ - [0.345, 0.71, 0.31, 0.27]
713
+ - [0.67, 0.71, 0.31, 0.27]
714
+ metadata:
715
+ - {panel_type: "detail", focus: "object", composition: "landscape", shot_type: "extreme_closeup", camera_angle: "overhead"}
716
+ - {panel_type: "dialogue", focus: "character", composition: "landscape", shot_type: "medium_closeup", camera_angle: "eye_level"}
717
+ - {panel_type: "detail", focus: "object", composition: "landscape", shot_type: "closeup", camera_angle: "low_angle"}
718
+ - {panel_type: "action", focus: "event", composition: "portrait", shot_type: "full", camera_angle: "low_angle"}
719
+ - {panel_type: "action", focus: "event", composition: "portrait", shot_type: "full", camera_angle: "high_angle"}
720
+ - {panel_type: "closeup", focus: "emotion", composition: "landscape", shot_type: "closeup", camera_angle: "eye_level"}
721
+ - {panel_type: "reaction", focus: "emotion", composition: "landscape", shot_type: "extreme_closeup", camera_angle: "pov"}
722
+ - {panel_type: "reaction", focus: "characters", composition: "landscape", shot_type: "medium", camera_angle: "eye_level"}
723
+ # Page layouts configuration for multi-image PDF generation
724
+ # Each layout defines how images are arranged on a page
725
+ # Positions are defined as (x, y, width, height) in relative units (0-1)
726
+ # Coordinate system: (0,0) is top-left, X increases right, Y increases down
727
+ #
728
+ # Panel metadata helps guide image generation:
729
+ # - panel_type: establishing/action/closeup/dialogue/reaction/transition/detail/splash
730
+ # - focus: environment/character/characters/action/emotion/object/event
731
+ # - composition: wide/tall/square/portrait/landscape
732
+
733
+ layouts:
734
+ 1_image:
735
+ - id: "full_bleed"
736
+ label: "Full Bleed"
737
+ description: "Single image filling entire page"
738
+ positions:
739
+ - [0.0, 0.0, 1.0, 1.0]
740
+ metadata:
741
+ - {panel_type: "splash", focus: "event", composition: "square"}
742
+
743
+ - id: "classic_frame"
744
+ label: "Classic Frame"
745
+ description: "Single image with traditional margins"
746
+ positions:
747
+ - [0.05, 0.05, 0.9, 0.9]
748
+ metadata:
749
+ - {panel_type: "establishing", focus: "environment", composition: "square"}
750
+
751
+ - id: "portrait_focus"
752
+ label: "Portrait Focus"
753
+ description: "Vertical emphasis for character shots"
754
+ positions:
755
+ - [0.15, 0.02, 0.7, 0.96]
756
+ metadata:
757
+ - {panel_type: "closeup", focus: "character", composition: "portrait"}
758
+
759
+ - id: "cinematic_wide"
760
+ label: "Cinematic Wide"
761
+ description: "Wide letterbox format"
762
+ positions:
763
+ - [0.02, 0.25, 0.96, 0.5]
764
+ metadata:
765
+ - {panel_type: "establishing", focus: "environment", composition: "wide"}
766
+
767
+ - id: "floating_center"
768
+ label: "Floating Center"
769
+ description: "Centered with breathing room"
770
+ positions:
771
+ - [0.1, 0.15, 0.8, 0.7]
772
+ metadata:
773
+ - {panel_type: "dialogue", focus: "character", composition: "square"}
774
+
775
+ 2_images:
776
+ - id: "horizontal_split"
777
+ label: "Even Split"
778
+ description: "Two equal panels side by side"
779
+ positions:
780
+ - [0.02, 0.02, 0.47, 0.96]
781
+ - [0.51, 0.02, 0.47, 0.96]
782
+ metadata:
783
+ - {panel_type: "establishing", focus: "environment", composition: "portrait"}
784
+ - {panel_type: "action", focus: "character", composition: "portrait"}
785
+
786
+ - id: "vertical_split"
787
+ label: "Top & Bottom"
788
+ description: "Stacked panels"
789
+ positions:
790
+ - [0.02, 0.02, 0.96, 0.47]
791
+ - [0.02, 0.51, 0.96, 0.47]
792
+ metadata:
793
+ - {panel_type: "establishing", focus: "environment", composition: "wide"}
794
+ - {panel_type: "action", focus: "event", composition: "wide"}
795
+
796
+ - id: "hero_sidekick"
797
+ label: "Hero & Sidekick"
798
+ description: "Large main panel with small detail"
799
+ positions:
800
+ - [0.02, 0.02, 0.65, 0.96]
801
+ - [0.69, 0.25, 0.29, 0.5]
802
+ metadata:
803
+ - {panel_type: "action", focus: "character", composition: "portrait"}
804
+ - {panel_type: "reaction", focus: "emotion", composition: "portrait"}
805
+
806
+ - id: "before_after"
807
+ label: "Before & After"
808
+ description: "Cause and effect layout"
809
+ positions:
810
+ - [0.02, 0.02, 0.96, 0.44]
811
+ - [0.02, 0.48, 0.96, 0.5]
812
+ metadata:
813
+ - {panel_type: "establishing", focus: "environment", composition: "wide"}
814
+ - {panel_type: "action", focus: "event", composition: "wide"}
815
+
816
+ - id: "diagonal_tension"
817
+ label: "Diagonal Tension"
818
+ description: "Overlapping dynamic panels"
819
+ positions:
820
+ - [0.02, 0.02, 0.6, 0.6]
821
+ - [0.38, 0.38, 0.6, 0.6]
822
+ metadata:
823
+ - {panel_type: "action", focus: "event", composition: "square"}
824
+ - {panel_type: "action", focus: "event", composition: "square"}
825
+
826
+ 3_images:
827
+ - id: "triptych"
828
+ label: "Triptych"
829
+ description: "Three equal vertical panels"
830
+ positions:
831
+ - [0.02, 0.02, 0.31, 0.96]
832
+ - [0.345, 0.02, 0.31, 0.96]
833
+ - [0.67, 0.02, 0.31, 0.96]
834
+ metadata:
835
+ - {panel_type: "establishing", focus: "environment", composition: "portrait"}
836
+ - {panel_type: "action", focus: "character", composition: "portrait"}
837
+ - {panel_type: "reaction", focus: "emotion", composition: "portrait"}
838
+
839
+ - id: "hero_top"
840
+ label: "Establishing Shot"
841
+ description: "Large top panel with details below"
842
+ positions:
843
+ - [0.02, 0.02, 0.96, 0.5]
844
+ - [0.02, 0.54, 0.47, 0.44]
845
+ - [0.51, 0.54, 0.47, 0.44]
846
+ metadata:
847
+ - {panel_type: "establishing", focus: "environment", composition: "wide"}
848
+ - {panel_type: "action", focus: "character", composition: "square"}
849
+ - {panel_type: "reaction", focus: "emotion", composition: "square"}
850
 
851
  - id: "l_shape"
852
+ label: "L-Shape Flow"
853
+ description: "Reading flow in L pattern"
854
+ positions:
855
+ - [0.02, 0.02, 0.47, 0.47]
856
+ - [0.51, 0.02, 0.47, 0.47]
857
+ - [0.02, 0.51, 0.96, 0.47]
858
+ metadata:
859
+ - {panel_type: "dialogue", focus: "character", composition: "square"}
860
+ - {panel_type: "dialogue", focus: "character", composition: "square"}
861
+ - {panel_type: "action", focus: "event", composition: "wide"}
862
+
863
+ - id: "spotlight"
864
+ label: "Spotlight"
865
+ description: "Central focus with side panels"
866
+ positions:
867
+ - [0.02, 0.15, 0.28, 0.7]
868
+ - [0.32, 0.02, 0.36, 0.96]
869
+ - [0.7, 0.15, 0.28, 0.7]
870
+ metadata:
871
+ - {panel_type: "reaction", focus: "character", composition: "portrait"}
872
+ - {panel_type: "action", focus: "character", composition: "portrait"}
873
+ - {panel_type: "reaction", focus: "character", composition: "portrait"}
874
+
875
+ - id: "vertical_flow"
876
+ label: "Vertical Flow"
877
+ description: "Three stacked panels for sequential action"
878
+ positions:
879
+ - [0.02, 0.02, 0.96, 0.31]
880
+ - [0.02, 0.345, 0.96, 0.31]
881
+ - [0.02, 0.67, 0.96, 0.31]
882
+ metadata:
883
+ - {panel_type: "establishing", focus: "environment", composition: "wide"}
884
+ - {panel_type: "action", focus: "event", composition: "wide"}
885
+ - {panel_type: "action", focus: "event", composition: "wide"}
886
+
887
+ - id: "manga_action"
888
+ label: "Manga Action"
889
+ description: "Dynamic manga-style layout"
890
+ positions:
891
+ - [0.52, 0.02, 0.46, 0.45]
892
+ - [0.02, 0.02, 0.48, 0.65]
893
+ - [0.02, 0.69, 0.96, 0.29]
894
+ metadata:
895
+ - {panel_type: "reaction", focus: "emotion", composition: "square"}
896
+ - {panel_type: "action", focus: "character", composition: "portrait"}
897
+ - {panel_type: "action", focus: "event", composition: "wide"}
898
+
899
+ 4_images:
900
+ - id: "grid_2x2"
901
+ label: "Classic Grid"
902
+ description: "Traditional 2x2 layout"
903
+ positions:
904
+ - [0.02, 0.02, 0.47, 0.47]
905
+ - [0.51, 0.02, 0.47, 0.47]
906
+ - [0.02, 0.51, 0.47, 0.47]
907
+ - [0.51, 0.51, 0.47, 0.47]
908
+ metadata:
909
+ - {panel_type: "establishing", focus: "environment", composition: "square"}
910
+ - {panel_type: "dialogue", focus: "character", composition: "square"}
911
+ - {panel_type: "action", focus: "event", composition: "square"}
912
+ - {panel_type: "reaction", focus: "emotion", composition: "square"}
913
+
914
+ - id: "widescreen"
915
+ label: "Widescreen Strips"
916
+ description: "Four cinematic horizontal strips"
917
+ positions:
918
+ - [0.02, 0.02, 0.96, 0.23]
919
+ - [0.02, 0.27, 0.96, 0.23]
920
+ - [0.02, 0.52, 0.96, 0.23]
921
+ - [0.02, 0.77, 0.96, 0.21]
922
+ metadata:
923
+ - {panel_type: "establishing", focus: "environment", composition: "wide"}
924
+ - {panel_type: "dialogue", focus: "characters", composition: "wide"}
925
+ - {panel_type: "action", focus: "event", composition: "wide"}
926
+ - {panel_type: "reaction", focus: "emotion", composition: "wide"}
927
+
928
+ - id: "hero_cluster"
929
+ label: "Hero with Cluster"
930
+ description: "Large panel with three supporting"
931
  positions:
932
+ - [0.02, 0.02, 0.6, 0.63]
933
+ - [0.64, 0.02, 0.34, 0.3]
934
+ - [0.64, 0.34, 0.34, 0.31]
935
+ - [0.02, 0.67, 0.96, 0.31]
936
+ metadata:
937
+ - {panel_type: "action", focus: "event", composition: "square"}
938
+ - {panel_type: "detail", focus: "object", composition: "landscape"}
939
+ - {panel_type: "reaction", focus: "emotion", composition: "landscape"}
940
+ - {panel_type: "transition", focus: "environment", composition: "wide"}
941
+
942
+ - id: "comic_strip"
943
+ label: "Comic Strip"
944
+ description: "Newspaper strip style"
945
+ positions:
946
+ - [0.02, 0.3, 0.23, 0.4]
947
+ - [0.27, 0.3, 0.23, 0.4]
948
+ - [0.52, 0.3, 0.23, 0.4]
949
+ - [0.77, 0.3, 0.21, 0.4]
950
+ metadata:
951
+ - {panel_type: "establishing", focus: "environment", composition: "portrait"}
952
+ - {panel_type: "dialogue", focus: "characters", composition: "portrait"}
953
+ - {panel_type: "action", focus: "event", composition: "portrait"}
954
+ - {panel_type: "reaction", focus: "emotion", composition: "portrait"}
955
+
956
+ - id: "z_pattern"
957
+ label: "Z-Pattern"
958
+ description: "Natural reading flow in Z shape"
959
+ positions:
960
+ - [0.02, 0.02, 0.47, 0.35]
961
+ - [0.51, 0.02, 0.47, 0.35]
962
+ - [0.02, 0.39, 0.47, 0.59]
963
+ - [0.51, 0.39, 0.47, 0.59]
964
+ metadata:
965
+ - {panel_type: "establishing", focus: "environment", composition: "landscape"}
966
+ - {panel_type: "dialogue", focus: "character", composition: "landscape"}
967
+ - {panel_type: "action", focus: "character", composition: "portrait"}
968
+ - {panel_type: "action", focus: "event", composition: "portrait"}
969
+
970
+ - id: "explosion"
971
+ label: "Explosion"
972
+ description: "Central impact with surrounding panels"
973
+ positions:
974
+ - [0.02, 0.02, 0.35, 0.35]
975
+ - [0.63, 0.02, 0.35, 0.35]
976
+ - [0.27, 0.27, 0.46, 0.46]
977
+ - [0.02, 0.63, 0.96, 0.35]
978
+ metadata:
979
+ - {panel_type: "establishing", focus: "environment", composition: "square"}
980
+ - {panel_type: "detail", focus: "object", composition: "square"}
981
+ - {panel_type: "action", focus: "event", composition: "square"}
982
+ - {panel_type: "reaction", focus: "characters", composition: "wide"}
983
 
984
  5_images:
985
+ - id: "hero_banner"
986
+ label: "Hero Banner"
987
+ description: "Wide establishing shot with four panels below"
988
+ positions:
989
+ - [0.02, 0.02, 0.96, 0.38]
990
+ - [0.02, 0.42, 0.47, 0.28]
991
+ - [0.51, 0.42, 0.47, 0.28]
992
+ - [0.02, 0.72, 0.47, 0.26]
993
+ - [0.51, 0.72, 0.47, 0.26]
994
+ metadata:
995
+ - {panel_type: "establishing", focus: "environment", composition: "wide"}
996
+ - {panel_type: "dialogue", focus: "character", composition: "landscape"}
997
+ - {panel_type: "dialogue", focus: "character", composition: "landscape"}
998
+ - {panel_type: "action", focus: "event", composition: "landscape"}
999
+ - {panel_type: "reaction", focus: "emotion", composition: "landscape"}
1000
+
1001
+ - id: "manga_vertical"
1002
+ label: "Manga Vertical"
1003
+ description: "Japanese right-to-left vertical flow"
1004
+ positions:
1005
+ - [0.52, 0.02, 0.46, 0.32]
1006
+ - [0.02, 0.02, 0.48, 0.32]
1007
+ - [0.52, 0.36, 0.46, 0.3]
1008
+ - [0.02, 0.36, 0.48, 0.3]
1009
+ - [0.02, 0.68, 0.96, 0.3]
1010
+ metadata:
1011
+ - {panel_type: "establishing", focus: "environment", composition: "landscape"}
1012
+ - {panel_type: "dialogue", focus: "character", composition: "landscape"}
1013
+ - {panel_type: "action", focus: "character", composition: "landscape"}
1014
+ - {panel_type: "reaction", focus: "emotion", composition: "landscape"}
1015
+ - {panel_type: "action", focus: "event", composition: "wide"}
1016
+
1017
+ - id: "pyramid"
1018
+ label: "Pyramid"
1019
+ description: "Building tension from top to bottom"
1020
+ positions:
1021
+ - [0.25, 0.02, 0.5, 0.25]
1022
+ - [0.02, 0.29, 0.47, 0.3]
1023
+ - [0.51, 0.29, 0.47, 0.3]
1024
+ - [0.02, 0.61, 0.31, 0.37]
1025
+ - [0.67, 0.61, 0.31, 0.37]
1026
+ metadata:
1027
+ - {panel_type: "establishing", focus: "environment", composition: "landscape"}
1028
+ - {panel_type: "dialogue", focus: "character", composition: "landscape"}
1029
+ - {panel_type: "dialogue", focus: "character", composition: "landscape"}
1030
+ - {panel_type: "action", focus: "event", composition: "portrait"}
1031
+ - {panel_type: "action", focus: "event", composition: "portrait"}
1032
+
1033
+ - id: "spotlight_drama"
1034
+ label: "Spotlight Drama"
1035
+ description: "Central focus with corner details"
1036
+ positions:
1037
+ - [0.02, 0.02, 0.35, 0.35]
1038
+ - [0.63, 0.02, 0.35, 0.35]
1039
+ - [0.22, 0.22, 0.56, 0.56]
1040
+ - [0.02, 0.63, 0.35, 0.35]
1041
+ - [0.63, 0.63, 0.35, 0.35]
1042
+ metadata:
1043
+ - {panel_type: "detail", focus: "object", composition: "square"}
1044
+ - {panel_type: "detail", focus: "object", composition: "square"}
1045
+ - {panel_type: "action", focus: "character", composition: "square"}
1046
+ - {panel_type: "reaction", focus: "emotion", composition: "square"}
1047
+ - {panel_type: "reaction", focus: "emotion", composition: "square"}
1048
+
1049
+ - id: "euro_bd"
1050
+ label: "Euro BD Classic"
1051
+ description: "Franco-Belgian structured layout"
1052
+ positions:
1053
+ - [0.02, 0.02, 0.47, 0.29]
1054
+ - [0.51, 0.02, 0.47, 0.29]
1055
+ - [0.02, 0.33, 0.96, 0.31]
1056
+ - [0.02, 0.66, 0.47, 0.32]
1057
+ - [0.51, 0.66, 0.47, 0.32]
1058
+ metadata:
1059
+ - {panel_type: "establishing", focus: "environment", composition: "landscape"}
1060
+ - {panel_type: "dialogue", focus: "character", composition: "landscape"}
1061
+ - {panel_type: "action", focus: "event", composition: "wide"}
1062
+ - {panel_type: "dialogue", focus: "characters", composition: "landscape"}
1063
+ - {panel_type: "reaction", focus: "emotion", composition: "landscape"}
1064
+
1065
+ - id: "action_burst"
1066
+ label: "Action Burst"
1067
+ description: "Dynamic superhero action layout"
1068
+ positions:
1069
+ - [0.02, 0.02, 0.55, 0.55]
1070
+ - [0.59, 0.02, 0.39, 0.26]
1071
+ - [0.59, 0.3, 0.39, 0.27]
1072
+ - [0.02, 0.59, 0.47, 0.39]
1073
+ - [0.51, 0.59, 0.47, 0.39]
1074
+ metadata:
1075
+ - {panel_type: "action", focus: "event", composition: "square"}
1076
+ - {panel_type: "detail", focus: "object", composition: "landscape"}
1077
+ - {panel_type: "closeup", focus: "emotion", composition: "landscape"}
1078
+ - {panel_type: "action", focus: "character", composition: "landscape"}
1079
+ - {panel_type: "reaction", focus: "characters", composition: "landscape"}
1080
 
1081
  6_images:
1082
+ - id: "classic_grid"
1083
+ label: "Classic 2x3 Grid"
1084
+ description: "Traditional American comic layout"
1085
+ positions:
1086
+ - [0.02, 0.02, 0.47, 0.31]
1087
+ - [0.51, 0.02, 0.47, 0.31]
1088
+ - [0.02, 0.345, 0.47, 0.31]
1089
+ - [0.51, 0.345, 0.47, 0.31]
1090
+ - [0.02, 0.67, 0.47, 0.31]
1091
+ - [0.51, 0.67, 0.47, 0.31]
1092
+ metadata:
1093
+ - {panel_type: "establishing", focus: "environment", composition: "landscape"}
1094
+ - {panel_type: "dialogue", focus: "character", composition: "landscape"}
1095
+ - {panel_type: "dialogue", focus: "character", composition: "landscape"}
1096
+ - {panel_type: "action", focus: "event", composition: "landscape"}
1097
+ - {panel_type: "action", focus: "event", composition: "landscape"}
1098
+ - {panel_type: "reaction", focus: "emotion", composition: "landscape"}
1099
+
1100
+ - id: "hero_surround"
1101
+ label: "Hero Surrounded"
1102
+ description: "Large central panel with five around it"
1103
+ positions:
1104
+ - [0.02, 0.02, 0.31, 0.28]
1105
+ - [0.67, 0.02, 0.31, 0.28]
1106
+ - [0.02, 0.7, 0.31, 0.28]
1107
+ - [0.67, 0.7, 0.31, 0.28]
1108
+ - [0.25, 0.25, 0.5, 0.5]
1109
+ - [0.35, 0.35, 0.63, 0.33]
1110
+ metadata:
1111
+ - {panel_type: "detail", focus: "object", composition: "square"}
1112
+ - {panel_type: "detail", focus: "object", composition: "square"}
1113
+ - {panel_type: "detail", focus: "object", composition: "square"}
1114
+ - {panel_type: "detail", focus: "object", composition: "square"}
1115
+ - {panel_type: "action", focus: "character", composition: "square"}
1116
+ - {panel_type: "action", focus: "event", composition: "landscape"}
1117
+
1118
+ - id: "staircase"
1119
+ label: "Staircase"
1120
+ description: "Diagonal reading flow"
1121
+ positions:
1122
+ - [0.02, 0.02, 0.45, 0.3]
1123
+ - [0.49, 0.02, 0.49, 0.3]
1124
+ - [0.02, 0.34, 0.45, 0.3]
1125
+ - [0.49, 0.34, 0.49, 0.3]
1126
+ - [0.02, 0.66, 0.45, 0.32]
1127
+ - [0.49, 0.66, 0.49, 0.32]
1128
+ metadata:
1129
+ - {panel_type: "establishing", focus: "environment", composition: "landscape"}
1130
+ - {panel_type: "dialogue", focus: "character", composition: "landscape"}
1131
+ - {panel_type: "dialogue", focus: "character", composition: "landscape"}
1132
+ - {panel_type: "action", focus: "event", composition: "landscape"}
1133
+ - {panel_type: "action", focus: "character", composition: "landscape"}
1134
+ - {panel_type: "reaction", focus: "emotion", composition: "landscape"}
1135
+
1136
+ - id: "splash_detail"
1137
+ label: "Splash with Details"
1138
+ description: "Full-page moment with detail insets"
1139
+ positions:
1140
+ - [0.05, 0.05, 0.4, 0.4]
1141
+ - [0.55, 0.05, 0.4, 0.4]
1142
+ - [0.05, 0.55, 0.4, 0.4]
1143
+ - [0.55, 0.55, 0.4, 0.4]
1144
+ - [0.25, 0.25, 0.5, 0.5]
1145
+ - [0.35, 0.35, 0.3, 0.3]
1146
+ metadata:
1147
+ - {panel_type: "detail", focus: "environment", composition: "square"}
1148
+ - {panel_type: "detail", focus: "object", composition: "square"}
1149
+ - {panel_type: "detail", focus: "object", composition: "square"}
1150
+ - {panel_type: "detail", focus: "character", composition: "square"}
1151
+ - {panel_type: "action", focus: "event", composition: "square"}
1152
+ - {panel_type: "closeup", focus: "emotion", composition: "square"}
1153
+
1154
+ - id: "manga_4koma_plus"
1155
+ label: "Manga 4-Koma Plus"
1156
+ description: "Japanese 4-panel with header/footer"
1157
+ positions:
1158
+ - [0.02, 0.02, 0.96, 0.15]
1159
+ - [0.02, 0.19, 0.47, 0.25]
1160
+ - [0.51, 0.19, 0.47, 0.25]
1161
+ - [0.02, 0.46, 0.47, 0.25]
1162
+ - [0.51, 0.46, 0.47, 0.25]
1163
+ - [0.02, 0.73, 0.96, 0.25]
1164
+ metadata:
1165
+ - {panel_type: "establishing", focus: "environment", composition: "wide"}
1166
+ - {panel_type: "dialogue", focus: "character", composition: "landscape"}
1167
+ - {panel_type: "dialogue", focus: "character", composition: "landscape"}
1168
+ - {panel_type: "action", focus: "event", composition: "landscape"}
1169
+ - {panel_type: "action", focus: "event", composition: "landscape"}
1170
+ - {panel_type: "reaction", focus: "emotion", composition: "wide"}
1171
+
1172
+ - id: "tension_build"
1173
+ label: "Tension Builder"
1174
+ description: "Progressive revelation layout"
1175
+ positions:
1176
+ - [0.02, 0.02, 0.96, 0.22]
1177
+ - [0.02, 0.26, 0.47, 0.22]
1178
+ - [0.51, 0.26, 0.47, 0.22]
1179
+ - [0.02, 0.5, 0.31, 0.48]
1180
+ - [0.345, 0.5, 0.31, 0.48]
1181
+ - [0.67, 0.5, 0.31, 0.48]
1182
+ metadata:
1183
+ - {panel_type: "establishing", focus: "environment", composition: "wide"}
1184
+ - {panel_type: "dialogue", focus: "character", composition: "landscape"}
1185
+ - {panel_type: "dialogue", focus: "character", composition: "landscape"}
1186
+ - {panel_type: "action", focus: "character", composition: "portrait"}
1187
+ - {panel_type: "action", focus: "character", composition: "portrait"}
1188
+ - {panel_type: "closeup", focus: "emotion", composition: "portrait"}
1189
+
1190
+ 7_images:
1191
+ - id: "hero_hexagon"
1192
+ label: "Hero Hexagon"
1193
+ description: "Central focus with six surrounding"
1194
+ positions:
1195
+ - [0.28, 0.02, 0.44, 0.3]
1196
+ - [0.02, 0.25, 0.3, 0.25]
1197
+ - [0.68, 0.25, 0.3, 0.25]
1198
+ - [0.25, 0.35, 0.5, 0.3]
1199
+ - [0.02, 0.52, 0.3, 0.25]
1200
+ - [0.68, 0.52, 0.3, 0.25]
1201
+ - [0.28, 0.68, 0.44, 0.3]
1202
+ metadata:
1203
+ - {panel_type: "establishing", focus: "environment", composition: "landscape"}
1204
+ - {panel_type: "detail", focus: "object", composition: "landscape"}
1205
+ - {panel_type: "detail", focus: "object", composition: "landscape"}
1206
+ - {panel_type: "action", focus: "character", composition: "landscape"}
1207
+ - {panel_type: "dialogue", focus: "character", composition: "landscape"}
1208
+ - {panel_type: "dialogue", focus: "character", composition: "landscape"}
1209
+ - {panel_type: "reaction", focus: "emotion", composition: "landscape"}
1210
+
1211
+ - id: "narrative_flow"
1212
+ label: "Narrative Flow"
1213
+ description: "Story progression with varied sizes"
1214
+ positions:
1215
+ - [0.02, 0.02, 0.47, 0.28]
1216
+ - [0.51, 0.02, 0.47, 0.28]
1217
+ - [0.02, 0.32, 0.96, 0.24]
1218
+ - [0.02, 0.58, 0.31, 0.4]
1219
+ - [0.345, 0.58, 0.31, 0.4]
1220
+ - [0.67, 0.58, 0.31, 0.2]
1221
+ - [0.67, 0.8, 0.31, 0.18]
1222
+ metadata:
1223
+ - {panel_type: "establishing", focus: "environment", composition: "landscape"}
1224
+ - {panel_type: "dialogue", focus: "character", composition: "landscape"}
1225
+ - {panel_type: "action", focus: "event", composition: "wide"}
1226
+ - {panel_type: "action", focus: "character", composition: "portrait"}
1227
+ - {panel_type: "action", focus: "character", composition: "portrait"}
1228
+ - {panel_type: "closeup", focus: "emotion", composition: "portrait"}
1229
+ - {panel_type: "detail", focus: "object", composition: "portrait"}
1230
+
1231
+ - id: "double_spread"
1232
+ label: "Double Feature"
1233
+ description: "Two hero panels with details"
1234
+ positions:
1235
+ - [0.02, 0.02, 0.47, 0.45]
1236
+ - [0.51, 0.02, 0.47, 0.45]
1237
+ - [0.02, 0.49, 0.31, 0.24]
1238
+ - [0.345, 0.49, 0.31, 0.24]
1239
+ - [0.67, 0.49, 0.31, 0.24]
1240
+ - [0.02, 0.75, 0.47, 0.23]
1241
+ - [0.51, 0.75, 0.47, 0.23]
1242
+ metadata:
1243
+ - {panel_type: "action", focus: "event", composition: "portrait"}
1244
+ - {panel_type: "action", focus: "event", composition: "portrait"}
1245
+ - {panel_type: "detail", focus: "object", composition: "landscape"}
1246
+ - {panel_type: "dialogue", focus: "character", composition: "landscape"}
1247
+ - {panel_type: "detail", focus: "object", composition: "landscape"}
1248
+ - {panel_type: "reaction", focus: "emotion", composition: "landscape"}
1249
+ - {panel_type: "reaction", focus: "emotion", composition: "landscape"}
1250
+
1251
+ - id: "spiral_narrative"
1252
+ label: "Spiral Narrative"
1253
+ description: "Circular reading flow"
1254
+ positions:
1255
+ - [0.28, 0.02, 0.44, 0.25]
1256
+ - [0.52, 0.15, 0.46, 0.25]
1257
+ - [0.52, 0.42, 0.46, 0.25]
1258
+ - [0.28, 0.55, 0.44, 0.25]
1259
+ - [0.02, 0.42, 0.44, 0.25]
1260
+ - [0.02, 0.15, 0.44, 0.25]
1261
+ - [0.3, 0.3, 0.4, 0.22]
1262
+ metadata:
1263
+ - {panel_type: "establishing", focus: "environment", composition: "landscape"}
1264
+ - {panel_type: "dialogue", focus: "character", composition: "landscape"}
1265
+ - {panel_type: "action", focus: "event", composition: "landscape"}
1266
+ - {panel_type: "action", focus: "event", composition: "landscape"}
1267
+ - {panel_type: "dialogue", focus: "character", composition: "landscape"}
1268
+ - {panel_type: "reaction", focus: "emotion", composition: "landscape"}
1269
+ - {panel_type: "closeup", focus: "character", composition: "landscape"}
1270
+
1271
+ - id: "action_montage"
1272
+ label: "Action Montage"
1273
+ description: "Fast-paced action sequence"
1274
+ positions:
1275
+ - [0.02, 0.02, 0.96, 0.3]
1276
+ - [0.02, 0.34, 0.31, 0.3]
1277
+ - [0.345, 0.34, 0.31, 0.3]
1278
+ - [0.67, 0.34, 0.31, 0.3]
1279
+ - [0.02, 0.66, 0.23, 0.32]
1280
+ - [0.27, 0.66, 0.23, 0.32]
1281
+ - [0.52, 0.66, 0.46, 0.32]
1282
+ metadata:
1283
+ - {panel_type: "establishing", focus: "environment", composition: "wide"}
1284
+ - {panel_type: "action", focus: "event", composition: "square"}
1285
+ - {panel_type: "action", focus: "event", composition: "square"}
1286
+ - {panel_type: "action", focus: "event", composition: "square"}
1287
+ - {panel_type: "closeup", focus: "emotion", composition: "portrait"}
1288
+ - {panel_type: "closeup", focus: "emotion", composition: "portrait"}
1289
+ - {panel_type: "reaction", focus: "characters", composition: "landscape"}
1290
+
1291
+ - id: "cinematic_sequence"
1292
+ label: "Cinematic Sequence"
1293
+ description: "Movie-like panel progression"
1294
+ positions:
1295
+ - [0.02, 0.02, 0.96, 0.28]
1296
+ - [0.02, 0.32, 0.47, 0.2]
1297
+ - [0.51, 0.32, 0.47, 0.2]
1298
+ - [0.02, 0.54, 0.31, 0.21]
1299
+ - [0.345, 0.54, 0.31, 0.21]
1300
+ - [0.67, 0.54, 0.31, 0.21]
1301
+ - [0.02, 0.77, 0.96, 0.21]
1302
+ metadata:
1303
+ - {panel_type: "establishing", focus: "environment", composition: "wide"}
1304
+ - {panel_type: "dialogue", focus: "character", composition: "landscape"}
1305
+ - {panel_type: "dialogue", focus: "character", composition: "landscape"}
1306
+ - {panel_type: "action", focus: "event", composition: "square"}
1307
+ - {panel_type: "action", focus: "event", composition: "square"}
1308
+ - {panel_type: "closeup", focus: "emotion", composition: "square"}
1309
+ - {panel_type: "reaction", focus: "characters", composition: "wide"}
1310
+
1311
+ 8_images:
1312
+ - id: "mega_grid"
1313
+ label: "Mega Grid"
1314
+ description: "Classic 4x2 grid"
1315
+ positions:
1316
+ - [0.02, 0.02, 0.23, 0.47]
1317
+ - [0.27, 0.02, 0.23, 0.47]
1318
+ - [0.52, 0.02, 0.23, 0.47]
1319
+ - [0.77, 0.02, 0.21, 0.47]
1320
+ - [0.02, 0.51, 0.23, 0.47]
1321
+ - [0.27, 0.51, 0.23, 0.47]
1322
+ - [0.52, 0.51, 0.23, 0.47]
1323
+ - [0.77, 0.51, 0.21, 0.47]
1324
+ metadata:
1325
+ - {panel_type: "establishing", focus: "environment", composition: "portrait"}
1326
+ - {panel_type: "dialogue", focus: "character", composition: "portrait"}
1327
+ - {panel_type: "dialogue", focus: "character", composition: "portrait"}
1328
+ - {panel_type: "action", focus: "event", composition: "portrait"}
1329
+ - {panel_type: "action", focus: "event", composition: "portrait"}
1330
+ - {panel_type: "action", focus: "character", composition: "portrait"}
1331
+ - {panel_type: "closeup", focus: "emotion", composition: "portrait"}
1332
+ - {panel_type: "reaction", focus: "emotion", composition: "portrait"}
1333
+
1334
+ - id: "chapter_opener"
1335
+ label: "Chapter Opener"
1336
+ description: "Splash page with progressive reveal"
1337
+ positions:
1338
+ - [0.02, 0.02, 0.96, 0.45]
1339
+ - [0.02, 0.49, 0.23, 0.24]
1340
+ - [0.27, 0.49, 0.23, 0.24]
1341
+ - [0.52, 0.49, 0.23, 0.24]
1342
+ - [0.77, 0.49, 0.21, 0.24]
1343
+ - [0.02, 0.75, 0.23, 0.23]
1344
+ - [0.27, 0.75, 0.23, 0.23]
1345
+ - [0.52, 0.75, 0.46, 0.23]
1346
+ metadata:
1347
+ - {panel_type: "splash", focus: "environment", composition: "wide"}
1348
+ - {panel_type: "detail", focus: "object", composition: "square"}
1349
+ - {panel_type: "detail", focus: "character", composition: "square"}
1350
+ - {panel_type: "dialogue", focus: "character", composition: "square"}
1351
+ - {panel_type: "dialogue", focus: "character", composition: "square"}
1352
+ - {panel_type: "action", focus: "event", composition: "square"}
1353
+ - {panel_type: "closeup", focus: "emotion", composition: "square"}
1354
+ - {panel_type: "reaction", focus: "characters", composition: "landscape"}
1355
+
1356
+ - id: "parallel_stories"
1357
+ label: "Parallel Stories"
1358
+ description: "Two simultaneous narratives"
1359
+ positions:
1360
+ - [0.02, 0.02, 0.47, 0.23]
1361
+ - [0.51, 0.02, 0.47, 0.23]
1362
+ - [0.02, 0.27, 0.47, 0.23]
1363
+ - [0.51, 0.27, 0.47, 0.23]
1364
+ - [0.02, 0.52, 0.47, 0.23]
1365
+ - [0.51, 0.52, 0.47, 0.23]
1366
+ - [0.02, 0.77, 0.47, 0.21]
1367
+ - [0.51, 0.77, 0.47, 0.21]
1368
+ metadata:
1369
+ - {panel_type: "establishing", focus: "environment", composition: "landscape"}
1370
+ - {panel_type: "establishing", focus: "environment", composition: "landscape"}
1371
+ - {panel_type: "dialogue", focus: "character", composition: "landscape"}
1372
+ - {panel_type: "dialogue", focus: "character", composition: "landscape"}
1373
+ - {panel_type: "action", focus: "event", composition: "landscape"}
1374
+ - {panel_type: "action", focus: "event", composition: "landscape"}
1375
+ - {panel_type: "reaction", focus: "emotion", composition: "landscape"}
1376
+ - {panel_type: "reaction", focus: "emotion", composition: "landscape"}
1377
+
1378
+ - id: "hero_explosion"
1379
+ label: "Hero Explosion"
1380
+ description: "Central impact radiating outward"
1381
+ positions:
1382
+ - [0.02, 0.02, 0.3, 0.3]
1383
+ - [0.68, 0.02, 0.3, 0.3]
1384
+ - [0.02, 0.68, 0.3, 0.3]
1385
+ - [0.68, 0.68, 0.3, 0.3]
1386
+ - [0.34, 0.02, 0.32, 0.28]
1387
+ - [0.02, 0.34, 0.28, 0.32]
1388
+ - [0.7, 0.34, 0.28, 0.32]
1389
+ - [0.34, 0.7, 0.32, 0.28]
1390
+ metadata:
1391
+ - {panel_type: "detail", focus: "object", composition: "square"}
1392
+ - {panel_type: "detail", focus: "object", composition: "square"}
1393
+ - {panel_type: "detail", focus: "character", composition: "square"}
1394
+ - {panel_type: "detail", focus: "character", composition: "square"}
1395
+ - {panel_type: "action", focus: "event", composition: "landscape"}
1396
+ - {panel_type: "action", focus: "event", composition: "portrait"}
1397
+ - {panel_type: "action", focus: "event", composition: "portrait"}
1398
+ - {panel_type: "closeup", focus: "emotion", composition: "landscape"}
1399
+
1400
+ - id: "magazine_style"
1401
+ label: "Magazine Style"
1402
+ description: "Editorial layout with varied sizes"
1403
+ positions:
1404
+ - [0.02, 0.02, 0.63, 0.35]
1405
+ - [0.67, 0.02, 0.31, 0.35]
1406
+ - [0.02, 0.39, 0.31, 0.28]
1407
+ - [0.35, 0.39, 0.31, 0.28]
1408
+ - [0.68, 0.39, 0.3, 0.28]
1409
+ - [0.02, 0.69, 0.31, 0.29]
1410
+ - [0.35, 0.69, 0.31, 0.29]
1411
+ - [0.68, 0.69, 0.3, 0.29]
1412
+ metadata:
1413
+ - {panel_type: "establishing", focus: "environment", composition: "landscape"}
1414
+ - {panel_type: "dialogue", focus: "character", composition: "portrait"}
1415
+ - {panel_type: "detail", focus: "object", composition: "square"}
1416
+ - {panel_type: "action", focus: "event", composition: "square"}
1417
+ - {panel_type: "dialogue", focus: "character", composition: "square"}
1418
+ - {panel_type: "action", focus: "character", composition: "square"}
1419
+ - {panel_type: "closeup", focus: "emotion", composition: "square"}
1420
+ - {panel_type: "reaction", focus: "emotion", composition: "square"}
1421
+
1422
+ - id: "epic_finale"
1423
+ label: "Epic Finale"
1424
+ description: "Climactic page layout"
1425
+ positions:
1426
+ - [0.02, 0.02, 0.31, 0.25]
1427
+ - [0.345, 0.02, 0.31, 0.25]
1428
+ - [0.67, 0.02, 0.31, 0.25]
1429
+ - [0.02, 0.29, 0.47, 0.4]
1430
+ - [0.51, 0.29, 0.47, 0.4]
1431
+ - [0.02, 0.71, 0.31, 0.27]
1432
+ - [0.345, 0.71, 0.31, 0.27]
1433
+ - [0.67, 0.71, 0.31, 0.27]
1434
+ metadata:
1435
+ - {panel_type: "detail", focus: "object", composition: "landscape"}
1436
+ - {panel_type: "dialogue", focus: "character", composition: "landscape"}
1437
+ - {panel_type: "detail", focus: "object", composition: "landscape"}
1438
+ - {panel_type: "action", focus: "event", composition: "portrait"}
1439
+ - {panel_type: "action", focus: "event", composition: "portrait"}
1440
+ - {panel_type: "closeup", focus: "emotion", composition: "landscape"}
1441
+ - {panel_type: "reaction", focus: "emotion", composition: "landscape"}
1442
+ - {panel_type: "reaction", focus: "characters", composition: "landscape"}