openpencil/apps
Fini 98211e6226 fix(ai): heuristic image-area detection for non-canonical frame placeholders
Why: 2026-05-09 user report — the food-app card design landed with
empty colored rectangles where restaurant photos should be (Bella
Italia / Green Bowl / Margherita Pizza). Root cause: the model emitted
plain frames named "Image" / "Photo" / "Cover" with a solid fill as
the card-top image area, instead of using add_image_placeholder_v1
(which sets role: 'image-placeholder'). The auto-search pipeline
strict-checks role and missed all of them, so scanAndFillImages found
nothing to search and the cards stayed solid-colored.

What: new isImageAreaFrameByHeuristic(node) supplements the strict
role check. Conservative match — only fires when ALL of:
  - frame node WITHOUT role='image-placeholder' (strict path handles
    that)
  - name matches /\\b(image|photo|cover|hero|thumbnail|thumb|picture|
    banner|poster)\\b/i
  - width >= 80 AND height >= 60 (filters tiny color swatches)
  - exactly one solid (non-image) fill (gradient = decorative, image
    = already filled, both skip)
  - 0 or 1 children (an icon child is OK, multi-child = real layout)

collectImageSearchTargets walks both the strict and heuristic paths
and produces the same kind: 'placeholder-frame' target either way, so
the existing query / aspect / search code-path runs unchanged.

10 new tests cover positive matches (Image / Photo / Cover / Hero /
Thumbnail / Banner / Poster), negative for canonical placeholder
(double-counting prevented), unrelated names (Card / Wrapper),
already-filled image fills, gradients, content-rich frames, single-
icon-child acceptance, undersize frames, and non-numeric dimensions.
26 / 26 tests in image-search-pipeline.test.ts pass (was 16; +10).
1098 / 1098 AI service tests pass (unchanged).
2026-05-09 20:59:49 +08:00
..
cli Merge origin/v0.8.0 into feat/rust-ification 2026-05-03 21:00:00 +08:00
desktop Merge origin/v0.8.0 into feat/rust-ification 2026-05-03 21:00:00 +08:00
web fix(ai): heuristic image-area detection for non-canonical frame placeholders 2026-05-09 20:59:49 +08:00