similarbuild 0.3.0 → 0.3.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "similarbuild",
3
- "version": "0.3.0",
3
+ "version": "0.3.2",
4
4
  "description": "Visual migration framework for Claude Code — clone a live page, get a paste-ready WordPress/Elementor or Shopify section file, validated and auto-corrected.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -37,7 +37,36 @@ A single `.html` file written to `outputPath`. The file is a fragment — no `<h
37
37
 
38
38
  ## On Activation
39
39
 
40
- 1. **Read the inputs.** Parse `inspection.json` (capture `sectionType`, `tokens`, `dom`, `pseudoElements`, `imgUrls`, **`hydratedHeader`**, **`hydratedFooter`**) and `assets-map.json` (the URL → localPath / inline-SVG dictionary). If `fixHints` is given, also read `previousHtmlPath`.
40
+ 1. **Read the inputs.** Parse `inspection.json` (capture `sectionType`, `tokens`, `dom`, **`domLive`**, `pseudoElements`, `imgUrls`, **`hydratedHeader`**, **`hydratedFooter`**, and `screenshot` path) and `assets-map.json` (the URL → localPath / inline-SVG dictionary). If `fixHints` is given, also read `previousHtmlPath`.
41
+
42
+ **§V03-2 — Vision-based composition (MANDATORY when composing PDPs, collections, and any page with lazy-hydrated body content).** The DOM walker runs once and can miss content that depends on JavaScript hydration triggered by viewport intersection, user interaction, or framework-internal state (Shopify Dawn/OS 2.0 PDPs are the canonical case — `<x-product-form>`, `<price-list>`, `<variant-radios>`, Loox reviews widget, "you may also like", FAQ Q&A all hide their content from the static walker). The screenshot at `inspection.screenshot` captured the page AFTER `inspect-live` did step-scroll 0→12000 + lazy-hydration waits — it is the ground truth of what the user actually sees.
43
+
44
+ **Workflow when composing a body page** (PDP, collection, home, anything that's NOT `--target-section=header|footer`):
45
+
46
+ 1. **Open the screenshot** via Read tool: `Read({ file_path: <absolute path from inspection.screenshot> })`. The image is included in your context as visual content — you can SEE the page.
47
+ 2. **Identify visible components** in the section bbox you're composing: gallery images (count and approximate aspect), price (current + compare-at if struck through), sale banners, variant selectors (size dropdowns, color swatches), tier pricing offers, CTAs (color, label text), reviews snippets, trust badges, FAQ accordions, etc.
48
+ 3. **For each visible component, emit REAL markup** reflecting what you saw. Examples:
49
+ - 6-thumb gallery visible → emit `<ul class="pdp__thumbs"><li><img src="…" alt="…"></li>×6</ul>` with the thumb URLs taken from `inspection.imgUrls` filtered by Shopify-CDN patterns + product-slug match. If a thumb URL isn't in imgUrls, emit a `<!-- TODO: thumb N — visible in screenshot at (x,y), no src in inspection -->` comment and a placeholder `<img>` so the user knows.
50
+ - Price "$29.00" with strikethrough "$34.00" → `<span class="pdp__price">$29.00</span> <s class="pdp__compare">$34.00</s>` — read the digits LITERALLY from the screenshot, do not pull from a different DOM source unless they match.
51
+ - "Mother's Day Sale — Buy 2 Pairs, Get 2 FREE" banner → emit the banner with the EXACT text you read.
52
+ - 3 tier offers (1 Pair $29, 2 Pairs + 2 FREE $58, 3 Pairs + 5 FREE $88) → emit 3 radio cards with the literal pricing and "Best Seller"/"Best Value" badges as you see them.
53
+ - Variant selectors (Size XL dropdown, Color "Classic Black" dropdown) → emit `<select>` elements with option lists matching what's visible (you may not see all options if dropdown is closed — emit the ones visible + a `<!-- TODO: additional options likely below the fold -->` comment).
54
+ - CTA "ADD TO CART" red button → emit `<button class="pdp__cta">ADD TO CART</button>` with red background color sampled from your visual reading (or use a known brand red from `inspection.tokens.colors`).
55
+
56
+ 4. **Hybridize DOM + vision**:
57
+ - **Texts and visible structure** → read from screenshot (most reliable for lazy-hydrated content).
58
+ - **`href` for clickable links** → look in `inspection.domLive` / `inspection.hydratedHeader` / `inspection.hydratedFooter` / `inspection.imgUrls` for matching elements (by visible link text or image alt). If no match, emit `href="#"` plus a `<!-- TODO: resolve href for "…" -->` comment.
59
+ - **`form action`, `method`, hidden inputs** → ALWAYS from `inspection.hydratedFooter.forms` / `inspection.dom` / `inspection.domLive` (never guess these — submission will break).
60
+ - **`src` for images** → resolve via `assetsMap.assets[url].localPath` when URL appears in `inspection.imgUrls`. When you see an image in the screenshot but can't find its URL in imgUrls (custom-element-rendered img), emit a placeholder + TODO comment.
61
+ - **Computed style (fonts, colors, spacing)** → from `inspection.tokens` + `domLive[].computedStyle` (canonical for layout-derivable values like body background).
62
+
63
+ 5. **No fabrication, but vision-reading is NOT fabrication.** Emitting `<span>$29.00</span>` when you can clearly SEE "$29.00" in the screenshot is reading, not inventing. The fabrication rule means: don't make up content that isn't in either the DOM or the screenshot. It does NOT mean ignore the screenshot.
64
+
65
+ 6. **Coverage gate.** When composing PDPs, aim for: gallery + price + variants + CTA + (optional) reviews summary + trust badges. If any of these are visible in the screenshot but you cannot emit faithful markup (e.g. complex tier pricing with sliders), emit a STRUCTURAL placeholder + clear TODO comment — never leave a section bare.
66
+
67
+ Vision-based composition is the v0.3.2 answer to the v0.3.0 catch-22 (Cap A flatten sees shadow content but loses bbox; domLive has bbox but misses shadow content). The screenshot is the ground truth that bridges both.
68
+
69
+ **§V03-1 — Use `domLive` as the canonical body tree when present.** When `inspection.domLive` is non-null, it holds the live-walker snapshot taken BEFORE Cap A substituted `dom[]` with the shadow-flattened tree. The flattened `dom[]` carries `bbox={0,0,0,0}` and empty `computedStyle` because parseHTMLUnsafe returns a detached doc — it's structurally rich (gallery imgs, custom-element children) but useless for layout. The composer needs real bboxes, real `computedStyle.background`, real heights. Always prefer `inspection.domLive` for body section composition (bbox, computedStyle, hero detection, section ordering). Use `inspection.dom` only when `domLive === null` (page had no shadow roots — flatten didn't fire) or when you specifically need shadow-flattened content like a PDP gallery (consult `dom` for image-rich PDP nodes, but use `domLive` for the surrounding layout).
41
70
 
