diff --git a/crates/cli/src/component_capture.rs b/crates/cli/src/component_capture.rs index baac66267..e76d01e30 100644 --- a/crates/cli/src/component_capture.rs +++ b/crates/cli/src/component_capture.rs @@ -132,6 +132,7 @@ fn render_page( return {html:document.documentElement.outerHTML,svg:document.querySelectorAll('svg').length,images:document.images.length,controls:document.querySelectorAll('button,input,select,textarea,a[href]').length}; })()"#.replace("ASSEMBLED", if assembled { "true" } else { "false" }); let dom=page.evaluate_value_in_world(&world,&inspect).map_err(|e|e.message)?; + let fonts = page.evaluate_value_in_world(&world, include_str!("component_fonts.js")).map_err(|e| e.message)?; let isolated = if let Some(targets) = isolation { page.set_transparent_background().map_err(|e| e.message)?; let script = format!("({})({})", include_str!("component_isolation.js"), targets); @@ -179,6 +180,7 @@ fn render_page( proof["isolation"] = isolated; proof["capturedDomSha256"] = json!(hash(captured_dom.as_str().unwrap().as_bytes())); } + proof["fonts"] = fonts; if assembled { proof["kind"] = json!("assembled-page"); proof["scriptPolicy"] = json!("pinned-local-and-inline; network-api-and-workers-disabled"); @@ -321,6 +323,23 @@ mod tests { use super::*; #[test] #[ignore = "requires Chromium"] + fn missing_primary_font_is_rejected_and_pinned_font_is_captured() { + let png = impeccable_comp::png_io::encode_png(&impeccable_comp::raster::create_image(300,100,[255;4]),&[]).unwrap(); + let html = "
Reviewing ${l(Q.description||m.name)}
Separate review items ${gi}
${Q.excluded.length?`Hidden in this preview: ${Q.excluded.map((P)=>l(P.name)).join(" · ")}
`:""}${l(o.title)} · Round ${o.round}
Reviewing ${l(j.description||c.name)}
Outlined · reviewed separately ${q}
${j.excluded.length?`Hidden in this preview: ${j.excluded.map((_)=>l(_.name)).join(" · ")}
`:""}${l(o.title)} · Round ${o.round}
Historical hotel artwork for testing this interface. Decisions stay in this preview; no run is changed.
':""} - ${V&&!a?`${qi} ${qi===1?"item":"items"} to review${Do}
${j.pending?``:""}${V.removed.length?`${V.removed.map((i)=>l(i.name)).join(" · ")}. Confirm these omissions are intentional before accepting the map.
${Hi} ${Hi===1?"item":"items"} to review${So}
${H.pending?``:""}${$.removed.length?`${$.removed.map((i)=>l(i.name)).join(" · ")}. Confirm these omissions are intentional before accepting the map.
${j.approved} approved · ${j.revisions} flagged for repair${n.missing.length?` · ${n.missing.length} missing`:""}
${y?"Your decisions are saved.":j.hasFeedback?"Send your feedback to start the next repair round.":"Confirm nothing is missing, then approve and continue."}
${l(U.prior.feedback||"No written feedback was supplied.")}${U.prior.split?"
Requested: split into separately reviewable components.
":""}Unchanged · approval kept
':""} - ${U?.change?.kind==="changed"?`${H.approved} approved · ${H.revisions} flagged for repair${n.missing.length?` · ${n.missing.length} missing`:""}
${z?"Your decisions are saved.":H.hasFeedback?"Send your feedback to start the next repair round.":"Confirm nothing is missing, then approve and continue."}
${l(B.prior.feedback||"No written feedback was supplied.")}${B.prior.split?"
Requested: split into separately reviewable components.
":""}Unchanged · approval kept
':""} + ${B?.change?.kind==="changed"?`Legacy region capture · may include overlapping components.
':""}${d?.context?.layering&&(Ri||O)?`${l(d.context.layering)}
`:""} -${!uo?.related.length?l(d.note):""}
-Viewing the previous round. Return to Current to make a decision.
':""}${y?`Return to Current to review this round.
":""}${l(xi.feedback||"No note — agent will diagnose.")}
`:""} - ${a?`${l(Vi||(y?"Your decision is saved.":s?"Sending…":Qi?"":"Approval confirms the composition and that nothing is missing."))}
`:""}This piece will be added to the unresolved inventory.
No components supplied.
Legacy region capture · may include overlapping components.
':""}${d?.context?.layering&&(Di||S)?`${l(d.context.layering)}
`:""} +${!zo?.related.length?l(d.note):""}
+Viewing the previous round. Return to Current to make a decision.
':""}${z?`Return to Current to review this round.
":""}${l(ti.feedback||"No note — agent will diagnose.")}
`:""} + ${p?`${l(Ji||(z?"Your decision is saved.":s?"Sending…":Yi?"":"Approval confirms the composition and that nothing is missing."))}
`:""}This piece will be added to the unresolved inventory.
No components supplied.
${z==="pending"?"Nothing left to review. Your decisions are ready.":"No components reviewed yet."}
`:""}${v==="pending"?"Nothing left to review. Your decisions are ready.":"No components reviewed yet."}
`:""}Reviewing ${esc(scope.description || item.name)}
Separate review items ${names}
${scope.excluded.length?`Hidden in this preview: ${scope.excluded.map(other=>esc(other.name)).join(' · ')}
`:''}Reviewing ${esc(scope.description || item.name)}
Outlined · reviewed separately ${names}
${scope.excluded.length?`Hidden in this preview: ${scope.excluded.map(other=>esc(other.name)).join(' · ')}
`:''}