42
71
  **§V03-0a — `hydratedHeader` / `hydratedFooter` payload.** When `inspect-live` captures the page chrome WHILE THE BOTTOM IS IN VIEW, it snapshots the hydrated HTML before Shopify themes tear menus down on intersection-out. The payload is the canonical source of truth for header/footer composition — prefer it over `dom[]` subtrees, which may be empty `<ul>` shells if the live walker ran after tear-down. Each one has:
43
72
  - `html`: outerHTML of the chrome subtree (string).
@@ -276,11 +276,26 @@ async function verifyInspectionComplete(inspectionPath) {
276
276
  }
277
277
  }
278
278
 
279
+ // §V03-1 — Prefer domLive when present. v0.3.0 introduced
280
+ // inspection.domLive as the live-walker snapshot taken BEFORE Cap A's
281
+ // shadow-flatten substitution. The flattened dom[] (parsedDoc detached)
282
+ // has bbox={0,0,0,0} and empty computedStyle on every node, which
283
+ // would falsely trigger 'sections-no-computed-style' on any page with
284
+ // open shadow roots (every Shopify Dawn/OS 2.0 page has ≥1). The
285
+ // composer needs real layout, so we read sections from domLive when
286
+ // available and fall back to dom[] otherwise.
287
+ const domForPreflight = Array.isArray(inspection.domLive)
288
+ ? inspection.domLive
289
+ : inspection.domLive && typeof inspection.domLive === 'object'
290
+ ? [inspection.domLive]
291
+ : inspection.dom
292
+
279
293
  // (a) dom non-empty
280
- if (!Array.isArray(inspection.dom) || inspection.dom.length === 0) {
294
+ if (!Array.isArray(domForPreflight) || domForPreflight.length === 0) {
281
295
  missing.push('dom-empty')
282
296
  }
283
- info.domRoots = Array.isArray(inspection.dom) ? inspection.dom.length : 0
297
+ info.domRoots = Array.isArray(domForPreflight) ? domForPreflight.length : 0
298
+ info.domSource = inspection.domLive ? 'domLive' : 'dom'
284
299
 
285
300
  // (b) screenshot path exists on disk
286
301
  let screenshotPath = inspection.screenshot
@@ -300,11 +315,11 @@ async function verifyInspectionComplete(inspectionPath) {
300
315
  }
301
316
  }
302
317
 
303
- // (c) ≥1 classified section
318
+ // (c) ≥1 classified section (counted from domForPreflight per above)
304
319
  let sectionsCount = 0
305
320
  let sectionsWithBg = 0
306
321
  let sectionsWithBbox = 0
307
- walkAllNodes(inspection.dom, (n) => {
322
+ walkAllNodes(domForPreflight, (n) => {
308
323
  if (n.sectionType) {
309
324
  sectionsCount++
310
325
  if (n.bbox && typeof n.bbox.h === 'number' && n.bbox.h > 0) sectionsWithBbox++